示例#1
0
static
int _aux_srvc_add_service_cb(void* item, void* cookie)
{
    struct vservice* srvc_item = (struct vservice*)item;
    varg_decl(cookie, 0, struct vservice**, to);
    varg_decl(cookie, 1, vsrvcInfo*, srvci);
    varg_decl(cookie, 2, time_t*, now);
    varg_decl(cookie, 3, int*, max_period);
    varg_decl(cookie, 4, int*, found);

    if (vtoken_equal(&srvc_item->srvci->hostid, &srvci->hostid) &&
        vsrvcHash_equal(&srvc_item->srvci->hash, &srvci->hash)) {
        *to = srvc_item;
        *found = 1;
        return 1;
    }
    if ((int)(*now - srvc_item->rcv_ts) > *max_period) {
        *to = srvc_item;
        *max_period = *now - srvc_item->rcv_ts;
    }
    return 0;
}
示例#2
0
文件: vroute.c 项目: zll5267/vdht
/*
 * the routine to pack a @ping query and send it.
 * @route:
 * @conn:
 */
static
int _vroute_dht_ping(struct vroute* route, vnodeConn* conn)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    retS((!(route->props & PROP_PING))); //ping disabled.

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->ping(&token, &route->myid, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    route->ops->inspect(route, &token, VROUTE_INSP_SND_PING);

    // make a record according to the query, which will be used to
    // check invality of response msg.
    recr_space->ops->make(recr_space, &token);
    vlogD("send @ping");
    return 0;
}


/*
 * the routine to pack and send a response to @ping query back to source node
 * where the ping query was from.
 * @route:
 * @conn:
 * @token:
 * @info :
 */
static
int _vroute_dht_ping_rsp(struct vroute* route, vnodeConn* conn, vtoken* token, vnodeInfo* nodei)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);
    vassert(nodei);
    retS((!(route->props & PROP_PING_R)));

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->ping_rsp(token, &route->myid, nodei, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @ping_rsp");
    return 0;
}

/*
 * the routine to pack and send @find_node query to specific node to get infos
 * of a given node.
 * @route:
 * @conn:
 * @target
 */
static
int _vroute_dht_find_node(struct vroute* route, vnodeConn* conn, vnodeId* targetId)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(targetId);
    retS((!(route->props & PROP_FIND_NODE)));

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->find_node(&token, &route->myid, targetId, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    route->ops->inspect(route, &token, VROUTE_INSP_SND_FIND_NODE);
    recr_space->ops->make(recr_space, &token);
    vlogD("send @find_node");
    return 0;
}

/*
 * the routine to pack and send a response to @find_node query.
 * @route:
 * @conn:
 * @token:
 * @info:
 */
static
int _vroute_dht_find_node_rsp(struct vroute* route, vnodeConn* conn, vtoken* token, vnodeInfo* nodei)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);
    vassert(nodei);
    retS((!(route->props & PROP_FIND_NODE_R)));

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->find_node_rsp(token, &route->myid, nodei, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @find_node_rsp");
    return 0;
}

/*
 * the routine to pack and send a @find_closest_nodes query( closest to @targetId).
 * @route:
 * @conn:
 * @targetId:
 */
static
int _vroute_dht_find_closest_nodes(struct vroute* route, vnodeConn* conn, vnodeId* targetId)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(targetId);
    retS((!(route->props & PROP_FIND_CLOSEST_NODES)));

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->find_closest_nodes(&token, &route->myid, targetId, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    route->ops->inspect(route, &token, VROUTE_INSP_SND_FIND_CLOSEST_NODES);
    recr_space->ops->make(recr_space, &token);
    vlogD("send @find_closest_nodes");
    return 0;
}

/*
 * the routine to pack and send back a response to @find_closest_nodes query.
 * @route:
 * @conn:
 * @token:
 * @closest: array of closest nodes (closest to given id);
 */
static
int _vroute_dht_find_closest_nodes_rsp(struct vroute* route, vnodeConn* conn, vtoken* token, struct varray* closest)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);
    vassert(closest);
    retS((!(route->props & PROP_FIND_CLOSEST_NODES_R)));

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->find_closest_nodes_rsp(token, &route->myid, closest, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @find_closest_nodes_rsp");
    return 0;
}

/*
 * the routine to pack and send a @reflex_addr query.
 * @route:
 * @conn:
 */
static
int _vroute_dht_reflex(struct vroute* route, vnodeConn* conn)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->reflex(&token, &route->myid, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    route->ops->inspect(route, &token, VROUTE_INSP_SND_REFLEX);
    recr_space->ops->make(recr_space, &token);
    vlogD("send @reflex");
    return 0;
}

