📄 stun.c
字号:
return NULL; } stun->sh_pri_info.ai_addrlen = 16; stun->sh_pri_info.ai_addr = &stun->sh_pri_addr->su_sa; stun->sh_sec_info.ai_addrlen = 16; stun->sh_sec_info.ai_addr = &stun->sh_sec_addr->su_sa; stun->sh_localinfo.li_addrlen = 16; stun->sh_localinfo.li_addr = stun->sh_local_addr; stun->sh_domain = su_strdup(stun->sh_home, domain); stun->sh_dns_lookup = NULL; if (server) { err = stun_atoaddr(stun->sh_home, AF_INET, &stun->sh_pri_info, server); if (err < 0) { errno = ENOENT; return NULL; } } stun->sh_nattype = stun_nat_unknown; stun->sh_root = root; /* always try TLS: */ stun->sh_use_msgint = 1; /* whether use of shared-secret msgint is required */ stun->sh_req_msgint = req_msg_integrity; stun->sh_max_retries = STUN_MAX_RETRX; /* initialize username and password */ stun_init_buffer(&stun->sh_username); stun_init_buffer(&stun->sh_passwd); stun->sh_nattype = stun_nat_unknown; /* initialize random number generator */ srand(time(NULL)); return stun;}/** * Performs shared secret request/response processing. * Result will be trigged in STUN handle callback (state * one of stun_tls_*). **/int stun_obtain_shared_secret(stun_handle_t *sh, stun_discovery_f sdf, stun_discovery_magic_t *magic, tag_type_t tag, tag_value_t value, ...){#if HAVE_OPENSSL int events = -1; int one, err = -1; su_wait_t wait[1] = { SU_WAIT_INIT }; su_socket_t s = INVALID_SOCKET; int family; su_addrinfo_t *ai = NULL; stun_discovery_t *sd; /* stun_request_t *req; */ ta_list ta; assert(sh); enter; if (!sh->sh_pri_addr[0].su_port) { /* no STUN server address, perform a DNS-SRV lookup */ ta_list ta; ta_start(ta, tag, value); SU_DEBUG_5(("Delaying STUN shared-secret req. for DNS-SRV query.\n")); err = priv_dns_queue_action(sh, stun_action_tls_query, sdf, magic, ta_tags(ta)); ta_end(ta); return err; } ai = &sh->sh_pri_info; if (sh->sh_use_msgint == 1) { SU_DEBUG_3(("%s: Obtaining shared secret.\n", __func__)); } else { SU_DEBUG_3(("No message integrity enabled.\n")); return errno = EFAULT, -1; } /* open tcp connection to server */ s = su_socket(family = AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { return STUN_ERROR(errno, socket); } /* asynchronous connect() */ if (su_setblocking(s, 0) < 0) { return STUN_ERROR(errno, su_setblocking); } if (setsockopt(s, SOL_TCP, TCP_NODELAY, (void *)&one, sizeof one) == -1) { return STUN_ERROR(errno, setsockopt); } /* Do an asynchronous connect(). Three error codes are ok, * others cause return -1. */ if (connect(s, (struct sockaddr *) &sh->sh_pri_addr, ai->ai_addrlen) == SOCKET_ERROR) { err = su_errno(); if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) { return STUN_ERROR(err, connect); } } SU_DEBUG_9(("%s: %s: %s\n", __func__, "connect", su_strerror(err))); sd = stun_discovery_create(sh, stun_action_tls_query, sdf, magic); sd->sd_socket = s; /* req = stun_request_create(sd); */ events = SU_WAIT_CONNECT | SU_WAIT_ERR; if (su_wait_create(wait, s, events) == -1) return STUN_ERROR(errno, su_wait_create); /* su_root_eventmask(sh->sh_root, sh->sh_root_index, s, events); */ if ((sd->sd_index = su_root_register(sh->sh_root, wait, stun_tls_callback, (su_wakeup_arg_t *) sd, 0)) == -1) { return STUN_ERROR(errno, su_root_register); } ta_start(ta, tag, value); sd->sd_tags = tl_adup(sh->sh_home, ta_args(ta)); ta_end(ta); sd->sd_state = stun_tls_connecting; /* Create and start timer for connect() timeout */ SU_DEBUG_3(("%s: creating timeout timer for connect()\n", __func__)); sd->sd_timer = su_timer_create(su_root_task(sh->sh_root), STUN_TLS_CONNECT_TIMEOUT); su_timer_set(sd->sd_timer, stun_tls_connect_timer_cb, (su_wakeup_arg_t *) sd); return 0;#else /* !HAVE_OPENSSL */ return -1;#endif}static stun_request_t *stun_request_create(stun_discovery_t *sd){ stun_handle_t *sh = sd->sd_handle; stun_request_t *req = NULL; enter; req = calloc(sizeof(stun_request_t), 1); if (!req) return NULL; req->sr_handle = sh; req->sr_discovery = sd; /* This is the default */ req->sr_socket = sd->sd_socket; req->sr_localinfo.li_addrlen = sizeof(su_sockaddr_t); req->sr_localinfo.li_addr = req->sr_local_addr; /* default timeout for next sendto() */ req->sr_timeout = STUN_SENDTO_TIMEOUT; req->sr_retry_count = 0; /* req->sr_action = action; */ req->sr_request_mask = 0; req->sr_msg = calloc(sizeof(stun_msg_t), 1); req->sr_state = stun_req_discovery_init; memcpy(req->sr_local_addr, sd->sd_bind_addr, sizeof(su_sockaddr_t)); /* Insert this request to the request queue */ x_insert(sh->sh_requests, req, sr); return req;}void stun_request_destroy(stun_request_t *req){ stun_handle_t *sh; assert(req); enter; sh = req->sr_handle; if (x_is_inserted(req, sr)) x_remove(req, sr); req->sr_handle = NULL; req->sr_discovery = NULL; /* memset(req->sr_destination, 0, sizeof(su_sockaddr_t)); */ if (req->sr_timer) { su_timer_destroy(req->sr_timer); req->sr_timer = NULL; SU_DEBUG_7(("%s: timer destroyed.\n", __func__)); } if (req->sr_msg) { free(req->sr_msg); req->sr_msg = NULL; } free(req); SU_DEBUG_9(("%s: request destroyed.\n", __func__)); return;}/** Destroy a STUN client */ void stun_handle_destroy(stun_handle_t *sh){ stun_discovery_t *sd = NULL, *kill = NULL; stun_request_t *a, *b; enter; if (sh->sh_dns_lookup) stun_dns_lookup_destroy(sh->sh_dns_lookup); if (sh->sh_dns_pend_tags) su_free(sh->sh_home, sh->sh_dns_pend_tags); for (a = sh->sh_requests; a; ) { b = a->sr_next; stun_request_destroy(a); a = b; } /* There can be several discoveries using the same socket. It is still enough to deregister the socket in first of them */ for (sd = sh->sh_discoveries; sd; ) { kill = sd; sd = sd->sd_next; /* Index has same value as sockfd, right? ... or not? */ if (kill->sd_index != -1) su_root_deregister(sh->sh_root, kill->sd_index); if (kill->sd_action == stun_action_tls_query) su_close(kill->sd_socket); stun_discovery_destroy(kill); } stun_free_message(&sh->sh_tls_request); stun_free_message(&sh->sh_tls_response); stun_free_buffer(&sh->sh_username); stun_free_buffer(&sh->sh_passwd); su_home_zap(sh->sh_home);}/** Create wait object and register it to the handle callback */staticint assign_socket(stun_discovery_t *sd, su_socket_t s, int register_socket) { stun_handle_t *sh = sd->sd_handle; int events; stun_discovery_t *tmp; /* su_localinfo_t clientinfo[1]; */ su_sockaddr_t su[1]; socklen_t sulen; char addr[SU_ADDRSIZE]; int err; su_wait_t wait[1] = { SU_WAIT_INIT }; enter; if (s == INVALID_SOCKET) { SU_DEBUG_3(("%s: invalid socket\n", __func__)); return errno = EINVAL, -1; } for (tmp = sh->sh_discoveries; tmp; tmp = tmp->sd_next) { if (tmp->sd_socket == s) { sd->sd_socket = s; sd->sd_index = tmp->sd_index; memcpy(sd->sd_bind_addr, tmp->sd_bind_addr, sizeof(su_sockaddr_t)); return 0; } } sd->sd_socket = s; if (!register_socket) return 0; /* set socket asynchronous */ if (su_setblocking(s, 0) < 0) { return STUN_ERROR(errno, su_setblocking); } /* xxx -- check if socket is already assigned to this root */ memset(su, 0, sulen = sizeof su); /* Try to bind it if not already bound */ if (getsockname(s, &su->su_sa, &sulen) == -1 || su->su_port == 0) { sulen = su->su_len = sizeof su->su_sin; su->su_family = AF_INET;#if defined (__CYGWIN__) get_localinfo(AF_INET, su, &sulen);#endif if ((err = bind(s, &su->su_sa, sulen)) < 0) { SU_DEBUG_3(("%s: bind(%s:%u): %s\n", __func__, su_inet_ntop(su->su_family, SU_ADDR(su), addr, sizeof(addr)), (unsigned) ntohs(su->su_port), su_strerror(su_errno()))); return -1; } if (getsockname(s, &su->su_sa, &sulen) == -1) { return STUN_ERROR(errno, getsockname); } } memcpy(&sd->sd_bind_addr, su, sulen); SU_DEBUG_3(("%s: local socket is bound to %s:%u\n", __func__, su_inet_ntop(su->su_family, SU_ADDR(su), addr, sizeof(addr)), (unsigned) ntohs(su->su_port))); events = SU_WAIT_IN | SU_WAIT_ERR; if (su_wait_create(wait, s, events) == -1) { return STUN_ERROR(su_errno(), su_wait_create); } /* Register receiving function with events specified above */ if ((sd->sd_index = su_root_register(sh->sh_root, wait, stun_bind_callback, (su_wakeup_arg_t *) sd, 0)) < 0) { return STUN_ERROR(errno, su_root_register); } SU_DEBUG_7(("%s: socket registered.\n", __func__)); return 0;}/** * Helper function needed by Cygwin builds. */#if defined (__CYGWIN__)static int get_localinfo(int family, su_sockaddr_t *su, socklen_t *return_len){ su_localinfo_t hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL; int i, error; char addr[SU_ADDRSIZE]; hints->li_family = family; if ((error = su_getlocalinfo(hints, &res)) == 0) { /* try to bind to the first available address */ for (i = 0, li = res; li; li = li->li_next) { if (li->li_family != family) continue; if (li->li_scope == LI_SCOPE_HOST) continue; assert(*return_len >= li->li_addrlen); memcpy(su, li->li_addr, *return_len = li->li_addrlen); SU_DEBUG_3(("%s: using local address %s\n", __func__, su_inet_ntop(family, SU_ADDR(su), addr, sizeof(addr)))); break; } su_freelocalinfo(res); if (!li) { /* Not found */ return STUN_ERROR(error, su_getlocalinfo); } return 0; } else { return STUN_ERROR(error, su_getlocalinfo); }}#endifstatic void priv_lookup_cb(stun_dns_lookup_t *self, stun_magic_t *magic){ const char *udp_target = NULL; uint16_t udp_port = 0; int res; stun_handle_t *sh = (stun_handle_t *)magic; res = stun_dns_lookup_udp_addr(self, &udp_target, &udp_port); if (res == 0 && udp_target) { /* XXX: assumption that same host and port used for UDP/TLS */ stun_atoaddr(sh->sh_home, AF_INET, &sh->sh_pri_info, udp_target); if (udp_port) sh->sh_pri_addr[0].su_port = htons(udp_port); else sh->sh_pri_addr[0].su_port = htons(STUN_DEFAULT_PORT); /* step: now that server address is known, continue * the pending action */ SU_DEBUG_5(("STUN server address found, running queue actions (%d).\n", sh->sh_dns_pend_action)); switch(sh->sh_dns_pend_action) { case stun_action_tls_query: stun_obtain_shared_secret(sh, sh->sh_dns_pend_cb, sh->sh_dns_pend_ctx, TAG_NEXT(sh->sh_dns_pend_tags)); break; case stun_action_binding_request: stun_bind(sh, sh->sh_dns_pend_cb, sh->sh_dns_pend_ctx, TAG_NEXT(sh->sh_dns_pend_tags)); break; case stun_action_test_lifetime: stun_test_lifetime(sh, sh->sh_dns_pend_cb, sh->sh_dns_pend_ctx, TAG_NEXT(sh->sh_dns_pend_tags)); break; case stun_action_test_nattype: stun_test_nattype(sh, sh->sh_dns_pend_cb, sh->sh_dns_pend_ctx, TAG_NEXT(sh->sh_dns_pend_tags)); break; default: SU_DEBUG_5(("Warning: unknown pending STUN DNS-SRV action.\n")); } } else { /* DNS lookup failed */ SU_DEBUG_5(("Warning: STUN DNS-SRV lookup failed.\n")); if (sh->sh_dns_pend_cb) { sh->sh_dns_pend_cb(sh->sh_dns_pend_ctx, sh, NULL, sh->sh_dns_pend_action, stun_error); } } su_free(sh->sh_home, sh->sh_dns_pend_tags), sh->sh_dns_pend_tags = NULL; sh->sh_dns_pend_action = 0; sh->sh_dns_pend_cb = NULL; sh->sh_dns_pend_ctx = NULL;}/** * Queus a discovery process for later execution when DNS-SRV lookup * has been completed. */static int priv_dns_queue_action(stun_handle_t *sh, stun_action_t action, stun_discovery_f sdf, stun_discovery_magic_t *magic,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -