qemu/qapi/string-input-visitor.c
<<
>>
Prefs
   1/*
   2 * String parsing visitor
   3 *
   4 * Copyright Red Hat, Inc. 2012-2016
   5 *
   6 * Author: Paolo Bonzini <pbonzini@redhat.com>
   7 *         David Hildenbrand <david@redhat.com>
   8 *
   9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  10 * See the COPYING.LIB file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qapi/error.h"
  15#include "qapi/string-input-visitor.h"
  16#include "qapi/visitor-impl.h"
  17#include "qapi/qmp/qerror.h"
  18#include "qapi/qmp/qnull.h"
  19#include "qemu/option.h"
  20#include "qemu/cutils.h"
  21
  22typedef enum ListMode {
  23    /* no list parsing active / no list expected */
  24    LM_NONE,
  25    /* we have an unparsed string remaining */
  26    LM_UNPARSED,
  27    /* we have an unfinished int64 range */
  28    LM_INT64_RANGE,
  29    /* we have an unfinished uint64 range */
  30    LM_UINT64_RANGE,
  31    /* we have parsed the string completely and no range is remaining */
  32    LM_END,
  33} ListMode;
  34
  35/* protect against DOS attacks, limit the amount of elements per range */
  36#define RANGE_MAX_ELEMENTS 65536
  37
  38typedef union RangeElement {
  39    int64_t i64;
  40    uint64_t u64;
  41} RangeElement;
  42
  43struct StringInputVisitor
  44{
  45    Visitor visitor;
  46
  47    /* List parsing state */
  48    ListMode lm;
  49    RangeElement rangeNext;
  50    RangeElement rangeEnd;
  51    const char *unparsed_string;
  52    void *list;
  53
  54    /* The original string to parse */
  55    const char *string;
  56};
  57
  58static StringInputVisitor *to_siv(Visitor *v)
  59{
  60    return container_of(v, StringInputVisitor, visitor);
  61}
  62
  63static void start_list(Visitor *v, const char *name, GenericList **list,
  64                       size_t size, Error **errp)
  65{
  66    StringInputVisitor *siv = to_siv(v);
  67
  68    assert(siv->lm == LM_NONE);
  69    siv->list = list;
  70    siv->unparsed_string = siv->string;
  71
  72    if (!siv->string[0]) {
  73        if (list) {
  74            *list = NULL;
  75        }
  76        siv->lm = LM_END;
  77    } else {
  78        if (list) {
  79            *list = g_malloc0(size);
  80        }
  81        siv->lm = LM_UNPARSED;
  82    }
  83}
  84
  85static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
  86{
  87    StringInputVisitor *siv = to_siv(v);
  88
  89    switch (siv->lm) {
  90    case LM_END:
  91        return NULL;
  92    case LM_INT64_RANGE:
  93    case LM_UINT64_RANGE:
  94    case LM_UNPARSED:
  95        /* we have an unparsed string or something left in a range */
  96        break;
  97    default:
  98        abort();
  99    }
 100
 101    tail->next = g_malloc0(size);
 102    return tail->next;
 103}
 104
 105static void check_list(Visitor *v, Error **errp)
 106{
 107    const StringInputVisitor *siv = to_siv(v);
 108
 109    switch (siv->lm) {
 110    case LM_INT64_RANGE:
 111    case LM_UINT64_RANGE:
 112    case LM_UNPARSED:
 113        error_setg(errp, "Fewer list elements expected");
 114        return;
 115    case LM_END:
 116        return;
 117    default:
 118        abort();
 119    }
 120}
 121
 122static void end_list(Visitor *v, void **obj)
 123{
 124    StringInputVisitor *siv = to_siv(v);
 125
 126    assert(siv->lm != LM_NONE);
 127    assert(siv->list == obj);
 128    siv->list = NULL;
 129    siv->unparsed_string = NULL;
 130    siv->lm = LM_NONE;
 131}
 132
 133static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
 134{
 135    const char *endptr;
 136    int64_t start, end;
 137
 138    /* parse a simple int64 or range */
 139    if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
 140        return -EINVAL;
 141    }
 142    end = start;
 143
 144    switch (endptr[0]) {
 145    case '\0':
 146        siv->unparsed_string = endptr;
 147        break;
 148    case ',':
 149        siv->unparsed_string = endptr + 1;
 150        break;
 151    case '-':
 152        /* parse the end of the range */
 153        if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
 154            return -EINVAL;
 155        }
 156        if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
 157            return -EINVAL;
 158        }
 159        switch (endptr[0]) {
 160        case '\0':
 161            siv->unparsed_string = endptr;
 162            break;
 163        case ',':
 164            siv->unparsed_string = endptr + 1;
 165            break;
 166        default:
 167            return -EINVAL;
 168        }
 169        break;
 170    default:
 171        return -EINVAL;
 172    }
 173
 174    /* we have a proper range (with maybe only one element) */
 175    siv->lm = LM_INT64_RANGE;
 176    siv->rangeNext.i64 = start;
 177    siv->rangeEnd.i64 = end;
 178    return 0;
 179}
 180
 181static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
 182                             Error **errp)
 183{
 184    StringInputVisitor *siv = to_siv(v);
 185    int64_t val;
 186
 187    switch (siv->lm) {
 188    case LM_NONE:
 189        /* just parse a simple int64, bail out if not completely consumed */
 190        if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
 191                error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
 192                           name ? name : "null", "int64");
 193            return;
 194        }
 195        *obj = val;
 196        return;
 197    case LM_UNPARSED:
 198        if (try_parse_int64_list_entry(siv, obj)) {
 199            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
 200                       "list of int64 values or ranges");
 201            return;
 202        }
 203        assert(siv->lm == LM_INT64_RANGE);
 204        /* fall through */
 205    case LM_INT64_RANGE:
 206        /* return the next element in the range */
 207        assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
 208        *obj = siv->rangeNext.i64++;
 209
 210        if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
 211            /* end of range, check if there is more to parse */
 212            siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
 213        }
 214        return;
 215    case LM_END:
 216        error_setg(errp, "Fewer list elements expected");
 217        return;
 218    default:
 219        abort();
 220    }
 221}
 222
 223static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
 224{
 225    const char *endptr;
 226    uint64_t start, end;
 227
 228    /* parse a simple uint64 or range */
 229    if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
 230        return -EINVAL;
 231    }
 232    end = start;
 233
 234    switch (endptr[0]) {
 235    case '\0':
 236        siv->unparsed_string = endptr;
 237        break;
 238    case ',':
 239        siv->unparsed_string = endptr + 1;
 240        break;
 241    case '-':
 242        /* parse the end of the range */
 243        if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
 244            return -EINVAL;
 245        }
 246        if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
 247            return -EINVAL;
 248        }
 249        switch (endptr[0]) {
 250        case '\0':
 251            siv->unparsed_string = endptr;
 252            break;
 253        case ',':
 254            siv->unparsed_string = endptr + 1;
 255            break;
 256        default:
 257            return -EINVAL;
 258        }
 259        break;
 260    default:
 261        return -EINVAL;
 262    }
 263
 264    /* we have a proper range (with maybe only one element) */
 265    siv->lm = LM_UINT64_RANGE;
 266    siv->rangeNext.u64 = start;
 267    siv->rangeEnd.u64 = end;
 268    return 0;
 269}
 270
 271static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 272                              Error **errp)
 273{
 274    StringInputVisitor *siv = to_siv(v);
 275    uint64_t val;
 276
 277    switch (siv->lm) {
 278    case LM_NONE:
 279        /* just parse a simple uint64, bail out if not completely consumed */
 280        if (qemu_strtou64(siv->string, NULL, 0, &val)) {
 281            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
 282                       "uint64");
 283            return;
 284        }
 285        *obj = val;
 286        return;
 287    case LM_UNPARSED:
 288        if (try_parse_uint64_list_entry(siv, obj)) {
 289            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
 290                       "list of uint64 values or ranges");
 291            return;
 292        }
 293        assert(siv->lm == LM_UINT64_RANGE);
 294        /* fall through */
 295    case LM_UINT64_RANGE:
 296        /* return the next element in the range */
 297        assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
 298        *obj = siv->rangeNext.u64++;
 299
 300        if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
 301            /* end of range, check if there is more to parse */
 302            siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
 303        }
 304        return;
 305    case LM_END:
 306        error_setg(errp, "Fewer list elements expected");
 307        return;
 308    default:
 309        abort();
 310    }
 311}
 312
 313static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
 314                            Error **errp)
 315{
 316    StringInputVisitor *siv = to_siv(v);
 317    Error *err = NULL;
 318    uint64_t val;
 319
 320    assert(siv->lm == LM_NONE);
 321    parse_option_size(name, siv->string, &val, &err);
 322    if (err) {
 323        error_propagate(errp, err);
 324        return;
 325    }
 326
 327    *obj = val;
 328}
 329
 330static void parse_type_bool(Visitor *v, const char *name, bool *obj,
 331                            Error **errp)
 332{
 333    StringInputVisitor *siv = to_siv(v);
 334
 335    assert(siv->lm == LM_NONE);
 336    if (!strcasecmp(siv->string, "on") ||
 337        !strcasecmp(siv->string, "yes") ||
 338        !strcasecmp(siv->string, "true")) {
 339        *obj = true;
 340        return;
 341    }
 342    if (!strcasecmp(siv->string, "off") ||
 343        !strcasecmp(siv->string, "no") ||
 344        !strcasecmp(siv->string, "false")) {
 345        *obj = false;
 346        return;
 347    }
 348
 349    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 350               "boolean");
 351}
 352
 353static void parse_type_str(Visitor *v, const char *name, char **obj,
 354                           Error **errp)
 355{
 356    StringInputVisitor *siv = to_siv(v);
 357
 358    assert(siv->lm == LM_NONE);
 359    *obj = g_strdup(siv->string);
 360}
 361
 362static void parse_type_number(Visitor *v, const char *name, double *obj,
 363                              Error **errp)
 364{
 365    StringInputVisitor *siv = to_siv(v);
 366    double val;
 367
 368    assert(siv->lm == LM_NONE);
 369    if (qemu_strtod_finite(siv->string, NULL, &val)) {
 370        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 371                   "number");
 372        return;
 373    }
 374
 375    *obj = val;
 376}
 377
 378static void parse_type_null(Visitor *v, const char *name, QNull **obj,
 379                            Error **errp)
 380{
 381    StringInputVisitor *siv = to_siv(v);
 382
 383    assert(siv->lm == LM_NONE);
 384    *obj = NULL;
 385
 386    if (siv->string[0]) {
 387        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 388                   "null");
 389        return;
 390    }
 391
 392    *obj = qnull();
 393}
 394
 395static void string_input_free(Visitor *v)
 396{
 397    StringInputVisitor *siv = to_siv(v);
 398
 399    g_free(siv);
 400}
 401
 402Visitor *string_input_visitor_new(const char *str)
 403{
 404    StringInputVisitor *v;
 405
 406    assert(str);
 407    v = g_malloc0(sizeof(*v));
 408
 409    v->visitor.type = VISITOR_INPUT;
 410    v->visitor.type_int64 = parse_type_int64;
 411    v->visitor.type_uint64 = parse_type_uint64;
 412    v->visitor.type_size = parse_type_size;
 413    v->visitor.type_bool = parse_type_bool;
 414    v->visitor.type_str = parse_type_str;
 415    v->visitor.type_number = parse_type_number;
 416    v->visitor.type_null = parse_type_null;
 417    v->visitor.start_list = start_list;
 418    v->visitor.next_list = next_list;
 419    v->visitor.check_list = check_list;
 420    v->visitor.end_list = end_list;
 421    v->visitor.free = string_input_free;
 422
 423    v->string = str;
 424    v->lm = LM_NONE;
 425    return &v->visitor;
 426}
 427