/*
 * the routine to pack and send back a response to @reflex_addr query.
 * @route:
 * @conn:
 * @token:
 * @reflexive_addr:
 */
static
int _vroute_dht_reflex_rsp(struct vroute* route, vnodeConn* conn, vtoken* token, struct sockaddr_in* reflexive_addr)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);
    vassert(reflexive_addr);

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->reflex_rsp(token, &route->myid, reflexive_addr, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @reflex_rsp");
    return 0;
}

/*
 * the routine to pack and send a @probe_connectivity query
 * @route:
 * @conn:
 * @targetId:
 */
static
int _vroute_dht_probe(struct vroute* route, vnodeConn* conn, vnodeId* targetId)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(targetId);

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->probe(&token, &route->myid, targetId, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    route->ops->inspect(route, &token, VROUTE_INSP_SND_PROBE);
    recr_space->ops->make(recr_space, &token);
    vlogD("send @probe");
    return 0;
}

/*
 * the routine to pack and send back a response to @probe_connectivity query.
 * @route:
 * @conn:
 * @token:
 */
static
int _vroute_dht_probe_rsp(struct vroute* route, vnodeConn* conn, vtoken* token)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->probe_rsp(token, &route->myid, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @probe_rsp");
    return 0;
}

/*
 * the routine to pack and send @post_service indication.
 * @route:
 * @conn:
 * @srvc:
 */
static
int _vroute_dht_post_service(struct vroute* route, vnodeConn* conn, vsrvcInfo* srvci)
{
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(srvci);
    retS((!(route->props & PROP_POST_SERVICE)));

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->post_service(&token, &route->myid, srvci, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @post_service");
    return 0;
}

/*
 * the routine to pack and send a @find_service query.
 * @route:
 * @conn:
 * @srvcId:
 */
static
int _vroute_dht_find_service(struct vroute* route, vnodeConn* conn, vsrvcHash* hash)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    void* buf = NULL;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(hash);
    retS((!(route->props & PROP_FIND_SERVICE)));

    buf = vdht_buf_alloc();
    retE((!buf));

    vtoken_make(&token);
    ret = route->enc_ops->find_service(&token, &route->myid, hash, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }

    route->ops->inspect(route, &token, VROUTE_INSP_SND_FIND_SERVICE);
    recr_space->ops->make(recr_space, &token);
    vlogD("send @find_service");
    return 0;
}

/*
 * the routine to pack and send a response to @find_service query.
 * @route:
 * @conn:
 * @token:
 * @srvcs:
 */
static
int _vroute_dht_find_service_rsp(struct vroute* route, vnodeConn* conn, vtoken* token, vsrvcInfo* srvc)
{
    void* buf = NULL;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(token);
    vassert(srvc);
    retS((!(route->props & PROP_FIND_SERVICE_R)));

    buf = vdht_buf_alloc();
    retE((!buf));

    ret = route->enc_ops->find_service_rsp(token, &route->myid, srvc, buf, vdht_buf_len());
    ret1E((ret < 0), vdht_buf_free(buf));
    {
        struct vmsg_usr msg = {
            .addr  = to_vsockaddr_from_sin(&conn->remote),
            .spec  = to_vsockaddr_from_sin(&conn->local),
            .msgId = VMSG_DHT,
            .data  = buf,
            .len   = ret
        };
        ret = route->msger->ops->push(route->msger, &msg);
        ret1E((ret < 0), vdht_buf_free(buf));
    }
    vlogD("send @find_service_rsp");
    return 0;
}

static
struct vroute_dht_ops route_dht_ops = {
    .ping                   = _vroute_dht_ping,
    .ping_rsp               = _vroute_dht_ping_rsp,
    .find_node              = _vroute_dht_find_node,
    .find_node_rsp          = _vroute_dht_find_node_rsp,
    .find_closest_nodes     = _vroute_dht_find_closest_nodes,
    .find_closest_nodes_rsp = _vroute_dht_find_closest_nodes_rsp,
    .reflex                 = _vroute_dht_reflex,
    .reflex_rsp             = _vroute_dht_reflex_rsp,
    .probe                  = _vroute_dht_probe,
    .probe_rsp              = _vroute_dht_probe_rsp,
    .post_service           = _vroute_dht_post_service,
    .find_service           = _vroute_dht_find_service,
    .find_service_rsp       = _vroute_dht_find_service_rsp
};

static
void _aux_vnodeInfo_free(void* item, void* cookie)
{
    vnodeInfo_relax* nodei = (vnodeInfo_relax*)item;
    vassert(nodei);

    vnodeInfo_relax_free(nodei);
    return ;
}

