📄 stun.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//** * @file stun.c STUN client module * * See RFC 3489 for further information. * * @author Martti Mela <Martti.Mela@nokia.com> * @author Tat Chan <Tat.Chan@nokia.com> * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Kai Vehmanen <Kai.Vehmanen@nokia.com> * * @date Created: Thu Jul 24 17:21:00 2003 ppessi */#include "config.h" #include <assert.h>#define SU_ROOT_MAGIC_T struct stun_magic_t/* #define SU_WAKEUP_ARG_T struct stun_handle_s */#include "sofia-sip/stun.h"#include "stun_internal.h"#include "sofia-sip/stun_tag.h"#include <sofia-sip/su_alloc.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/su_log.h>#include <sofia-sip/su.h>#include <sofia-sip/su_localinfo.h>#if defined(HAVE_OPENSSL)#include <openssl/opensslv.h>#endif#if !defined(ETIMEDOUT) && defined(_WIN32)#define ETIMEDOUT WSAETIMEDOUT#endif/* Missing socket symbols */#ifndef SOL_TCP#define SOL_TCP IPPROTO_TCP#endif/** STUN log. */su_log_t stun_log[] = { SU_LOG_INIT("stun", "STUN_DEBUG", SU_DEBUG) }; enum { STUN_SENDTO_TIMEOUT = 1000, STUN_TLS_CONNECT_TIMEOUT = 8000,};int stun_change_map[4][4] = { {0, 1, 2, 3}, /* no change */ {2, 3, 0, 1}, /* change ip */ {1, 0, 3, 2}, /* change port */ {3, 2, 1, 0} /* change ip and port, Ca:Cp */};/* NAT TYPES */typedef enum stun_nattype_e { stun_nat_unknown, stun_open_internet, stun_udp_blocked, stun_sym_udp_fw, stun_nat_full_cone, stun_nat_sym, stun_nat_res_cone, stun_nat_port_res_cone,} stun_nattype_t;#define CHG_IP 0x001#define CHG_PORT 0x004#define x_insert(l, n, x) \ ((l) ? (l)->x##_prev = &(n)->x##_next : 0, \ (n)->x##_next = (l), (n)->x##_prev = &(l), (l) = (n))#define x_remove(n, x) \ ((*(n)->x##_prev = (n)->x##_next) ? \ (n)->x##_next->x##_prev = (n)->x##_prev : 0)#define x_is_inserted(n, x) ((n)->x##_prev != NULL)struct stun_discovery_s { stun_discovery_t *sd_next, **sd_prev; /**< Linked list */ stun_handle_t *sd_handle; su_addrinfo_t sd_pri_info; /**< server primary info */ su_sockaddr_t sd_pri_addr[1]; /**< server primary address */ su_addrinfo_t sd_sec_info; /**< server secondary info */ su_sockaddr_t sd_sec_addr[1]; /**< server secondary address */ stun_action_t sd_action; /**< My action */ stun_state_t sd_state; /**< Progress states */ su_socket_t sd_socket; /**< target socket */ su_sockaddr_t sd_bind_addr[1]; /**< local address */ su_socket_t sd_socket2; /**< Alternative socket */ int sd_index; /**< root_register index */ /* Binding discovery */ su_sockaddr_t sd_addr_seen_outside[1]; /**< local address */ /* NAT type related */ stun_nattype_t sd_nattype; /**< Determined NAT type */ int sd_first; /**< These are the requests */ int sd_second; int sd_third; int sd_fourth; /* Life time related */ int sd_lt_cur; int sd_lt; int sd_lt_max; /* Keepalive timeout */ unsigned int sd_timeout; su_timer_t *sd_timer;};struct stun_request_s { stun_request_t *sr_next, **sr_prev; /**< Linked list */ stun_msg_t *sr_msg; /**< STUN message pointer */ stun_handle_t *sr_handle; /**< backpointer, STUN object */ su_socket_t sr_socket; /**< Alternative socket */ su_localinfo_t sr_localinfo; /**< local addrinfo */ su_sockaddr_t sr_local_addr[1]; /**< local address */ su_sockaddr_t sr_destination[1]; stun_state_t sr_state; /**< Progress states */ int sr_retry_count; /**< current retry number */ long sr_timeout; /**< timeout for next sendto() */ int sr_from_y; int sr_request_mask; /**< Mask consisting of chg_ip and chg_port */ stun_discovery_t *sr_discovery;};struct stun_handle_s{ su_home_t sh_home[1]; su_root_t *sh_root; /**< event loop */ int sh_root_index; /**< object index of su_root_register() */ stun_request_t *sh_requests; /**< outgoing requests list */ stun_discovery_t *sh_discoveries; /**< Actions list */ int sh_max_retries; /**< max resend for sendto() */ su_addrinfo_t sh_pri_info; /**< server primary info */ su_sockaddr_t sh_pri_addr[1]; /**< server primary address */ su_addrinfo_t sh_sec_info; /**< server secondary info */ su_sockaddr_t sh_sec_addr[1]; /**< server secondary address */ su_localinfo_t sh_localinfo; /**< local addrinfo */ su_sockaddr_t sh_local_addr[1]; /**< local address */#if defined(HAVE_OPENSSL) SSL_CTX *sh_ctx; /**< SSL context for TLS */ SSL *sh_ssl; /**< SSL handle for TLS */#else void *sh_ctx; /**< SSL context for TLS */ void *sh_ssl; /**< SSL handle for TLS */#endif stun_msg_t sh_tls_request; stun_msg_t sh_tls_response; int sh_nattype; /**< NAT-type, see stun_common.h */ stun_event_f sh_callback; /**< callback for calling application */ stun_magic_t *sh_context; /**< application context */ stun_buffer_t sh_username; stun_buffer_t sh_passwd; int sh_use_msgint; /**< try message integrity (TLS) */ int sh_req_msgint; /**< require use of msg-int (TLS) */};#define STUN_STATE_STR(x) case x: return #xchar const *stun_str_state(stun_state_t state){ switch (state) { STUN_STATE_STR(stun_no_assigned_event); STUN_STATE_STR(stun_dispose_me); STUN_STATE_STR(stun_tls_connecting); STUN_STATE_STR(stun_tls_writing); STUN_STATE_STR(stun_tls_closing); STUN_STATE_STR(stun_tls_reading); STUN_STATE_STR(stun_tls_done); STUN_STATE_STR(stun_discovery_init); STUN_STATE_STR(stun_discovery_processing); STUN_STATE_STR(stun_discovery_done); STUN_STATE_STR(stun_bind_init); STUN_STATE_STR(stun_bind_processing); STUN_STATE_STR(stun_bind_done); STUN_STATE_STR(stun_tls_connection_timeout); STUN_STATE_STR(stun_tls_connection_failed); STUN_STATE_STR(stun_tls_ssl_connect_failed); STUN_STATE_STR(stun_request_not_found); STUN_STATE_STR(stun_bind_error); STUN_STATE_STR(stun_bind_timeout); STUN_STATE_STR(stun_discovery_timeout); STUN_STATE_STR(stun_request_timeout); case stun_error: default: return "stun_error"; }}/* char const *stun_nattype(stun_handle_t *sh) */char const *stun_nattype(stun_discovery_t *sd){ char const *stun_nattype_str[] = { "NAT type undetermined", "Open Internet", "UDP traffic is blocked or server unreachable", "Symmetric UDP Firewall", "Full-Cone NAT", "Symmetric NAT", "Restricted Cone NAT", "Port Restricted Cone NAT", }; if (sd) return stun_nattype_str[sd->sd_nattype]; else return stun_nattype_str[stun_nat_unknown];}int stun_lifetime(stun_discovery_t *sd){ return sd ? sd->sd_lt_cur : -1;}#if defined(HAVE_OPENSSL)char const stun_version[] = "sofia-sip-stun using " OPENSSL_VERSION_TEXT;#elsechar const stun_version[] = "sofia-sip-stun";#endifint do_action(stun_handle_t *sh, stun_msg_t *binding_response);int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);stun_discovery_t *stun_discovery_create(stun_handle_t *sh, stun_action_t action);int stun_discovery_destroy(stun_discovery_t *sd);int action_bind(stun_request_t *req, stun_msg_t *binding_response);int action_determine_nattype(stun_request_t *req, stun_msg_t *binding_response);int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);stun_request_t *stun_request_create(stun_discovery_t *sd);int stun_send_binding_request(stun_request_t *req, su_sockaddr_t *srvr_addr);int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);/* timers */void stun_sendto_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg);void stun_tls_connect_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg);void stun_get_lifetime_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg);void stun_keepalive_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg);/** * Return su_root_t assigned to stun_handle_t. * * @param self stun_handle_t object * @return su_root_t object, NULL if self not given. */su_root_t *stun_handle_root(stun_handle_t *self){ return self ? self->sh_root : NULL;}/** * Check if a STUN handle should be created. * * Return true either there is a tag STUNTAG_SERVER() in list or if * STUN_SERVER environment variable is set. * * @param tag,value,... tag-value list */int stun_is_requested(tag_type_t tag, tag_value_t value, ...){ ta_list ta; tagi_t const *t; char const *stun_server; enter; ta_start(ta, tag, value); t = tl_find(ta_args(ta), stuntag_server); stun_server = t && t->t_value ? (char *)t->t_value : getenv("STUN_SERVER"); ta_end(ta); return stun_server != NULL;}/** * Create a STUN handle * * @param tag,value,... tag-value list * * @TAGS * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address * @TAG STUNTAG_REQUIRE_INTEGRITY() true if msg integrity should be * used enforced * */stun_handle_t *stun_handle_create(stun_magic_t *context, su_root_t *root, stun_event_f cb, tag_type_t tag, tag_value_t value, ...){ stun_handle_t *stun = NULL; char const *server = NULL; int req_msg_integrity = 1; int err; ta_list ta; enter; ta_start(ta, tag, value); tl_gets(ta_args(ta), STUNTAG_SERVER_REF(server), STUNTAG_REQUIRE_INTEGRITY_REF(req_msg_integrity), TAG_END()); stun = su_home_clone(NULL, sizeof(*stun)); if (!stun) { SU_DEBUG_3(("%s: %s failed\n", __func__, "su_home_clone()")); return NULL; } /* Enviroment overrides */ if (getenv("STUN_SERVER")) { server = getenv("STUN_SERVER"); SU_DEBUG_5(("%s: using STUN_SERVER=%s\n", __func__, server)); } SU_DEBUG_5(("%s(\"%s\"): called\n", "stun_handle_tcreate", server)); if (!server) 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; err = stun_atoaddr(AF_INET, &stun->sh_pri_info, server); if (err < 0) return NULL; stun->sh_nattype = stun_nat_unknown; stun->sh_root = root; stun->sh_context = context; stun->sh_callback = cb; /* 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)); ta_end(ta); return stun;}#if defined(HAVE_OPENSSL)/** Shared secret request/response processing */int stun_handle_request_shared_secret(stun_handle_t *sh){ int events = -1; int one, err = -1; su_wait_t wait[1] = { SU_WAIT_INIT }; su_socket_t s = SOCKET_ERROR; int family; su_addrinfo_t *ai = NULL; su_timer_t *connect_timer = NULL; stun_discovery_t *sd; /* stun_request_t *req; */ assert(sh); enter; ai = &sh->sh_pri_info; if (sh->sh_use_msgint == 1) { SU_DEBUG_3(("Contacting Server to obtain shared secret. " \ "Please wait.\n")); } 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 == -1) { STUN_ERROR(errno, socket); return -1; } /* asynchronous connect() */ if (su_setblocking(s, 0) < 0) { STUN_ERROR(errno, su_setblocking); return -1; } if (setsockopt(s, SOL_TCP, TCP_NODELAY, (void *)&one, sizeof one) == -1) { STUN_ERROR(errno, setsockopt); return -1; } /* 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) { STUN_ERROR(err, connect); return -1; } } SU_DEBUG_9(("%s: %s: %s\n", __func__, "connect", su_strerror(err))); sd = stun_discovery_create(sh, stun_action_tls_query); sd->sd_socket = s; /* req = stun_request_create(sd); */ events = SU_WAIT_CONNECT | SU_WAIT_ERR; if (su_wait_create(wait, s, events) == -1) STUN_ERROR(errno, su_wait_create); su_root_eventmask(sh->sh_root, sh->sh_root_index, s, events);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -