typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];

本文介绍了一种利用checked_delete和checked_array_delete实现的安全删除方法,通过在编译阶段捕获潜在错误来增强程序的健壮性。

看智能指针的时候遇到一组函数蛮有意思的,即checked_delete(T* x)和checked_array_delete(T* x),这两个函数的作用是安全删除参数所指向的变量或数组。

[cpp]  view plain  copy
  1. template<class T> inline void checked_delete(T* x)  
  2. {  
  3.   typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  
  4.   (voidsizeof(type_must_be_complete);  
  5.   delete x;  
  6. }  
  7.   
  8. template<class T> inline void checked_array_delete(T* x)  
  9. {  
  10.   typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  
  11.   (voidsizeof(type_must_be_complete);  
  12.   delete[] x;  
  13. }  

函数只有最后一行不同,分别调用了delete和delete[]操作符,顺便说一句,在C/C++中指针既可以指向一个变量,也可以指向一个数组,因此这两个函数仅仅从参数无法区别出来待删除的是单个变量还是数组,只能由调用者自行保证调用了正确的函数。

函数总共三行语句,第三行是根本目的,很容易理解,前两行的目的就是为了所谓的安全性了。怎么个安全法呢?这两个函数是函数模版,在编译时无法确定参数的类型,而动态运行时的错误是比较棘手的,所以用这行代码:

[cpp]  view plain  copy
  1. typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  

来将运行时错误转变为编译期错误。这句话其实就是定义了一个固定大小的char型数组,数组名为type_must_be_complete,数组大小是多少呢?是sizeof(T)?1:-1, ?:这个三元操作符大家都很熟悉了,若sizeof(T)非0,这个表达式的值为1,即typedef了一个大小为1的char型数组,否则定义一个大小为-1的数组。数组大小还能为负数?当然不能,于是就会报错,而且是编译期错误,于是就将一个动态运行时错误在编译时就发现了。

接下来解释sizeof什么时候返回0. C/C++语言本身似乎没有这种情况,但有些编译器会作一些扩展,比如GCC对于incomplete type使用sizeof时,会返回0.那什么又叫做incomplete type呢,就是那些声明了,但没有定义的类型,例如:

[cpp]  view plain  copy
  1. class A;  
  2.   
  3. extern A a;  

C++标准允许通过一个 delete 表达式删除指向不完全类的指针。如果该类有一个非平凡的析构函数,或者有一个类相关的 delete 操作符,那么其行为就是无定义的。因此编译器作了这种扩展,以将这种未定义的行为转为编译期错误,帮助程序员们及早发现。

函数的第二行语句的作用据说是为了防止编译器优化,因为编译器检测到typedef的类型未被使用到的话可能就会将其优化掉,因而第二行语句使用了这个类型,告诉编译器这个typedef是有用的,不能优化掉。至于(void)强制类型转换,问了同学,说是为了消除编译器对未使用sizeof返回值的警告。

仅仅几行代码都有这么多讲究,要学的东西还很多啊。错误和疏漏的地方欢迎批评指正!

/* * Copyright 2019 TP-Link Technologies Co., Ltd. All rights reserved. * * */ #include "nano_bus.h" #include "nano_switch.h" #include "nano_loop.h" //#define NANO_BUS_DEBUG #ifdef NANO_BUS_DEBUG #define NANO_BUS_LOG(format, ...) OSAL_PRINTF("%s:%d: "format, __FUNCTION__, __LINE__, ##__VA_ARGS__) #else #define NANO_BUS_LOG(format, ...) #endif #define NANO_BUS_DAEMON_ADDR 0x1 enum { NANO_BUS_MSG_REQUEST, NANO_BUS_MSG_RESPONSE, NANO_BUS_MSG_EVENT, }; typedef struct _nano_bus_request_t { struct list_head list; SH_INT32 id; nano_bus_request_cb_t cb; void *priv; SH_BOOL done; SH_BOOL async; SH_UINT64 time; } nano_bus_request_t; typedef struct _nano_busd_event_node_t { struct list_head list; SH_CHAR *name; SH_INT32 ref_count; } nano_busd_event_node_t; typedef struct _nano_busd_event_t { struct list_head list; struct list_head evts; SH_UINT8 addr; } nano_busd_event_t; typedef struct _nano_busd_context_t { struct list_head objs; struct list_head evts; struct nano_loop_context *loop_ctx; struct nano_loop_fd sock; struct nano_loop_timeout timer; } nano_busd_context_t; static void nano_bus_relase_msg_data(nano_bus_msg_t *msg) { if (msg->data && !msg->no_free) { json_delete(msg->data); } msg->data = NULL; msg->no_free = FALSE; } static void nano_bus_relase_msg(nano_bus_msg_t *msg) { if (!msg) { return; } if (msg->obj) { osal_free(msg->obj); } if (msg->method) { osal_free(msg->method); } nano_bus_relase_msg_data(msg); osal_free(msg); } static SH_INT32 _nano_bus_send_reply(nano_switch_fd_t fd, SH_UINT8 addr, nano_bus_msg_t *msg, json_t *data, SH_INT32 status) { nano_switch_fd_addr_t fd_addr; nano_bus_relase_msg_data(msg); msg->data = data; msg->type = NANO_BUS_MSG_RESPONSE; msg->status = status; fd_addr.local_addr = addr; if (nano_switch_send(fd, &fd_addr, (SH_CHAR*)msg, sizeof(*msg)) != 0) { nano_bus_relase_msg(msg); return -1; } return 0; } static void nano_busd_add_obj_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; nano_bus_object_t *obj; json_t *jname; SH_INT32 status = -1; if (!msg || !msg->data) { goto out; } jname = json_get_object_item(msg->data, "name"); if (!jname) { goto out; } list_for_each_entry(obj, &ctx->objs, list) { if (!OSAL_STRCMP(obj->name, jname->valuestring)) { goto out; } } obj = osal_malloc(sizeof(nano_bus_object_t)); if (!obj) { goto out; } OSAL_MEMZERO(obj, sizeof(nano_bus_object_t)); obj->name = osal_strdup(jname->valuestring); if (!obj->name) { osal_free(obj); goto out; } obj->addr = msg->src_addr; list_add(&obj->list, &ctx->objs); status = 0; out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, status); } } static void nano_busd_register_event_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; nano_busd_event_t *pevt, *pnevt = NULL; nano_busd_event_node_t *pevt_node, *pnevt_node; json_t *jevt; SH_INT32 i, num, status = -1; SH_BOOL found; if (!msg || !msg->data) { goto out; } list_for_each_entry(pevt, &ctx->evts, list) { if (pevt->addr == msg->src_addr) { pnevt = pevt; break; } } if (!pnevt) { pnevt = osal_malloc(sizeof(nano_busd_event_t)); if (!pnevt) { goto out; } OSAL_MEMZERO(pnevt, sizeof(nano_busd_event_t)); INIT_LIST_HEAD(&pnevt->evts); pnevt->addr = msg->src_addr; list_add(&pnevt->list, &ctx->evts); } num = json_get_array_size(msg->data); for (i = 0; i < num; i++) { jevt = json_get_array_item(msg->data, i); found = FALSE; list_for_each_entry(pevt_node, &pnevt->evts, list) { if (!OSAL_STRCMP(pevt_node->name, jevt->valuestring)) { pevt_node->ref_count++; found = TRUE; break; } } if (!found) { pnevt_node = osal_malloc(sizeof(nano_busd_event_node_t)); if (pnevt_node) { OSAL_MEMZERO(pnevt_node, sizeof(nano_busd_event_node_t)); pnevt_node->name = osal_strdup(jevt->valuestring); pnevt_node->ref_count = 1; list_add(&pnevt_node->list, &pnevt->evts); } } } status = 0; out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, status); } } static void nano_busd_run_callback_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; SH_INT32 status = -1; if (!msg || !msg->cb) { goto out; } msg->cb(msg->cb_priv); status = 0; out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, status); } } #ifdef SHIP_LIB_NANO_BUS_DELETE_API static void nano_busd_delete_obj_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; nano_bus_object_t *obj; json_t *jname; SH_INT32 status = -1; if (!msg || !msg->data) { goto out; } jname = json_get_object_item(msg->data, "name"); if (!jname) { goto out; } status = 0; list_for_each_entry(obj, &ctx->objs, list) { if (!OSAL_STRCMP(obj->name, jname->valuestring)) { if (obj->addr == msg->src_addr) { list_del(&obj->list); osal_free(obj->name); osal_free(obj); } else { status = -1; } break; } } out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, status); } } static void nano_busd_unregister_event_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; nano_busd_event_t *pevt, *pnevt = NULL; nano_busd_event_node_t *pevt_node; json_t *jevt; SH_INT32 i, num, status = -1; if (!msg || !msg->data) { goto out; } list_for_each_entry(pevt, &ctx->evts, list) { if (pevt->addr == msg->src_addr) { pnevt = pevt; break; } } if (!pnevt) { status = 0; goto out; } num = json_get_array_size(msg->data); for (i = 0; i < num; i++) { jevt = json_get_array_item(msg->data, i); list_for_each_entry(pevt_node, &pnevt->evts, list) { if (!OSAL_STRCMP(pevt_node->name, jevt->valuestring)) { pevt_node->ref_count--; if (pevt_node->ref_count == 0) { list_del(&pevt_node->list); osal_free(pevt_node->name); osal_free(pevt_node); } break; } } } status = 0; out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, status); } } #endif #ifdef SHIP_LIB_NANO_BUS_CLI_API static void nano_busd_list_cb(nano_bus_msg_t *msg, void *context) { nano_busd_context_t *ctx = (nano_busd_context_t*)context; nano_bus_object_t *obj; json_t *jdata = NULL; SH_INT32 status = -1; if (!msg) { goto out; } jdata = json_create_array(); if (!jdata) { goto out; } list_for_each_entry(obj, &ctx->objs, list) { json_add_item_toarray(jdata, json_create_string(obj->name)); } status = 0; out: if (msg) { _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, jdata, status); } } #endif static const nano_bus_method_t nano_busd_methods[] = { {.name = "add_obj", .cb = nano_busd_add_obj_cb}, {.name = "register_event", .cb = nano_busd_register_event_cb}, {.name = "run_callback", .cb = nano_busd_run_callback_cb}, #ifdef SHIP_LIB_NANO_BUS_DELETE_API {.name = "delete_obj", .cb = nano_busd_delete_obj_cb}, {.name = "unregister_event", .cb = nano_busd_unregister_event_cb}, #endif #ifdef SHIP_LIB_NANO_BUS_CLI_API {.name = "list", .cb = nano_busd_list_cb}, #endif }; static nano_bus_object_t nano_busd_obj = { .name = "nano_bus", .methods = nano_busd_methods, .n_methods = ARRAY_SIZE(nano_busd_methods), }; static void nano_busd_process_request(nano_busd_context_t *ctx, nano_bus_msg_t *msg) { nano_bus_object_t *obj; SH_INT32 i; SH_BOOL processed = 0; nano_switch_fd_addr_t addr; list_for_each_entry(obj, &ctx->objs, list) { if (OSAL_STRCMP(obj->name, msg->obj)) { continue; } if (obj->n_methods > 0) { /* process locally */ for (i = 0; i < obj->n_methods; i++) { if (OSAL_STRCMP(obj->methods[i].name, msg->method)) { continue; } processed = 1; if (obj->methods[i].cb) { obj->methods[i].cb(msg, (void*)ctx); } else { nano_bus_relase_msg(msg); } break; } } else { processed = 1; /* forward to client */ msg->dst_addr = obj->addr; addr.local_addr = obj->addr; if (nano_switch_send(ctx->sock.fd, &addr, (SH_CHAR*)msg, sizeof(*msg)) != 0) { nano_bus_relase_msg(msg); } } break; } if (!processed) { SHIP_D_PRINTF("obj %s, method %s not found\n", msg->obj ? msg->obj : "NULL", msg->method ? msg->method : "NULL"); /* send an error reply */ _nano_bus_send_reply(ctx->sock.fd, msg->src_addr, msg, NULL, OSAL_ERROR); } } static void nano_busd_process_response(nano_busd_context_t *ctx, nano_bus_msg_t *msg) { nano_switch_fd_addr_t addr; addr.local_addr = msg->dst_addr; /* forward to client */ if (nano_switch_send(ctx->sock.fd, &addr, (SH_CHAR*)msg, sizeof(*msg)) != 0) nano_bus_relase_msg(msg); } static void nano_busd_process_event(nano_busd_context_t *ctx, nano_bus_msg_t *msg) { nano_busd_event_t *evt; nano_busd_event_node_t *evt_node; nano_bus_msg_t *msg2; nano_switch_fd_addr_t addr; if (msg->evt) { list_for_each_entry(evt, &ctx->evts, list) { list_for_each_entry(evt_node, &evt->evts, list) { if (!OSAL_STRCMP(evt_node->name, msg->evt)) { msg2 = osal_malloc(sizeof(*msg2)); if (msg2) { OSAL_MEMZERO(msg2, sizeof(*msg2)); msg2->type = msg->type; msg2->evt = osal_strdup(msg->evt); msg2->data = json_duplicate(msg->data, 1); addr.local_addr = evt->addr; if (nano_switch_send(ctx->sock.fd, &addr, (SH_CHAR*)msg2, sizeof(*msg2)) != 0) nano_bus_relase_msg(msg2); } break; } } } } nano_bus_relase_msg(msg); } static void nano_busd_process_msg(nano_busd_context_t *ctx, nano_bus_msg_t *msg) { if (!msg) return; switch (msg->type) { case NANO_BUS_MSG_REQUEST: NANO_BUS_LOG("request id=%d, src=%d, dst=%d, obj=%s, method=%s, data=%p\n", msg->id, msg->src_addr, msg->dst_addr, msg->obj, msg->method, msg->data); nano_busd_process_request(ctx, msg); break; case NANO_BUS_MSG_RESPONSE: NANO_BUS_LOG("response id=%d, src=%d, dst=%d, obj=%s, method=%s, status=%d, data=%p\n", msg->id, msg->src_addr, msg->dst_addr, msg->obj, msg->method, msg->status, msg->data); nano_busd_process_response(ctx, msg); break; case NANO_BUS_MSG_EVENT: NANO_BUS_LOG("event id=%d, src=%d, dst=%d, event=%s, data=%p\n", msg->id, msg->src_addr, msg->dst_addr, msg->evt, msg->data); nano_busd_process_event(ctx, msg); break; default: SHIP_D_PRINTF("nano_busd:unknown msg type %d\n", msg->type); nano_bus_relase_msg(msg); } } static void nano_busd_fd_handler(struct nano_loop_fd *u, SH_UINT32 events) { nano_busd_context_t *ctx = container_of(u, nano_busd_context_t, sock); nano_bus_msg_t *msg = NULL; nano_switch_fd_addr_t remote_addr; if (nano_switch_receive(ctx->sock.fd, &remote_addr, (SH_CHAR **)&msg, NULL) == 0) { if (msg) msg->src_addr = remote_addr.local_addr; nano_busd_process_msg(ctx, msg); } } static SH_BOOL daemon_started = FALSE; static struct nano_loop_context *main_loop_ctx; static nano_bus_context_t *main_bus_ctx; static nano_busd_context_t *busd_ctx; static void nano_busd_entry(void *arg) { nano_switch_fd_addr_t addr; busd_ctx = osal_malloc(sizeof(nano_busd_context_t)); if (!busd_ctx) { SHIP_W_PRINTF("ctx null\n"); goto out; } OSAL_MEMSET(busd_ctx, 0, sizeof(nano_busd_context_t)); main_loop_ctx = nano_loop_new(); if (!main_loop_ctx) { SHIP_W_PRINTF("loop null\n"); goto out; } busd_ctx->loop_ctx = main_loop_ctx; busd_ctx->sock.cb = nano_busd_fd_handler; OSAL_MEMZERO(&addr, sizeof(nano_switch_fd_addr_t)); addr.local_addr = NANO_BUS_DAEMON_ADDR; busd_ctx->sock.fd = nano_switch_endpoint_open(NANO_SWITCH_EP_TYPE_LOCAL, &addr, NULL); if (!busd_ctx->sock.fd) { SHIP_W_PRINTF("fd fail\n"); goto out; } INIT_LIST_HEAD(&busd_ctx->objs); INIT_LIST_HEAD(&busd_ctx->evts); list_add(&nano_busd_obj.list, &busd_ctx->objs); nano_loop_fd_add(main_loop_ctx, &busd_ctx->sock); main_bus_ctx = nano_bus_context_create(); nano_bus_add_loop(main_loop_ctx, main_bus_ctx); daemon_started = TRUE; nano_loop_run(main_loop_ctx); out: nano_loop_done(main_loop_ctx); if (busd_ctx) osal_free(busd_ctx); return; } nano_bus_context_t* nano_bus_main_ctx_get() { return main_bus_ctx; } struct nano_loop_context* nano_bus_main_loop_get() { return main_loop_ctx; } SH_INT32 nano_bus_init() { osal_thread_t id; osal_thread_create(&id, "ml", nano_busd_entry, (SH_ULONG)NULL, CONFIG_LIB_MAIN_LOOP_STACK_SIZE, OSAL_THREAD_PRIORITY_HIGH, 0); while (1) { if (!daemon_started) osal_sleep_ms(2); else break; } return OSAL_OK; } static void nano_bus_process_request(nano_bus_context_t *ctx, nano_bus_msg_t *msg) { nano_bus_object_t *obj; SH_INT32 i; SH_BOOL processed = FALSE; list_for_each_entry(obj, &ctx->objs, list) { if (OSAL_STRCMP(obj->name, msg->obj)) { continue; } for (i = 0; i < obj->n_methods; i++) { /* globbing should be the last in registered methods, otherwise the method after it will never be called */ if (OSAL_STRCMP(obj->methods[i].name, msg->method) && OSAL_STRCMP(obj->methods[i].name, "*")) { continue; } processed = TRUE; if (obj->methods[i].cb) { obj->methods[i].cb(msg, (void*)ctx); } else { nano_bus_relase_msg(msg); } break; } break; } if (!processed) { SHIP_D_PRINTF("obj %s, method %s not found\n", msg->obj ? msg->obj : "NULL", msg->method ? msg->method : "NULL"); /* send an error reply */ nano_bus_send_reply(ctx, msg, NULL, OSAL_ERROR); } } static void nano_bus_process_response(nano_bus_context_t *ctx, nano_bus_msg_t *msg) { SH_BOOL found = FALSE; nano_bus_request_t *req, *n; list_for_each_entry_safe(req, n, &ctx->reqs, list) { if (req->id == msg->id) { found = TRUE; req->done = TRUE; list_del(&req->list); if (req->cb) { req->cb(msg->status, msg->data, req->priv); } nano_bus_relase_msg(msg); if (req->async) osal_free(req); break; } } if (!found) nano_bus_relase_msg(msg); } static void nano_bus_process_event(nano_bus_context_t *ctx, nano_bus_msg_t *msg) { nano_bus_event_t *evt; if (msg->evt) { list_for_each_entry(evt, &ctx->evts, list) { /* find all matching event, there may be multipe call back with the same name */ if (!OSAL_STRCMP(evt->name, msg->evt)) { if (evt->cb) evt->cb(msg, (void*)ctx); } } } nano_bus_relase_msg(msg); } static void nano_bus_process_msg(nano_bus_context_t *ctx, nano_bus_msg_t *msg) { if (!msg) return; switch (msg->type) { case NANO_BUS_MSG_REQUEST: NANO_BUS_LOG("request id=%d, src=%d, dst=%d, obj=%s, method=%s, data=%p\r\n", msg->id, msg->src_addr, msg->dst_addr, msg->obj, msg->method, msg->data); nano_bus_process_request(ctx, msg); break; case NANO_BUS_MSG_RESPONSE: NANO_BUS_LOG("response id=%d, src=%d, dst=%d, obj=%s, method=%s, status=%d, data=%p\r\n", msg->id, msg->src_addr, msg->dst_addr, msg->obj, msg->method, msg->status, msg->data); nano_bus_process_response(ctx, msg); break; case NANO_BUS_MSG_EVENT: NANO_BUS_LOG("event id=%d, src=%d, dst=%d, event=%s, data=%p\n", msg->id, msg->src_addr, msg->dst_addr, msg->evt, msg->data); nano_bus_process_event(ctx, msg); break; default: SHIP_D_PRINTF("nano_bus:unknown msg type %d\n", msg->type); nano_bus_relase_msg(msg); } } /* do not release data when error happen */ static SH_INT32 nano_bus_start_request(nano_bus_context_t *ctx, nano_bus_request_t *req, SH_CHAR *obj, SH_CHAR *method, json_t *data, nano_bus_main_loop_cb_t cb, void *cb_priv, SH_BOOL nofree) { nano_bus_msg_t *msg; SH_INT32 ret = -1; nano_switch_fd_addr_t addr; msg = osal_malloc(sizeof(nano_bus_msg_t)); if (!msg) { goto out; } OSAL_MEMZERO(msg, sizeof(nano_bus_msg_t)); msg->type = NANO_BUS_MSG_REQUEST; msg->obj = osal_strdup(obj); msg->method = osal_strdup(method); if (!msg->obj || !msg->method) { nano_bus_relase_msg(msg); goto out; } msg->data = data; msg->cb = cb; msg->cb_priv = cb_priv; msg->no_free = nofree; msg->id = ctx->msgid++; if (msg->id < 0) { /* msgid seems overflow, start from 0 again */ msg->id = 0; ctx->msgid = 1; } req->id = msg->id; addr.local_addr = NANO_BUS_DAEMON_ADDR; if (nano_switch_send(ctx->sock.fd, &addr, (SH_CHAR*)msg, sizeof(*msg)) != 0) { msg->data = NULL; nano_bus_relase_msg(msg); goto out; } list_add(&req->list, &ctx->reqs); ret = 0; out: return ret; } static SH_INT32 nano_bus_complete_request(nano_bus_context_t *ctx, nano_bus_request_t *req, SH_INT32 tmo) { nano_bus_msg_t *msg; SH_UINT64 time_end = 0, timeout, tv; nano_switch_fd_t fds[2]; SH_INT32 len, ret, i; nano_switch_fd_addr_t remote_addr; if (tmo >= 0) { time_end = osal_time_ms() + tmo; } while (!req->done) { tv = osal_time_ms(); if (tmo >= 0) { if (time_end <= tv) { break; } else { timeout = time_end - tv; } } else { timeout = -1; } if (ctx->loop_ctx == main_loop_ctx) { /* if in mainloop we must also process the messages on busd sock */ len = 2; fds[0] = busd_ctx->sock.fd; fds[1] = ctx->sock.fd; } else { len = 1; fds[0] = ctx->sock.fd; } ret = nano_switch_select(fds, &len, timeout); if (ret != 0 || len < 1) { continue; } for (i = 0; i < len; i++) { msg = NULL; ret = nano_switch_receive(fds[i], &remote_addr, (SH_CHAR **)&msg, NULL); if (ret != 0) { continue; } if (fds[i] == ctx->sock.fd) { nano_bus_process_msg(ctx, msg); } else if (fds[i] == busd_ctx->sock.fd) { msg->src_addr = remote_addr.local_addr; nano_busd_process_msg(busd_ctx, msg); } else { nano_bus_relase_msg(msg); } } } if (!req->done) { list_del(&req->list); if (req->cb) req->cb(OSAL_ERR_TIMEOUT, NULL, req->priv); } return 0; } static SH_INT32 nano_bus_next_req_timeout(nano_bus_context_t *ctx) { nano_bus_request_t *req; SH_UINT64 tv, now = osal_time_ms(); if (list_empty(&ctx->reqs)) { return -1; } else { req = list_first_entry(&ctx->reqs, nano_bus_request_t, list); tv = req->time; } list_for_each_entry(req, &ctx->reqs, list) { if (tv > req->time) tv = req->time; } if (tv > now) return tv - now; else return 0; } static void nano_bus_fd_handler(struct nano_loop_fd *u, SH_UINT32 events) { nano_bus_context_t *ctx = container_of(u, nano_bus_context_t, sock); nano_bus_msg_t *msg; if (nano_switch_receive(ctx->sock.fd, NULL, (SH_CHAR **)&msg, NULL) == 0) { nano_bus_process_msg(ctx, msg); } } static void nano_bus_timer_handler(struct nano_loop_timeout *t) { nano_bus_context_t *ctx = container_of(t, nano_bus_context_t, timer); nano_bus_request_t *req, *n; SH_UINT64 now = osal_time_ms(); list_for_each_entry_safe(req, n, &ctx->reqs, list) { if (req->time <= now) { if (req->cb) req->cb(OSAL_ERR_TIMEOUT, NULL, req->priv); list_del(&req->list); osal_free(req); } } } nano_bus_context_t* nano_bus_context_create() { nano_bus_context_t *ctx; nano_switch_fd_addr_t addr; ctx = osal_malloc(sizeof(nano_bus_context_t)); if (ctx) { OSAL_MEMZERO(ctx, sizeof(nano_bus_context_t)); INIT_LIST_HEAD(&ctx->objs); INIT_LIST_HEAD(&ctx->reqs); INIT_LIST_HEAD(&ctx->evts); ctx->timer.cb = nano_bus_timer_handler; OSAL_MEMZERO(&addr, sizeof(nano_switch_fd_addr_t)); addr.local_addr = 0; ctx->sock.fd = nano_switch_endpoint_open(NANO_SWITCH_EP_TYPE_LOCAL, &addr, NULL); ctx->sock.cb = nano_bus_fd_handler; if (!ctx->sock.fd) { osal_free(ctx); return NULL; } } return ctx; } #ifdef SHIP_LIB_NANO_BUS_DELETE_API void nano_bus_context_destroy(nano_bus_context_t *ctx) { nano_bus_object_t *obj, *nobj; nano_bus_event_t *evt, *nevt; nano_bus_request_t *req, *nreq; list_for_each_entry_safe(obj, nobj, &ctx->objs, list) { nano_bus_delete_obj(ctx, obj->name); list_del(&obj->list); } list_for_each_entry_safe(evt, nevt, &ctx->evts, list) { nano_bus_unregister_events(ctx, evt, 1); list_del(&evt->list); } list_for_each_entry_safe(req, nreq, &ctx->reqs, list) { req->done = TRUE; list_del(&req->list); if (req->cb) req->cb(OSAL_ERR_CANCEL, NULL, req->priv); if (req->async) osal_free(req); } nano_loop_fd_delete(&ctx->sock); nano_switch_endpoint_close(ctx->sock.fd); nano_loop_timeout_cancel(&ctx->timer); osal_free(ctx); } #endif void nano_bus_add_loop(struct nano_loop_context *loop_ctx, nano_bus_context_t *ctx) { if (!loop_ctx || !ctx) return; ctx->loop_ctx = loop_ctx; nano_loop_fd_add(loop_ctx, &ctx->sock); } static void nano_bus_call_common_cb(SH_INT32 status, json_t *resp, void *priv) { SH_INT32 ret = -1; if (status == OSAL_OK) ret = 0; if (priv) *(SH_INT32*)priv = ret; } SH_INT32 nano_bus_add_obj(nano_bus_context_t *ctx, nano_bus_object_t *obj) { nano_bus_request_t req; json_t *data; SH_INT32 ret = -1, status = -1; data = json_create_object(); if (!data) goto out; json_add_string_toObject(data, "name", obj->name); OSAL_MEMZERO(&req, sizeof(req)); req.cb = nano_bus_call_common_cb; req.priv = &status; if (nano_bus_start_request(ctx, &req, "nano_bus", "add_obj", data, NULL, NULL, FALSE) != 0) goto out; data = NULL; nano_bus_complete_request(ctx, &req, 20000); if (status == 0) { ret = 0; list_add(&obj->list, &ctx->objs); } out: if (data) json_delete(data); return ret; } #ifdef SHIP_LIB_NANO_BUS_DELETE_API SH_INT32 nano_bus_delete_obj(nano_bus_context_t *ctx, SH_CHAR *name) { nano_bus_request_t req; json_t *data; SH_INT32 ret = -1, status = -1; data = json_create_object(); if (!data) goto out; json_add_string_toObject(data, "name", name); OSAL_MEMZERO(&req, sizeof(req)); req.cb = nano_bus_call_common_cb; req.priv = &status; if (nano_bus_start_request(ctx, &req, "nano_bus", "delete_obj", data, NULL, NULL, FALSE) != 0) goto out; data = NULL; nano_bus_complete_request(ctx, &req, 20000); if (status == 0) { ret = 0; } out: if (data) json_delete(data); return ret; } #endif SH_INT32 nano_bus_invoke(nano_bus_context_t *ctx, SH_CHAR *obj, SH_CHAR *method, json_t *data, SH_BOOL no_free, nano_bus_request_cb_t cb, void *priv, SH_INT32 tmo, SH_BOOL sync) { nano_bus_request_t *req; SH_INT32 ret = OSAL_ERROR; req = osal_malloc(sizeof(nano_bus_request_t)); if (!req) goto out; OSAL_MEMZERO(req, sizeof(nano_bus_request_t)); req->cb = cb; req->priv = priv; if (nano_bus_start_request(ctx, req, obj, method, data, NULL, NULL, no_free) < 0) goto out; if (sync) { nano_bus_complete_request(ctx, req, tmo); osal_free(req); ret = 0; } else { req->time = osal_time_ms() + tmo; req->async = TRUE; nano_loop_timeout_set(ctx->loop_ctx, &ctx->timer, nano_bus_next_req_timeout(ctx)); ret = req->id; } out: return ret; } SH_INT32 nano_bus_send_reply(nano_bus_context_t *ctx, nano_bus_msg_t *msg, json_t *data, SH_INT32 status) { SH_UINT8 tmp; tmp = msg->src_addr; msg->src_addr = msg->dst_addr; msg->dst_addr = tmp; return _nano_bus_send_reply(ctx->sock.fd, NANO_BUS_DAEMON_ADDR, msg, data, status); } void nano_bus_cancel_request(nano_bus_context_t *ctx, SH_INT32 request_id) { nano_bus_request_t *req, *n; list_for_each_entry_safe(req, n, &ctx->reqs, list) { if (req->id == request_id) { req->done = TRUE; list_del(&req->list); if (req->cb) req->cb(OSAL_ERR_CANCEL, NULL, req->priv); if (req->async) osal_free(req); break; } } } SH_INT32 nano_bus_run_cb_in_main_loop(nano_bus_context_t *ctx, nano_bus_main_loop_cb_t cb, void *priv) { nano_bus_request_t req; SH_INT32 ret = -1, status = -1; OSAL_MEMZERO(&req, sizeof(req)); req.cb = nano_bus_call_common_cb; req.priv = &status; if (nano_bus_start_request(ctx, &req, "nano_bus", "run_callback", NULL, cb, priv, FALSE) != 0) { goto out; } nano_bus_complete_request(ctx, &req, 20000); if (status == 0) { ret = 0; } out: return ret; } SH_INT32 nano_bus_register_events(nano_bus_context_t *ctx, nano_bus_event_t *evts, SH_INT32 n_evts) { nano_bus_request_t req; json_t *data, *item; SH_INT32 ret = -1, status = -1, i; data = json_create_array(); if (!data) goto out; for (i = 0; i < n_evts; i++) { item = json_create_string(evts[i].name); if (!item) continue; json_add_item_toarray(data, item); } OSAL_MEMZERO(&req, sizeof(req)); req.cb = nano_bus_call_common_cb; req.priv = &status; if (nano_bus_start_request(ctx, &req, "nano_bus", "register_event", data, NULL, NULL, FALSE) != 0) goto out; data = NULL; nano_bus_complete_request(ctx, &req, 20000); if (status == 0) { ret = 0; for (i = 0; i < n_evts; i++) { list_add(&evts[i].list, &ctx->evts); } } out: if (data) json_delete(data); return ret; } #ifdef SHIP_LIB_NANO_BUS_DELETE_API SH_INT32 nano_bus_unregister_events(nano_bus_context_t *ctx, nano_bus_event_t *evts, SH_INT32 n_evts) { nano_bus_request_t req; json_t *data, *item; SH_INT32 ret = -1, status = -1, i; data = json_create_array(); if (!data) goto out; for (i = 0; i < n_evts; i++) { item = json_create_string(evts[i].name); if (!item) continue; json_add_item_toarray(data, item); } OSAL_MEMZERO(&req, sizeof(req)); req.cb = nano_bus_call_common_cb; req.priv = &status; if (nano_bus_start_request(ctx, &req, "nano_bus", "unregister_event", data, NULL, NULL, FALSE) != 0) goto out; data = NULL; nano_bus_complete_request(ctx, &req, 20000); if (status == 0) { ret = 0; } out: if (data) json_delete(data); return ret; } #endif SH_INT32 nano_bus_send_event(nano_bus_context_t *ctx, SH_CHAR *evt, json_t *data) { nano_bus_msg_t *msg; SH_INT32 ret = -1; nano_switch_fd_addr_t addr; msg = osal_malloc(sizeof(*msg)); if (!msg) goto out; memset(msg, 0, sizeof(*msg)); msg->type = NANO_BUS_MSG_EVENT; msg->evt = osal_strdup(evt); msg->data = data; addr.local_addr = NANO_BUS_DAEMON_ADDR; if (nano_switch_send(ctx->sock.fd, &addr, (SH_CHAR*)msg, sizeof(*msg)) != 0) { nano_bus_relase_msg(msg); return -1; } ret = 0; out: return ret; } 分析一下这个
最新发布
12-09
#include <string.h> #include <stdlib.h> #include "event2/buffer.h" #include "udp_punch.h" #include "udt_c.h" #include "utils.h" #include "session.h" #ifdef HAVE_VS #include "../include/video_share.h" #endif #define PUNCH_SYN_SENT 1 #define PUNCH_SYN_RCVD 2 #define PUNCH_SYNACK_RCVD 3 #define PUNCH_UDT_CONNECTED 4 #define PUNCH_OPT_ACK 0x00000001 #ifndef PUNCH_PREPARING_TTL #define PUNCH_PREPARING_TTL 5 #endif /* private IP range -- RFC1918 * 10.0.0.0/8 * 172.16.0.0/12 * 192.168.0.0/16 */ #define PRIVATE_NIPV4(__nip) (((__nip)&0x000000FF) == 0x0000000A || \ ((__nip)&0x0000F0FF) == 0x000010AC || \ ((__nip)&0x0000FFFF) == 0x0000A8C0) #define UDT_SNDBUF_SIZE (256*1024) #define UDT_RCVBUF_SIZE (16*1024) #define UDT_MAX_SEG_SIZE 1420 #ifndef UDT_SNDLOWAT #define UDT_SNDLOWAT 1 #endif typedef struct punch_inner_data_s { struct event* evstun; /* event for UDP */ struct sockaddr_in sin_peer; /* peer address */ int status; int punch_prepared; int punch_try_cnt; int fd_udt; unsigned int udt_sndlowat; struct evbuffer* evbuf_out; } punch_inner_data_t; static const char* _sockaddr_ntoa(const struct sockaddr_in* sin) { static char s[32]; snprintf(s, sizeof(s), "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); return s; } static int stun_udtsyn(void* data, int size) { /* TODO: udt v4 */ static const char* udt4_syn = "\x80\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x04"; if ( size >= 20 && !memcmp(data, udt4_syn, 20) ) { return 1; } return 0; } static int udt_get_state(int fd_udt) { int state = BROKEN, len; len = sizeof(state); if( udt_getsockopt(fd_udt, 0, UDT_STATE, &state, &len) ) { return BROKEN; } return state; } static int udt_get_canrecv(int fd_udt) { int canrecv = -1, len; len = sizeof(canrecv); if( udt_getsockopt(fd_udt, 0, UDT_RCVDATA, &canrecv, &len) ) { return -1; } return canrecv; } static int udt_get_cansend(int fd_udt) { int sendbufused, len; len = sizeof(sendbufused); if( udt_getsockopt(fd_udt, 0, UDT_SNDDATA, &sendbufused, &len) ) { return -1; } return UDT_SNDBUF_SIZE - sendbufused*UDT_MAX_SEG_SIZE; } static int build_udt_socket(int fd_udp, int rendezvous, int without_linger) { UDTSOCKET fd_udt; int opt, ret = 0; fd_udt = udt_socket(PF_INET, SOCK_STREAM, 0); if ( -1 == fd_udt ) { DBG_LOG(DBG_ERR, "udt_socket failed, errcode %d\n", udt_getlasterror_code()); return -1; } /* set socket options: RENDEZVOUS, MSS, NONBLOCK, SND/RCV BUFFER size. */ opt = 1; if ( rendezvous ) { ret = udt_setsockopt(fd_udt, 0, UDT_RENDEZVOUS, &opt, sizeof(opt)); } if ( without_linger ) { struct linger lg; lg.l_onoff = 0; lg.l_linger = 0; DBG_LOG(DBG_ERR, "udt socket without linger\n"); ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_LINGER, &lg, sizeof(lg)); } opt = UDT_MAX_SEG_SIZE; /* strips headers (PPPOE, PPP, ...) off */ ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_MSS, &opt, sizeof(opt)); opt = 0; ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_RCVSYN, &opt, sizeof(opt)); if ( ret != 0 ) { DBG_LOG(DBG_ERR, "udt_setsockopt failed, errcode %d\n", udt_getlasterror_code()); udt_close(fd_udt); return -1; } /* set socket buffer size */ opt = UDT_SNDBUF_SIZE; udt_setsockopt(fd_udt, 0, UDT_SNDBUF, &opt, sizeof(opt)); opt >>= 1; udt_setsockopt(fd_udt, 0, UDP_SNDBUF, &opt, sizeof(opt)); opt = UDT_RCVBUF_SIZE; udt_setsockopt(fd_udt, 0, UDT_RCVBUF, &opt, sizeof(opt)); opt >>= 1; udt_setsockopt(fd_udt, 0, UDP_RCVBUF, &opt, sizeof(opt)); /* bind to local udp address */ if ( fd_udp != -1 ) { /* FIXME: non-block fd(UDP) makes udt_bind2 crazy!!! */ evutil_make_socket_blocking(fd_udp); if ( udt_bind2(fd_udt, fd_udp) != 0 ) { DBG_LOG(DBG_ERR, "udt_bind2 failed, errcode %d\n", udt_getlasterror_code()); udt_close(fd_udt); return -1; } } return fd_udt; } static int udt_do_send_stream(punch_inner_data_t* inner_ctx) { int ret, sent = 0; int size; char buf[4096] = {0}; do { memset(buf, 0, sizeof(buf)); size = evbuffer_copyout(inner_ctx->evbuf_out, buf, sizeof(buf)); if ( size <= 0 ) { break; } ret = udt_send(inner_ctx->fd_udt, buf, size, 0); if ( -1 == ret ) { if (udt_getlasterror_code() == EASYNCSND ) { return 0; } else { DBG_LOG(DBG_ERR, "udt_send failed, errcode %d\n", udt_getlasterror_code()); return -1; } } evbuffer_drain(inner_ctx->evbuf_out, ret); sent += ret; /* send not complete, or buffer empty */ if ( ret < size || size < sizeof(buf) ) { break; } } while (1); return sent; } #ifdef HAVE_VS #define HTTP_RESPONSE_503_6 \ "HTTP/1.0 503.6 Service Unavailable\r\n"\ "Server: p2pd\r\n"\ "Content-Length: 0\r\n"\ "Content-Type: text/html\r\n"\ "Connection: close\r\n"\ "\r\n" #endif static int read_local_data(punch_ctx_t *ctx) { punch_inner_data_t* inner_ctx = ctx->inner_data; char buf[8192]; int buf_size = sizeof(buf); int ret = 0; int avail_buf_size = 0; int iter_size = 0; int recv_size_from_local = 0; int send_size = 0; if (!ctx->test_local_server(ctx->env)) { return 0; } while (1) { avail_buf_size = udt_get_cansend(inner_ctx->fd_udt); if (avail_buf_size <= 0) { break; } iter_size = (avail_buf_size > buf_size)?buf_size:avail_buf_size; recv_size_from_local = ctx->read_from_local_server(ctx->env, (void *)buf, iter_size); if (recv_size_from_local < 0) { DBG_LOG(DBG_ERR, "recv data from local server failed.\n"); return -1; } if (recv_size_from_local == 0) { break; } ret = udt_send(inner_ctx->fd_udt, buf, recv_size_from_local, 0); if (ret < 0) { return -1; } } return 0; } #ifdef ADD_REMOTE_ADDR static int add_header_to_evbuf(session_t *sess, char *buf, int len) { if (sess->evbuf_in == NULL) { sess->evbuf_in = evbuffer_new(); if (NULL == sess->evbuf_in) { return -1; } } if (HTTP_HEADER_MAX_LEN <= evbuffer_get_length(sess->evbuf_in)) { DBG_LOG(DBG_ERR, "http header is too long.\n"); return -1; } evbuffer_add(sess->evbuf_in, buf, len); return 0; } static int evbuf_contain_complete_header(struct evbuffer *evbuf) { char *header_end = NULL; int buf_len = 0; if (NULL == evbuf) { /* false */ return FALSE; } header_end = evbuffer_find(evbuf, "\r\n\r\n", 4); if (NULL == header_end) { DBG_LOG(DBG_ERR, "http header is too long\n"); return FALSE; } return TRUE; } static int send_new_header(session_t *sess) { int ret = 0; char *header_end = NULL; int header_len = 0; char buf[HTTP_HEADER_MAX_LEN + 1] = {0}; punch_ctx_t *ctx = &sess->punch_ctx; ret = evbuffer_copyout(sess->evbuf_in, buf, sizeof(buf) - 1); if (ret <= 0) { return -1; } header_end = strstr(buf, "\r\n\r\n"); if (NULL == header_end) { return -1; } /* add \r\n */ header_end += 2; header_len = header_end - buf; int left_len = sizeof(buf) - header_len; /* �ⲿIP����ȷ���Զ˵ĵ�����ַ */ snprintf(header_end, left_len, "X-Client-Addr: %s\r\n\r\n", sess->prepare_req.eip); if ( ctx->write_to_local_server(ctx->env, buf, strlen(buf)) < 0 ) { return -1; } /* there is a "\r\n" left */ header_len += 2; evbuffer_drain(sess->evbuf_in, header_len); while (0 != evbuffer_get_length(sess->evbuf_in)) { ret = evbuffer_remove(sess->evbuf_in, buf, sizeof(buf) - 1); if (ret <= 0) { return -1; } if (ctx->write_to_local_server(ctx->env, buf, ret) < 0) { return -1; } } evbuffer_free(sess->evbuf_in); sess->evbuf_in = NULL; sess->header_recv = 1; return 0; } #endif static int read_peer_data(punch_ctx_t *ctx) { int ret = 0; punch_inner_data_t* inner_ctx = ctx->inner_data; char buf[2048]; int buf_size = sizeof(buf); session_t* sess = ctx->env; #ifdef HAVE_VS char sndbuf[256 + 1] = {0}; int size = 0; time_t t; #endif #ifdef ADD_REMOTE_ADDR char *new_header = NULL; #endif if(udt_get_canrecv(inner_ctx->fd_udt) <= 0 ) { return 0; } ret = udt_recv(inner_ctx->fd_udt, buf, sizeof(buf), 0); if ( -1 == ret && udt_getlasterror_code() == EASYNCRCV ) { return 0; } if ( ret <= 0 ) { return -1; } #ifdef HAVE_VS /* need to check permission when it is video share */ if(sess->prepare_req.is_video_share) { DBG_LOG(DBG_DBG, "it is video share, need to check permission\n"); t = time(NULL); if(strstr(buf, "/stream") != NULL) { DBG_LOG(DBG_DBG, "this request is POST /stream, now check permission\n"); if(0 == vs_share_rules_time_valid(&t, &sess->share_info->rules)) { DBG_LOG(DBG_ERR, "permission denied, send 503.6 to peer\n"); size = snprintf(sndbuf, 200, HTTP_RESPONSE_503_6); udt_send(inner_ctx->fd_udt, sndbuf, size, 0); return 0; } } else { DBG_LOG(DBG_DBG, "request type is not GET\n"); } DBG_LOG(DBG_DBG, "permission check passed\n"); } #endif #ifdef ADD_REMOTE_ADDR //printf("buf: %s\n", buf); if (!sess->header_recv) { if (0 > add_header_to_evbuf(sess, buf, ret)) { DBG_LOG(DBG_ERR, "failed to add header to evbuf\n"); return -1; } if (!evbuf_contain_complete_header(sess->evbuf_in)) { return 0; } if (0 > send_new_header(sess)) { DBG_LOG(DBG_ERR, "failed to send new header\n"); return -1; } return 0; } #endif if ( ctx->write_to_local_server(ctx->env, buf, ret) < 0 ) { return -1; } return 0; } static void udt_event_cb(evutil_socket_t fd, short events, void* arg) { int ret; punch_ctx_t* ctx = arg; punch_inner_data_t* inner_ctx = ctx->inner_data; int udt_stat = NONEXIST; udt_stat = udt_get_state(inner_ctx->fd_udt); if (udt_stat != CONNECTED) { if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) ) { return ; } else { DBG_LOG(DBG_ERR, "udt io-error, errcode %d\n", udt_getlasterror_code()); goto FAILED; } } else { if (!(inner_ctx->status & PUNCH_UDT_CONNECTED)) { DBG_LOG(DBG_DBG, "udt_connect OK, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); inner_ctx->status = PUNCH_UDT_CONNECTED; if ( ctx->succeed_cb(ctx->env, &inner_ctx->sin_peer) < 0 ) { DBG_LOG(DBG_ERR, "udt succeed_cb failed.\n"); goto FAILED; } } } ret = read_peer_data(ctx); if (ret < 0) { DBG_LOG(DBG_ERR, "read_peer_data failed.\n"); goto FAILED; } ret = read_local_data(ctx); if (ret < 0) { DBG_LOG(DBG_ERR, "read_local_data failed.\n"); goto FAILED; } return ; FAILED: ctx->error_cb(ctx->env); } static int udt_async_connect(punch_ctx_t* ctx, int rendezvous, int without_linger) { punch_inner_data_t* inner_ctx = ctx->inner_data; struct timeval tv = {0, 100*1000}; event_del(inner_ctx->evstun); event_assign(inner_ctx->evstun, ctx->base, -1, EV_TIMEOUT|EV_PERSIST, udt_event_cb, ctx); if ( event_add(inner_ctx->evstun, &tv) < 0 ) { return -1; } /* rendezvous */ inner_ctx->fd_udt = build_udt_socket(ctx->fd, rendezvous, without_linger); if ( inner_ctx->fd_udt < 0 ) { return -1; } DBG_LOG(DBG_DBG, "udt connect to %s...\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); return udt_connect(inner_ctx->fd_udt, (void*)&inner_ctx->sin_peer, sizeof(inner_ctx->sin_peer)); } static int stun_send_syn(punch_ctx_t* ctx, int opt) { punch_inner_data_t* inner_ctx = ctx->inner_data; struct sockaddr_in sin, sin2; char buf[512]; int ret = 0, len, i; snprintf(buf, sizeof(buf), "%s", ctx->stun_sid); if ( (opt & PUNCH_OPT_ACK) ) { buf[0]++; } len = (int)strlen(buf) + 1; sin = (opt & PUNCH_OPT_ACK) ? inner_ctx->sin_peer : ctx->sin_peer_wan; DBG_LOG(DBG_DBG, "punch buffer is %s\n", buf); // port = ntohs(sin.sin_port); //TODO: port prediction //PRINTF("sending %s...\n", (opt & PUNCH_OPT_ACK) ? "SYN/ACK" : "SYN" ); if ( !(opt & PUNCH_OPT_ACK) && ctx->sin_peer_lan.sin_addr.s_addr != ctx->sin_peer_wan.sin_addr.s_addr ) { /* LAN punching... */ ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&ctx->sin_peer_lan, sizeof(struct sockaddr_in)); } /* WAN punching... */ if(!inner_ctx->punch_prepared) { /* the first SYN has low TTL, * this low-TTL SYN is used to punch holes on the NATs of the PC's/IPC's own side. * * next we will send normal(whose TTL has not been set) SYNs, * and these normal SYNs will be sent every 1 second. * why 1 second, see the function udp_punch. */ int original_ttl, new_ttl, ttl_len; new_ttl = PUNCH_PREPARING_TTL; ttl_len = sizeof(original_ttl); if(getsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, &ttl_len) || setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &new_ttl, sizeof(new_ttl)) ) { inner_ctx->punch_prepared = 1; return 0; } /* send multiple times, avoid packet loss */ for(i = 0; i < 3; ++i) { sendto(ctx->fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)); } setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, ttl_len); inner_ctx->punch_prepared = 1; return 0; } ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)); if ( ret < 0 ) { if ( !sock_func_will_block(ret) ) { DBG_LOG(DBG_ERR, "sendto failed, fd:%d, sock_errno:%d\n", ctx->fd, sock_errno); goto FAILED; } return 0; } inner_ctx->punch_try_cnt ++; if(!(opt & PUNCH_OPT_ACK) && inner_ctx->punch_try_cnt > 3) { /* if we have sent the low-TTL SYN, as well as 3 normal SYNs(this takes 4 seconds approximately), * but hole punching still not done, we start to do port prediction. * * NOTE: port prediction strategy will be changed if we have better one. */ sin2 = sin; for(i = 1; i < 5; i++) { sin2.sin_port = htons(ntohs(sin.sin_port) + i); sendto(ctx->fd, buf, len, 0, (struct sockaddr *)&sin2, sizeof(struct sockaddr_in)); } } return 0; FAILED: return -1; } static void punch_event_cb(evutil_socket_t fd, short events, void* arg) { punch_ctx_t* ctx = arg; punch_inner_data_t* inner_ctx = ctx->inner_data; int opt_ack = 0; if (events & EV_TIMEOUT) { opt_ack = (PUNCH_SYN_SENT != inner_ctx->status) ? PUNCH_OPT_ACK : 0; stun_send_syn(ctx, opt_ack); return ; } if (events & EV_READ) { char buf[512] = {0}; struct sockaddr_in sin; socklen_t socklen = sizeof(sin); int size; size = recvfrom(fd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&sin, &socklen); if ( size < 0 ) { /* for udp socket, WSAENETRESET means TTL expired. * this is an ICMP error, called asynchronous error, * and only winSocket will tell udp socket this error, * others like bsd/linux will not. */ DBG_LOG(DBG_ERR, "recvfrom failed, fd:%d, sock_errno:%d\n", fd, sock_errno); if ( !sock_func_will_block(size) ) { goto FAILED; } return; } /* SYN received */ int syn_recv = !strcmp(buf, ctx->stun_sid); if (syn_recv) { if ( PRIVATE_NIPV4(sin.sin_addr.s_addr) || /* LAN */ PUNCH_SYN_SENT == inner_ctx->status ) { /* ����ΪLAN�� */ inner_ctx->sin_peer = sin; inner_ctx->status = PUNCH_SYN_RCVD; } stun_send_syn(ctx, PUNCH_OPT_ACK); DBG_LOG(DBG_DBG, "recvd SYN from %s\n", _sockaddr_ntoa(&sin)); return; } else { buf[0]--; int ack_recv = !strcmp(buf, ctx->stun_sid); if (ack_recv) { DBG_LOG(DBG_DBG, "recvd SYN, ACK from %s\n", _sockaddr_ntoa(&sin)); } else { DBG_LOG(DBG_DBG, "recvd trash data(%d bytes) from %s\n", size, _sockaddr_ntoa(&sin)); return; } } if ( PUNCH_SYN_SENT == inner_ctx->status ) { /* û���յ��Է���SYN����ֱ���յ�SYN+ACK */ /* ����Գ���+IP���� */ inner_ctx->sin_peer = sin; stun_send_syn(ctx, PUNCH_OPT_ACK); } else if (PUNCH_SYN_RCVD == inner_ctx->status) { /* prefer local to remote/harpin connections */ int is_local_connection = PRIVATE_NIPV4(inner_ctx->sin_peer.sin_addr.s_addr); int synack_from_remote_ip = sin.sin_addr.s_addr != inner_ctx->sin_peer.sin_addr.s_addr; if (is_local_connection && synack_from_remote_ip) { return; } /* received SYN, SYN/ACK from different remote address */ int different_addr = (inner_ctx->sin_peer.sin_addr.s_addr != sin.sin_addr.s_addr) || (inner_ctx->sin_peer.sin_port != sin.sin_port); if (different_addr) { inner_ctx->sin_peer = sin; } } else { DBG_LOG(DBG_ERR, "punching status error: %d\n", inner_ctx->status); return; } /* punching succeed */ DBG_LOG(DBG_DBG, "UDP hole punching succeed, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); stun_send_syn(ctx, PUNCH_OPT_ACK); inner_ctx->status = PUNCH_SYNACK_RCVD; int ret = udt_async_connect(ctx, 1, 0); if ( ret < 0 ) { goto FAILED; } return; } return; FAILED: DBG_LOG(DBG_ERR, "punching failed\n"); ctx->error_cb(ctx->env); } int udp_punch_send_stream(punch_ctx_t* ctx, const void* data, int size) { punch_inner_data_t* inner_ctx; int sent = 0; if (!ctx || !ctx->inner_data || !data || size < 0) { return -1; } inner_ctx = ctx->inner_data; if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) ) { return 0; } if ( !size ) { return (int)evbuffer_get_length(inner_ctx->evbuf_out); } /* data enqueue in evbuffer, and sent when timer expires */ if ( evbuffer_add(inner_ctx->evbuf_out, (char*)data+sent, size-sent) < 0 ) { return -1; } return (int)evbuffer_get_length(inner_ctx->evbuf_out); } int udp_punch(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; struct timeval tv = {1,1}; if (!ctx || !ctx->inner_data ) { return -1; } inner_ctx = ctx->inner_data; inner_ctx->evstun = event_new(ctx->base, ctx->fd, EV_READ|EV_TIMEOUT|EV_PERSIST, punch_event_cb, ctx); if (!inner_ctx->evstun) { DBG_LOG(DBG_ERR, "create evstun event, error\n"); return -1; } event_add(inner_ctx->evstun, &tv); int opt_ack = 0; stun_send_syn(ctx, opt_ack); stun_send_syn(ctx, opt_ack); inner_ctx->status = PUNCH_SYN_SENT; return 0; } int udp_punch_clear_internal(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; if ( !ctx || !ctx->inner_data ) { return -1; } inner_ctx = ctx->inner_data; if ( inner_ctx->evstun ) { event_free(inner_ctx->evstun); } if ( inner_ctx->fd_udt != -1 ) { /* since UDP fd has been binded to UDT fd, we must avoid double closeing UDP fd */ udt_close(inner_ctx->fd_udt); } else if (ctx->fd != -1) { evutil_closesocket(ctx->fd); } if ( inner_ctx->evbuf_out ) { evbuffer_free(inner_ctx->evbuf_out); } free(ctx->inner_data); ctx->inner_data = NULL; return 0; } int udp_punch_init_internal(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; if (!ctx || !ctx->base || ctx->fd < 0 ) { return -1; } udp_punch_clear_internal(ctx); ctx->inner_data = calloc(1, sizeof(punch_inner_data_t)); if (!ctx->inner_data) { return -1; } inner_ctx = ctx->inner_data; inner_ctx->fd_udt = -1; inner_ctx->udt_sndlowat = UDT_SNDLOWAT; inner_ctx->evstun = NULL; inner_ctx->evbuf_out = evbuffer_new(); if ( !inner_ctx->evbuf_out ) { DBG_LOG(DBG_ERR, "create evbuf_out, error\n"); udp_punch_clear_internal(ctx); return -1; } return 0; } int udp_punch_set_attr(punch_ctx_t* ctx, int attr, void* value) { punch_inner_data_t* inner_ctx; if( !ctx || !ctx->inner_data || !value ) { return -1; } inner_ctx = ctx->inner_data; switch(attr) { case PUNCHING_ATTR_UDT_SNDLOWAT: { unsigned int udt_sndlowat; udt_sndlowat = *(unsigned int*)value; if( udt_sndlowat <= 0 ) { return -1; } DBG_LOG(DBG_DBG, "udt_sndlowat changed: %u -> %u\n", inner_ctx->udt_sndlowat, udt_sndlowat); inner_ctx->udt_sndlowat = udt_sndlowat; return 0; }/* case of PUNCHING_ATTR_UDT_SNDLOWAT */ default: return -1; } return -1; }
11-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值