/* the routine to call when receiving a @ping query.
 *
 * @route:
 * @conn:
 * @ctxt:  dht decoder context.
 */
static
int _vroute_cb_ping(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_node_space* node_space = &route->node_space;
    vnodeInfo_relax nodei_relax;
    vnodeInfo* nodei = (vnodeInfo*)&nodei_relax;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret =route->dec_ops->ping(ctxt, &token, &fromId);
    retE((ret < 0));

    vnodeInfo_relax_init(&nodei_relax, &fromId, vnodeVer_unknown(), 0);
    vnodeInfo_add_addr(&nodei, &conn->remote);
    ret = node_space->ops->add_node(node_space, nodei, 1);
    retE((ret < 0));

    route->ops->inspect(route, &token, VROUTE_INSP_RCV_PING);
    ret = route->dht_ops->ping_rsp(route, conn, &token, (vnodeInfo*)&route->node->nodei);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receving a response to @ping query.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_ping_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_node_space* node_space = &route->node_space;
    struct vroute_recr_space* recr_space = &route->recr_space;
    vnodeInfo_relax nodei;
    vtoken  token;
    int ret = 0;

    ret = route->dec_ops->ping_rsp(ctxt, &token, (vnodeInfo*)&nodei);
    retE((ret < 0));
    retE((!recr_space->ops->check(recr_space, &token))); // skip vicious response.

    ret = node_space->ops->add_node(node_space, (vnodeInfo*)&nodei, 1);
    retE((ret < 0));
    route->ops->inspect(route, &token, VROUTE_INSP_RCV_PING_RSP);
    return 0;
}

