Example #1
0
qdr_address_t *qdr_add_local_address_CT(qdr_core_t *core, char aclass, const char *address, qd_address_treatment_t treatment)
{
    char           addr_string[1000];
    qdr_address_t *addr = 0;
    qd_iterator_t *iter = 0;

    snprintf(addr_string, sizeof(addr_string), "%c%s", aclass, address);
    iter = qd_iterator_string(addr_string, ITER_VIEW_ALL);

    qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
    if (!addr) {
        addr = qdr_address_CT(core, treatment);
        if (addr) {
            qd_hash_insert(core->addr_hash, iter, addr, &addr->hash_handle);
            DEQ_INSERT_TAIL(core->addrs, addr);
            addr->block_deletion = true;
            addr->local = (aclass == 'L');
        }
    }
    qd_iterator_free(iter);
    return addr;
}
Example #2
0
const char *qd_parse_annotations_v1(
    bool                   strip_anno_in,
    qd_iterator_t         *ma_iter_in,
    qd_parsed_field_t    **ma_ingress,
    qd_parsed_field_t    **ma_phase,
    qd_parsed_field_t    **ma_to_override,
    qd_parsed_field_t    **ma_trace,
    qd_iterator_pointer_t *blob_pointer,
    uint32_t              *blob_item_count)
{
    // Do full parse
    qd_iterator_reset(ma_iter_in);

    qd_parsed_turbo_list_t annos;
    uint32_t               user_entries;
    uint32_t               user_bytes;
    const char * parse_error = qd_parse_turbo(ma_iter_in, &annos, &user_entries, &user_bytes);
    if (parse_error) {
        return parse_error;
    }

    qd_parsed_turbo_t *anno;
    if (!strip_anno_in) {
        anno = DEQ_HEAD(annos);
        while (anno) {
            qd_iterator_t *key_iter =
                qd_iterator_buffer(anno->bufptr.buffer,
                                anno->bufptr.cursor - qd_buffer_base(anno->bufptr.buffer),
                                anno->size,
                                ITER_VIEW_ALL);
            assert(key_iter);

            qd_parsed_field_t *key_field = qd_parse(key_iter);
            assert(key_field);

            qd_iterator_t *iter = qd_parse_raw(key_field);
            assert(iter);

            qd_parsed_turbo_t *anno_val = DEQ_NEXT(anno);
            assert(anno_val);

            qd_iterator_t *val_iter =
                qd_iterator_buffer(anno_val->bufptr.buffer,
                                anno_val->bufptr.cursor - qd_buffer_base(anno_val->bufptr.buffer),
                                anno_val->size,
                                ITER_VIEW_ALL);
            assert(val_iter);

            qd_parsed_field_t *val_field = qd_parse(val_iter);
            assert(val_field);

            // Hoist the key name out of the buffers into a normal char array
            char key_name[QD_MA_MAX_KEY_LEN + 1];
            (void)qd_iterator_strncpy(iter, key_name, QD_MA_MAX_KEY_LEN + 1);

            // transfer ownership of the extracted value to the message
            if        (!strcmp(key_name, QD_MA_TRACE)) {
                *ma_trace = val_field;
            } else if (!strcmp(key_name, QD_MA_INGRESS)) {
                *ma_ingress = val_field;
            } else if (!strcmp(key_name, QD_MA_TO)) {
                *ma_to_override = val_field;
            } else if (!strcmp(key_name, QD_MA_PHASE)) {
                *ma_phase = val_field;
            } else {
                // TODO: this key had the QD_MA_PREFIX but it does not match
                //       one of the actual fields. 
                qd_parse_free(val_field);
            }

            qd_iterator_free(key_iter);
            qd_parse_free(key_field);
            qd_iterator_free(val_iter);
            // val_field is usually handed over to message_private and is freed 

            anno = DEQ_NEXT(anno_val);
        }
    }

    anno = DEQ_HEAD(annos);
    while (anno) {
        DEQ_REMOVE_HEAD(annos);
        free_qd_parsed_turbo_t(anno);
        anno = DEQ_HEAD(annos);
    }

    // Adjust size of user annotation blob by the size of the router
    // annotations
    blob_pointer->remaining = user_bytes;
    assert(blob_pointer->remaining >= 0);

    *blob_item_count = user_entries;
    assert(*blob_item_count >= 0);
    return 0;
}
Example #3
0
static char *test_parser_fixed_scalars(void *context)
{
    int idx = 0;
    qd_iterator_t *field = NULL;
    qd_parsed_field_t *parsed = NULL;
    static char error[1024];

    error[0] = 0;

    while (fs_vectors[idx].data) {
        field = qd_iterator_binary(fs_vectors[idx].data,
                                   fs_vectors[idx].length, ITER_VIEW_ALL);
        parsed = qd_parse(field);

        qd_iterator_t *typed_iter = qd_parse_typed(parsed);

        int length = qd_iterator_length(typed_iter);

        if (length != fs_vectors[idx].length) {
            strcpy(error, "Length of typed iterator does not match actual length");
            break;
        }

        if (!qd_parse_ok(parsed)) {
            strcpy(error, "Unexpected Parse Error");
            break;
        }
        if (qd_parse_tag(parsed) != fs_vectors[idx].expected_tag) {
            sprintf(error, "(%d) Tag: Expected %02x, Got %02x", idx,
                    fs_vectors[idx].expected_tag, qd_parse_tag(parsed));
            break;
        }
        if (fs_vectors[idx].check_uint &&
            qd_parse_as_uint(parsed) != fs_vectors[idx].expected_ulong) {
            sprintf(error, "(%d) UINT: Expected %"PRIx64", Got %"PRIx32, idx,
                    fs_vectors[idx].expected_ulong, qd_parse_as_uint(parsed));
            break;
        }
        if (fs_vectors[idx].check_ulong &&
            qd_parse_as_ulong(parsed) != fs_vectors[idx].expected_ulong) {
            sprintf(error, "(%d) ULONG: Expected %"PRIx64", Got %"PRIx64, idx,
                    fs_vectors[idx].expected_ulong, qd_parse_as_ulong(parsed));
            break;
        }
        if (fs_vectors[idx].check_int &&
            qd_parse_as_int(parsed) != fs_vectors[idx].expected_long) {
            sprintf(error, "(%d) INT: Expected %"PRIx64", Got %"PRIx32, idx,
                    fs_vectors[idx].expected_long, qd_parse_as_int(parsed));
            break;
        }
        if (fs_vectors[idx].check_long &&
            qd_parse_as_long(parsed) != fs_vectors[idx].expected_long) {
            sprintf(error, "(%d) LONG: Expected %"PRIx64", Got %"PRIx64, idx,
                    fs_vectors[idx].expected_long, qd_parse_as_long(parsed));
            break;
        }
        idx++;
        qd_iterator_free(field);
        field = 0;
        qd_parse_free(parsed);
        parsed = 0;
    }

    qd_iterator_free(field);
    qd_parse_free(parsed);
    return *error ? error : 0;
}
Example #4
0
static char *test_tracemask(void *context)
{
    qd_bitmask_t    *bm = NULL;
    qd_tracemask_t  *tm = qd_tracemask();
    qd_buffer_list_t list;
    static char      error[1024];

    error[0] = 0;
    qd_iterator_set_address(false, "0", "ROUTER");

    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.A", 0);
    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.B", 1);
    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.C", 2);
    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.D", 3);
    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.E", 4);
    qd_tracemask_add_router(tm, "amqp:/_topo/0/Router.F", 5);

    qd_tracemask_set_link(tm, 0, 4);
    qd_tracemask_set_link(tm, 3, 10);
    qd_tracemask_set_link(tm, 4, 3);
    qd_tracemask_set_link(tm, 5, 2);

    qd_composed_field_t *comp = qd_compose_subfield(0);
    qd_compose_start_list(comp);
    qd_compose_insert_string(comp, "0/Router.A");
    qd_compose_insert_string(comp, "0/Router.D");
    qd_compose_insert_string(comp, "0/Router.E");
    qd_compose_end_list(comp);

    DEQ_INIT(list);
    qd_compose_take_buffers(comp, &list);
    qd_compose_free(comp);

    int length = 0;
    qd_buffer_t *buf = DEQ_HEAD(list);
    while (buf) {
        length += qd_buffer_size(buf);
        buf = DEQ_NEXT(buf);
    }

    qd_iterator_t     *iter = qd_iterator_buffer(DEQ_HEAD(list), 0, length, ITER_VIEW_ALL);
    qd_parsed_field_t *pf   = qd_parse(iter);
    qd_iterator_free(iter);

    int ingress = -1;

    bm = qd_tracemask_create(tm, pf, &ingress);
    if (qd_bitmask_cardinality(bm) != 3) {
        sprintf(error, "Expected cardinality of 3, got %d", qd_bitmask_cardinality(bm));
        goto cleanup;
    }
    if (ingress != 0) {
        sprintf(error, "(A) Expected ingress index of 0, got %d", ingress);
        goto cleanup;
    }
    int total = 0;
    int bit, c;
    for (QD_BITMASK_EACH(bm, bit, c)) {
        total += bit;
    }
    if (total != 17) {
        sprintf(error, "Expected total bit value of 17, got %d", total);
        goto cleanup;
    }

    qd_bitmask_free(bm);
    bm = 0;
    qd_tracemask_del_router(tm, 3);
    qd_tracemask_remove_link(tm, 0);

    ingress = -1;
    bm = qd_tracemask_create(tm, pf, &ingress);
    qd_parse_free(pf);
    pf = 0;
    if (qd_bitmask_cardinality(bm) != 1) {
        sprintf(error, "Expected cardinality of 1, got %d", qd_bitmask_cardinality(bm));
        goto cleanup;
    }
    if (ingress != 0) {
        sprintf(error, "(B) Expected ingress index of 0, got %d", ingress);
        goto cleanup;
    }

    total = 0;
    for (QD_BITMASK_EACH(bm, bit, c)) {
        total += bit;
    }
    if (total != 3) {
        sprintf(error, "Expected total bit value of 3, got %d", total);
        // fallthrough
    }

cleanup:
    qd_parse_free(pf);
    qd_tracemask_free(tm);
    qd_bitmask_free(bm);
    for (qd_buffer_t *buf = DEQ_HEAD(list); buf; buf = DEQ_HEAD(list)) {
        DEQ_REMOVE_HEAD(list);
        qd_buffer_free(buf);
    }
    return *error ? error : 0;
}
Example #5
0
static char *test_map(void *context)
{
    static char error[1000];
    const char *data =
        "\xd1\x00\x00\x00\x2d\x00\x00\x00\x06"    // map32, 6 items
        "\xa3\x05\x66irst\xa1\x0evalue_of_first"  // (23) "first":"value_of_first"
        "\xa3\x06second\x52\x20"                  // (10) "second":32
        "\xa3\x05third\x41";                      // (8)  "third":true
    int data_len = 50;

    qd_iterator_t     *data_iter = qd_iterator_binary(data, data_len, ITER_VIEW_ALL);
    qd_parsed_field_t *field     = qd_parse(data_iter);

    if (!qd_parse_ok(field)) {
        snprintf(error, 1000, "Parse failed: %s", qd_parse_error(field));
        qd_iterator_free(data_iter);
        qd_parse_free(field);
        return error;
    }

    if (!qd_parse_is_map(field)) {
        qd_iterator_free(data_iter);
        qd_parse_free(field);
        return "Expected field to be a map";
    }

    uint32_t count = qd_parse_sub_count(field);
    if (count != 3) {
        snprintf(error, 1000, "Expected sub-count==3, got %"PRIu32, count);
        qd_iterator_free(data_iter);
        qd_parse_free(field);
        return error;
    }

    qd_parsed_field_t *key_field  = qd_parse_sub_key(field, 0);
    qd_iterator_t     *key_iter   = qd_parse_raw(key_field);
    qd_iterator_t     *typed_iter = qd_parse_typed(key_field);
    if (!qd_iterator_equal(key_iter, (unsigned char*) "first")) {
        unsigned char     *result   = qd_iterator_copy(key_iter);
        snprintf(error, 1000, "First key: expected 'first', got '%s'", result);
        free (result);
        return error;
    }

    if (!qd_iterator_equal(typed_iter, (unsigned char*) "\xa3\x05\x66irst"))
        return "Incorrect typed iterator on first-key";

    qd_parsed_field_t *val_field = qd_parse_sub_value(field, 0);
    qd_iterator_t     *val_iter  = qd_parse_raw(val_field);
    typed_iter = qd_parse_typed(val_field);
    if (!qd_iterator_equal(val_iter, (unsigned char*) "value_of_first")) {
        unsigned char     *result   = qd_iterator_copy(val_iter);
        snprintf(error, 1000, "First value: expected 'value_of_first', got '%s'", result);
        free (result);
        return error;
    }

    if (!qd_iterator_equal(typed_iter, (unsigned char*) "\xa1\x0evalue_of_first"))
        return "Incorrect typed iterator on first-key";

    key_field = qd_parse_sub_key(field, 1);
    key_iter  = qd_parse_raw(key_field);
    if (!qd_iterator_equal(key_iter, (unsigned char*) "second")) {
        unsigned char     *result   = qd_iterator_copy(key_iter);
        snprintf(error, 1000, "Second key: expected 'second', got '%s'", result);
        free (result);
        return error;
    }

    val_field = qd_parse_sub_value(field, 1);
    if (qd_parse_as_uint(val_field) != 32) {
        snprintf(error, 1000, "Second value: expected 32, got %"PRIu32, qd_parse_as_uint(val_field));
        return error;
    }

    key_field = qd_parse_sub_key(field, 2);
    key_iter  = qd_parse_raw(key_field);
    if (!qd_iterator_equal(key_iter, (unsigned char*) "third")) {
        unsigned char     *result   = qd_iterator_copy(key_iter);
        snprintf(error, 1000, "Third key: expected 'third', got '%s'", result);
        free (result);
        return error;
    }

    val_field = qd_parse_sub_value(field, 2);
    if (!qd_parse_as_bool(val_field)) {
        snprintf(error, 1000, "Third value: expected true");
        return error;
    }

    qd_iterator_free(data_iter);
    qd_parse_free(field);
    return 0;
}
Example #6
0
static char *test_integer_conversion(void *context)
{
    const struct fs_vector_t {
        const char *data;
        int         length;
        uint8_t     parse_as;
        bool        expect_fail;
        int64_t     expected_int;
        uint64_t    expected_uint;
    } fs_vectors[] = {
        // can successfully convert 64 bit values that are valid in the 32bit range
        {"\x80\x00\x00\x00\x00\xff\xff\xff\xff", 9, QD_AMQP_UINT, false, 0,         UINT32_MAX},
        {"\x80\x00\x00\x00\x00\x00\x00\x00\x00", 9, QD_AMQP_UINT, false, 0,         0},
        {"\x80\x00\x00\x00\x00\x00\x00\x00\x01", 9, QD_AMQP_UINT, false, 0,         1},
        {"\x81\x00\x00\x00\x00\x7f\xff\xff\xff", 9, QD_AMQP_INT,  false, INT32_MAX, 0},
        {"\x81\xFF\xFF\xFF\xFF\x80\x00\x00\x00", 9, QD_AMQP_INT,  false, INT32_MIN, 0},
        {"\x81\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9, QD_AMQP_INT,  false, -1,        0},

        // signed/unsigned conversions
        {"\x70\x7F\xFF\xFF\xFF",                 5, QD_AMQP_INT,  false, INT32_MAX, 0},
        {"\x71\x7F\xFF\xFF\xFF",                 5, QD_AMQP_UINT, false, 0,         INT32_MAX},
        {"\x80\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9, QD_AMQP_LONG, false, INT64_MAX, 0},
        {"\x81\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9, QD_AMQP_ULONG, false,0,         INT64_MAX},
        {"\x50\x7F",                             2, QD_AMQP_INT,  false, INT8_MAX,  0},
        {"\x60\x7F\xFF",                         3, QD_AMQP_INT,  false, INT16_MAX,  0},
        {"\x53\x7F",                             2, QD_AMQP_INT,  false, INT8_MAX,  0},
        {"\x55\x7F",                             2, QD_AMQP_UINT, false, 0,         INT8_MAX},
        {"\x51\x7F",                             2, QD_AMQP_UINT, false, 0,         INT8_MAX},
        {"\x61\x7F\xFF",                         3, QD_AMQP_UINT, false, 0,         INT16_MAX},

        // strings
        {"\xa1\x02 1",                           4, QD_AMQP_UINT, false, 0,         1},
        {"\xa1\x02-1",                           4, QD_AMQP_INT,  false, -1,        0},

        {"\xa1\x14" "18446744073709551615",     22, QD_AMQP_ULONG,false, 0,         UINT64_MAX},
        {"\xa1\x14" "-9223372036854775808",     22, QD_AMQP_LONG, false, INT64_MIN, 0},
        {"\xa1\x13" "9223372036854775807",      21, QD_AMQP_LONG, false, INT64_MAX, 0},
        {"\xa3\x13" "9223372036854775807",      21, QD_AMQP_LONG, false, INT64_MAX, 0},

        // cannot convert 64 bit values that are outside the 32bit range as int32
        {"\x80\x00\x00\x00\x01\x00\x00\x00\x00", 9, QD_AMQP_UINT, true,  0, 0},
        {"\x81\x00\x00\x00\x00\x80\x00\x00\x00", 9, QD_AMQP_INT,  true,  0, 0},
        {"\x81\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF", 9, QD_AMQP_INT,  true,  0, 0},

        // bad signed/unsigned conversions
        {"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 9, QD_AMQP_LONG,  true, 0, 0},
        {"\x81\x80\x00\x00\x00\x00\x00\x00\x00", 9, QD_AMQP_ULONG, true, 0, 0},
        {"\x70\x80\x00\x00\x00",                 5, QD_AMQP_LONG,  true, 0, 0},
        {"\x71\x80\x00\x00\x00",                 5, QD_AMQP_ULONG, true, 0, 0},
        {"\x55\x80",                             2, QD_AMQP_UINT,  true, 0, 0},
        {"\x51\x80",                             2, QD_AMQP_UINT,  true, 0, 0},
        {"\x54\x80",                             2, QD_AMQP_UINT,  true, 0, 0},
        {"\x61\x80\x00",                         3, QD_AMQP_UINT,  true, 0, 0},
        {"\x53\x80",                             2, QD_AMQP_INT,   true, 0, 0},
        {"\x52\x80",                             2, QD_AMQP_INT,   true, 0, 0},
        {"\x50\x80",                             2, QD_AMQP_LONG,  true, 0, 0},
        {"\x60\x80",                             2, QD_AMQP_LONG,  true, 0, 0},
        {NULL},
    };

    char *error = NULL;
    for (int i = 0; fs_vectors[i].data && !error; ++i) {
        qd_iterator_t     *data_iter = qd_iterator_binary(fs_vectors[i].data, fs_vectors[i].length, ITER_VIEW_ALL);
        qd_parsed_field_t *field = qd_parse(data_iter);

        if (!qd_parse_ok(field)) {
            error = "unexpected parse error";
            qd_iterator_free(data_iter);
            qd_parse_free(field);
            break;
        }

        bool equal = false;
        switch (fs_vectors[i].parse_as) {
        case QD_AMQP_UINT:
        {
            uint32_t tmp = qd_parse_as_uint(field);
            equal = (tmp == fs_vectors[i].expected_uint);
            break;
        }
        case QD_AMQP_ULONG:
        {
            uint64_t tmp = qd_parse_as_ulong(field);
            equal = (tmp == fs_vectors[i].expected_uint);
            break;
        }
        case QD_AMQP_INT:
        {

            int32_t tmp = qd_parse_as_int(field);
            equal = (tmp == fs_vectors[i].expected_int);
            break;
        }
        case QD_AMQP_LONG:
        {
            int64_t tmp = qd_parse_as_long(field);
            equal = (tmp == fs_vectors[i].expected_int);
            break;
        }
        }

        if (!qd_parse_ok(field)) {
            if (!fs_vectors[i].expect_fail) {
                error = "unexpected conversion/parse error";
            }
        } else if (fs_vectors[i].expect_fail) {
            error = "Conversion did not fail as expected";
        } else if (!equal) {
            error = "unexpected converted value";
        }

        qd_iterator_free(data_iter);
        qd_parse_free(field);
    }

    return error;
}
Example #7
0
static char* test_view_address_hash(void *context)
{
    struct {const char *addr; const char *view;} cases[] = {
    {"amqp:/_local/my-addr/sub",                "Lmy-addr/sub"},
    {"amqp:/_local/my-addr",                    "Lmy-addr"},
    {"amqp:/_topo/area/router/local/sub",       "Aarea"},
    {"amqp:/_topo/my-area/router/local/sub",    "Rrouter"},
    {"amqp:/_topo/my-area/my-router/local/sub", "Llocal/sub"},
    {"amqp:/_topo/area/all/local/sub",          "Aarea"},
    {"amqp:/_topo/my-area/all/local/sub",       "Tlocal/sub"},
    {"amqp:/_topo/all/all/local/sub",           "Tlocal/sub"},
    {"amqp://host:port/_local/my-addr",         "Lmy-addr"},
    {"_topo/area/router/my-addr",               "Aarea"},
    {"_topo/my-area/router/my-addr",            "Rrouter"},
    {"_topo/my-area/my-router/my-addr",         "Lmy-addr"},
    {"_topo/my-area/router",                    "Rrouter"},
    {"amqp:/mobile",                            "M1mobile"},
    {"mobile",                                  "M1mobile"},
    {"/mobile",                                 "M1mobile"},

    // Re-run the above tests to make sure trailing dots are ignored.
    {"amqp:/_local/my-addr/sub.",                "Lmy-addr/sub"},
    {"amqp:/_local/my-addr.",                    "Lmy-addr"},
    {"amqp:/_topo/area/router/local/sub.",       "Aarea"},
    {"amqp:/_topo/my-area/router/local/sub.",    "Rrouter"},
    {"amqp:/_topo/my-area/my-router/local/sub.", "Llocal/sub"},
    {"amqp:/_topo/area/all/local/sub.",          "Aarea"},
    {"amqp:/_topo/my-area/all/local/sub.",       "Tlocal/sub"},
    {"amqp:/_topo/all/all/local/sub.",           "Tlocal/sub"},
    {"amqp://host:port/_local/my-addr.",         "Lmy-addr"},
    {"_topo/area/router/my-addr.",               "Aarea"},
    {"_topo/my-area/router/my-addr.",            "Rrouter"},
    {"_topo/my-area/my-router/my-addr.",         "Lmy-addr"},
    {"_topo/my-area/router.",                    "Rrouter"},
    {"_topo/my-area/router:",                    "Rrouter:"},

    {0, 0}
    };
    int idx;

    for (idx = 0; cases[idx].addr; idx++) {
        qd_iterator_t *iter = qd_iterator_string(cases[idx].addr, ITER_VIEW_ADDRESS_HASH);
        char *ret = view_address_hash(context, iter, cases[idx].addr, cases[idx].view);
        qd_iterator_free(iter);
        if (ret) return ret;
    }

    for (idx = 0; cases[idx].addr; idx++) {
        qd_buffer_list_t chain;
        DEQ_INIT(chain);
        build_buffer_chain(&chain, cases[idx].addr, 3);
        qd_iterator_t *iter = qd_iterator_buffer(DEQ_HEAD(chain), 0,
                                                 strlen(cases[idx].addr),
                                                 ITER_VIEW_ADDRESS_HASH);
        char *ret = view_address_hash(context, iter, cases[idx].addr, cases[idx].view);
        release_buffer_chain(&chain);
        if (ret) return ret;
    }

    return 0;
}
Example #8
0
/**
 * Inbound Delivery Handler
 */
static void AMQP_rx_handler(void* context, qd_link_t *link, pn_delivery_t *pnd)
{
    qd_router_t    *router   = (qd_router_t*) context;
    pn_link_t      *pn_link  = qd_link_pn(link);
    qdr_link_t     *rlink    = (qdr_link_t*) qd_link_get_context(link);
    qdr_delivery_t *delivery = 0;
    qd_message_t   *msg;

    //
    // Receive the message into a local representation.  If the returned message
    // pointer is NULL, we have not yet received a complete message.
    //
    // Note:  In the link-routing case, consider cutting the message through.  There's
    //        no reason to wait for the whole message to be received before starting to
    //        send it.
    //
    msg = qd_message_receive(pnd);
    if (!msg)
        return;

    //
    // Consume the delivery.
    //
    pn_link_advance(pn_link);

    //
    // If there's no router link, free the message and finish.  It's likely that the link
    // is closing.
    //
    if (!rlink) {
        qd_message_free(msg);
        return;
    }

    //
    // Handle the link-routed case
    //
    if (qdr_link_is_routed(rlink)) {
        pn_delivery_tag_t dtag = pn_delivery_tag(pnd);
        delivery = qdr_link_deliver_to_routed_link(rlink, msg, pn_delivery_settled(pnd), (uint8_t*) dtag.start, dtag.size);
        if (delivery) {
            if (pn_delivery_settled(pnd))
                pn_delivery_settle(pnd);
            else {
                pn_delivery_set_context(pnd, delivery);
                qdr_delivery_set_context(delivery, pnd);
                qdr_delivery_incref(delivery);
            }
        }
        return;
    }

    //
    // Determine if the incoming link is anonymous.  If the link is addressed,
    // there are some optimizations we can take advantage of.
    //
    bool anonymous_link = qdr_link_is_anonymous(rlink);

    //
    // Determine if the user of this connection is allowed to proxy the
    // user_id of messages. A message user_id is proxied when the
    // property value differs from the authenticated user name of the connection.
    // If the user is not allowed to proxy the user_id then the message user_id
    // must be blank or it must be equal to the connection user name.
    //
    bool             check_user = false;
    qd_connection_t *conn       = qd_link_connection(link);
    if (conn->policy_settings) 
        check_user = !conn->policy_settings->allowUserIdProxy;

    //
    // Validate the content of the delivery as an AMQP message.  This is done partially, only
    // to validate that we can find the fields we need to route the message.
    //
    // If the link is anonymous, we must validate through the message properties to find the
    // 'to' field.  If the link is not anonymous, we don't need the 'to' field as we will be
    // using the address from the link target.
    //
    qd_message_depth_t  validation_depth = (anonymous_link || check_user) ? QD_DEPTH_PROPERTIES : QD_DEPTH_MESSAGE_ANNOTATIONS;
    bool                valid_message    = qd_message_check(msg, validation_depth);

    if (valid_message) {
        if (check_user) {
            // This connection must not allow proxied user_id
            qd_iterator_t *userid_iter  = qd_message_field_iterator(msg, QD_FIELD_USER_ID);
            if (userid_iter) {
                // The user_id property has been specified
                if (qd_iterator_remaining(userid_iter) > 0) {
                    // user_id property in message is not blank
                    if (!qd_iterator_equal(userid_iter, (const unsigned char *)conn->user_id)) {
                        // This message is rejected: attempted user proxy is disallowed
                        qd_log(router->log_source, QD_LOG_DEBUG, "Message rejected due to user_id proxy violation. User:%s", conn->user_id);
                        pn_link_flow(pn_link, 1);
                        pn_delivery_update(pnd, PN_REJECTED);
                        pn_delivery_settle(pnd);
                        qd_message_free(msg);
                        qd_iterator_free(userid_iter);
                        return;
                    }
                }
                qd_iterator_free(userid_iter);
            }
        }

        qd_parsed_field_t   *in_ma        = qd_message_message_annotations(msg);
        qd_bitmask_t        *link_exclusions;
        bool                 strip        = qdr_link_strip_annotations_in(rlink);
        qd_iterator_t *ingress_iter = router_annotate_message(router, in_ma, msg, &link_exclusions, strip);

        if (anonymous_link) {
            qd_iterator_t *addr_iter = 0;
            int phase = 0;
            
            //
            // If the message has delivery annotations, get the to-override field from the annotations.
            //
            if (in_ma) {
                qd_parsed_field_t *ma_to = qd_parse_value_by_key(in_ma, QD_MA_TO);
                if (ma_to) {
                    addr_iter = qd_iterator_dup(qd_parse_raw(ma_to));
                    phase = qd_message_get_phase_annotation(msg);
                }
            }

            //
            // Still no destination address?  Use the TO field from the message properties.
            //
            if (!addr_iter)
                addr_iter = qd_message_field_iterator(msg, QD_FIELD_TO);

            if (addr_iter) {
                qd_iterator_reset_view(addr_iter, ITER_VIEW_ADDRESS_HASH);
                if (phase > 0)
                    qd_iterator_annotate_phase(addr_iter, '0' + (char) phase);
                delivery = qdr_link_deliver_to(rlink, msg, ingress_iter, addr_iter, pn_delivery_settled(pnd),
                                               link_exclusions);
            }
        } else {
            const char *term_addr = pn_terminus_get_address(qd_link_remote_target(link));
            if (!term_addr)
                term_addr = pn_terminus_get_address(qd_link_source(link));

            if (term_addr) {
                qd_composed_field_t *to_override = qd_compose_subfield(0);
                qd_compose_insert_string(to_override, term_addr);
                qd_message_set_to_override_annotation(msg, to_override);
                int phase = qdr_link_phase(rlink);
                if (phase != 0)
                    qd_message_set_phase_annotation(msg, phase);
            }
            delivery = qdr_link_deliver(rlink, msg, ingress_iter, pn_delivery_settled(pnd), link_exclusions);
        }

        if (delivery) {
            if (pn_delivery_settled(pnd))
                pn_delivery_settle(pnd);
            else {
                pn_delivery_set_context(pnd, delivery);
                qdr_delivery_set_context(delivery, pnd);
                qdr_delivery_incref(delivery);
            }
        } else {
            //
            // The message is now and will always be unroutable because there is no address.
            //
            pn_link_flow(pn_link, 1);
            pn_delivery_update(pnd, PN_REJECTED);
            pn_delivery_settle(pnd);
            qd_message_free(msg);
        }

        //
        // Rules for delivering messages:
        //
        // For addressed (non-anonymous) links:
        //   to-override must be set (done in the core?)
        //   uses qdr_link_deliver to hand over to the core
        //
        // For anonymous links:
        //   If there's a to-override in the annotations, use that address
        //   Or, use the 'to' field in the message properties
        //



    } else {
        //
        // Message is invalid.  Reject the message and don't involve the router core.
        //
        pn_link_flow(pn_link, 1);
        pn_delivery_update(pnd, PN_REJECTED);
        pn_delivery_settle(pnd);
        qd_message_free(msg);
    }
}