/*
 * the routine to call when receiving a @find_node query.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_node(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_node_space* node_space = &route->node_space;
    vnodeInfo_relax nodei;
    struct varray closest;
    vnodeId targetId;
    vnodeId fromId;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->find_node(ctxt, &token, &fromId, &targetId);
    retE((ret < 0));
    ret = node_space->ops->get_node(node_space, &targetId, (vnodeInfo*)&nodei);
    retE((ret < 0));
    if (ret == 1) { //means found
        ret = route->dht_ops->find_node_rsp(route, conn, &token, (vnodeInfo*)&nodei);
        retE((ret < 0));
        return 0;
    }

    // otherwise, send @find_closest_nodes_rsp response instead.
    varray_init(&closest, MAX_CAPC);
    ret = node_space->ops->get_neighbors(node_space, &targetId, &closest, MAX_CAPC);
    ret1E((ret < 0), varray_deinit(&closest));
    if (ret == 0) { //means no closest nodes.
        varray_deinit(&closest);
        return 0;
    }
    ret = route->dht_ops->find_closest_nodes_rsp(route, conn, &token, &closest);
    varray_zero(&closest, _aux_vnodeInfo_free, NULL);
    varray_deinit(&closest);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receiving a response to @find_node query.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_node_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_node_space* node_space = &route->node_space;
    struct vroute_recr_space* recr_space = &route->recr_space;
    vnodeInfo_relax nodei;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->find_node_rsp(ctxt, &token, &fromId, (vnodeInfo*)&nodei);
    retE((ret < 0));
    retE((!recr_space->ops->check(recr_space, &token)));

    ret = node_space->ops->add_node(node_space, (vnodeInfo*)&nodei, 0);
    retE((ret < 0));

    route->ops->inspect(route, &token, VROUTE_INSP_RCV_FIND_NODE_RSP);
    return 0;
}

/*
 * the routine to call when receiving a @find_closest_nodes query.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_closest_nodes(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_node_space* node_space = &route->node_space;
    struct varray closest;
    vnodeId targetId;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->find_closest_nodes(ctxt, &token, &fromId, &targetId);
    retE((ret < 0));

    varray_init(&closest, MAX_CAPC);
    ret = node_space->ops->get_neighbors(node_space, &targetId, &closest, MAX_CAPC);
    ret1E((ret < 0), varray_deinit(&closest));
    if (ret == 0) {
        varray_deinit(&closest);
        return 0; // not response if no closest nodes found.
    }

    ret = route->dht_ops->find_closest_nodes_rsp(route, conn, &token, &closest);
    varray_zero(&closest, _aux_vnodeInfo_free, NULL);
    varray_deinit(&closest);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receiving a response to @find_closest_nodes
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_closest_nodes_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    struct vroute_node_space* node_space = &route->node_space;
    struct varray closest;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;
    int i = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    varray_init(&closest, MAX_CAPC);
    ret = route->dec_ops->find_closest_nodes_rsp(ctxt, &token, &fromId, &closest);
    ret1E((ret < 0), varray_deinit(&closest));
    if (!recr_space->ops->check(recr_space, &token)) {
        varray_zero(&closest, _aux_vnodeInfo_free, NULL);
        varray_deinit(&closest);
        return -1;
    }

    for (i = 0; i < varray_size(&closest); i++) {
        node_space->ops->add_node(node_space, (vnodeInfo*)varray_get(&closest, i), 0);
    }
    varray_zero(&closest, _aux_vnodeInfo_free, NULL);
    varray_deinit(&closest);

    route->ops->inspect(route, &token, VROUTE_INSP_RCV_FIND_CLOSEST_NODES_RSP);
    return 0;
}

/*
 * the routine to call when receiving a @reflex query.
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_reflex(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->reflex(ctxt, &token, &fromId);
    retE((ret < 0));

    ret = route->dht_ops->reflex_rsp(route, conn, &token, &conn->remote);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receiving a response to @reflex
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_reflex_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    struct vnode* node = route->node;
    struct sockaddr_in reflexive_addr;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->reflex_rsp(ctxt, &token, &fromId, &reflexive_addr);
    retE((ret < 0));
    retE((!recr_space->ops->check(recr_space, &token)));

    ret = node->ops->reflex_addr(node, &conn->local, &reflexive_addr);
    retE((ret < 0));
    route->ops->inspect(route, &token, VROUTE_INSP_RCV_REFLEX_RSP);
    return 0;
}

/*
 * the routine to call when receiving a @probe query.
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_probe(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    vnodeId targetId;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->probe(ctxt, &token, &fromId, &targetId);
    retE((ret < 0));
    retE((!vtoken_equal(&targetId, &route->myid)));

    ret = route->dht_ops->probe_rsp(route, conn, &token);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receiving a response to @probe query.
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_probe_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_recr_space* recr_space = &route->recr_space;
    struct vroute_node_space* node_space = &route->node_space;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->probe_rsp(ctxt, &token, &fromId);
    retE((ret < 0));
    retE((!recr_space->ops->check(recr_space, &token)));

    ret = node_space->ops->adjust_connectivity(node_space, &fromId, conn);
    retE((ret < 0));
    route->ops->inspect(route, &token, VROUTE_INSP_RCV_PROBE_RSP);
    return 0;
}

/*
 * the routine to call when receiving a @post_service indication.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_post_service(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_srvc_space* srvc_space = &route->srvc_space;
    vsrvcInfo_relax srvci;
    vnodeId fromId;
    vtoken  token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    ret = route->dec_ops->post_service(ctxt, &token, &fromId, (vsrvcInfo*)&srvci);
    retE((ret < 0));
    ret = srvc_space->ops->add_service(srvc_space, (vsrvcInfo*)&srvci);
    retE((ret < 0));
    route->ops->inspect(route, &token, VROUTE_INSP_RCV_POST_SERVICE);
    return 0;
}

/*
 * the routine to call when receiving a @find_service query.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_service(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_srvc_space* srvc_space = &route->srvc_space;
    vsrvcInfo_relax srvci;
    vsrvcHash srvcHash;
    vnodeId fromId;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(conn);
    vassert(ctxt);

    memset(&srvci, 0, sizeof(srvci));
    srvci.capc = VSRVCINFO_MAX_ADDRS;

    ret = route->dec_ops->find_service(ctxt, &token, &fromId, &srvcHash);
    retE((ret < 0));
    ret = srvc_space->ops->get_service(srvc_space, &srvcHash, (vsrvcInfo*)&srvci);
    retE((ret < 0));
    retS((ret == 0));
    ret = route->dht_ops->find_service_rsp(route, conn, &token, (vsrvcInfo*)&srvci);
    retE((ret < 0));
    return 0;
}

/*
 * the routine to call when receiving a response to @find_service.
 *
 * @route:
 * @conn:
 * @ctxt:
 */
static
int _vroute_cb_find_service_rsp(struct vroute* route, vnodeConn* conn, void* ctxt)
{
    struct vroute_srvc_probe_helper* probe_helper = &route->probe_helper;
    struct vroute_srvc_space* srvc_space = &route->srvc_space;
    struct vroute_recr_space* recr_space = &route->recr_space;
    struct vroute_node_space* node_space = &route->node_space;
    vsrvcInfo_relax srvci;
    vnodeId fromId;
    vtoken token;
    int ret = 0;

    vassert(route);
    vassert(ctxt);
    vassert(conn);

    ret = route->dec_ops->find_service_rsp(ctxt, &token, &fromId, (vsrvcInfo*)&srvci);
    retE((ret < 0));
    retE((!recr_space->ops->check(recr_space, &token)));

    ret = srvc_space->ops->add_service(srvc_space, (vsrvcInfo*)&srvci);
    retE((ret < 0));

    //try to add info of node hosting that service.
    ret = node_space->ops->probe_node(node_space, &srvci.hostid);
    retE((ret < 0));
    route->ops->inspect(route, &token, VROUTE_INSP_RCV_FIND_SERVICE_RSP);
    probe_helper->ops->invoke(probe_helper, &srvci.hash, (vsrvcInfo*)&srvci);
    return 0;
}

static
vroute_dht_cb_t route_cb_ops[] = {
    _vroute_cb_ping,
    _vroute_cb_ping_rsp,
    _vroute_cb_find_node,
    _vroute_cb_find_node_rsp,
    _vroute_cb_find_closest_nodes,
    _vroute_cb_find_closest_nodes_rsp,
    _vroute_cb_reflex,
    _vroute_cb_reflex_rsp,
    _vroute_cb_probe,
    _vroute_cb_probe_rsp,
    _vroute_cb_post_service,
    _vroute_cb_find_service,
    _vroute_cb_find_service_rsp,
    NULL
};

static
int _aux_route_msg_cb(void* cookie, struct vmsg_usr* mu)
{
    struct vroute* route = (struct vroute*)cookie;
    vnodeConn conn;
    void* ctxt = NULL;
    int   ret  = 0;

    vassert(route);
    vassert(mu);

    ret = route->dec_ops->dec_begin(mu->data, mu->len, &ctxt);
    retE((ret >= VDHT_UNKNOWN));
    retE((ret < 0));
    vlogD("received @%s", vdht_get_desc(ret));

    vnodeConn_set(&conn, to_sockaddr_sin(mu->spec), to_sockaddr_sin(mu->addr));
    vlock_enter(&route->lock);
    ret = route->cb_ops[ret](route, &conn, ctxt);
    vlock_leave(&route->lock);
    route->dec_ops->dec_done(ctxt);
    retE((ret < 0));
    return 0;
}

static
int _aux_route_load_proto_caps(struct vconfig* cfg, uint32_t* props)
{
    struct varray* tuple = NULL;
    int i = 0;

    vassert(cfg);
    vassert(props);

    tuple = cfg->ops->get_tuple_val(cfg, "dht.protocol");
    if (!tuple) {
        *props |= PROP_PING;
        *props |= PROP_PING_R;
        return 0;
    }
    for (i = 0; i < varray_size(tuple); i++) {
        struct vcfg_item* item = NULL;
        int dht_id = -1;

        item = (struct vcfg_item*)varray_get(tuple, i);
        retE((item->type != CFG_STR));
        dht_id = vdht_get_dhtId_by_desc(item->val.s);
        retE((dht_id < 0));
        retE((dht_id >= VDHT_UNKNOWN));

        *props |= (1 << dht_id);
    }
    return 0;
}

int vroute_init(struct vroute* route, struct vconfig* cfg, struct vhost* host, vnodeId* myid)
{
    vassert(route);
    vassert(host);
    vassert(myid);

    vtoken_copy(&route->myid, myid);
    _aux_route_load_proto_caps(cfg, &route->props);

    vlock_init(&route->lock);
    vroute_node_space_init(&route->node_space, route, cfg, myid);
    vroute_srvc_space_init(&route->srvc_space, cfg);
    vroute_recr_space_init(&route->recr_space);
    vroute_srvc_probe_helper_init(&route->probe_helper);

    route->ops     = &route_ops;
    route->dht_ops = &route_dht_ops;
    route->cb_ops  = route_cb_ops;
    route->enc_ops = &dht_enc_ops;
    route->dec_ops = &dht_dec_ops;

    route->cfg   = cfg;
    route->msger = &host->msger;
    route->node  = &host->node;

    route->insp_cb = NULL;
    route->insp_cookie = NULL;

    route->msger->ops->add_cb(route->msger, route, _aux_route_msg_cb, VMSG_DHT);
    return 0;
}

void vroute_deinit(struct vroute* route)
{
    vassert(route);

    vroute_srvc_probe_helper_deinit (&route->probe_helper);
    vroute_recr_space_deinit(&route->recr_space);
    vroute_node_space_deinit(&route->node_space);
    vroute_srvc_space_deinit(&route->srvc_space);
    vlock_deinit(&route->lock);
    return ;
}