📄 stun.c
字号:
tag_type_t tag, tag_value_t value, ...){ ta_list ta; if (!sh->sh_dns_pend_action) { if (!sh->sh_dns_lookup) { sh->sh_dns_lookup = stun_dns_lookup((stun_magic_t*)sh, sh->sh_root, priv_lookup_cb, sh->sh_domain); ta_start(ta, tag, value); assert(sh->sh_dns_pend_tags == NULL); sh->sh_dns_pend_tags = tl_tlist(sh->sh_home, ta_tags(ta)); ta_end(ta); sh->sh_dns_pend_cb = sdf; sh->sh_dns_pend_ctx = magic; } sh->sh_dns_pend_action |= action; return 0; } return -1;}static int priv_stun_bind_send(stun_handle_t *sh, stun_request_t *req, stun_discovery_t *sd){ int res = stun_send_binding_request(req, sh->sh_pri_addr); if (res < 0) { stun_free_message(req->sr_msg); stun_discovery_destroy(sd); } return res;}/** * Performs a STUN Binding Discovery (see RFC3489/3489bis) process * * To integrity protect the discovery process, first call * stun_request_shared_secret() on the handle 'sh'. * * If STUNTAG_REGISTER_SOCKET() is omitted, or set to false, the * client is responsible for socket i/o. Other stun module will * perform the whole discovery process and return the results * via callback 'sdf'. * * @param sh pointer to valid stun handle * @param sdf callback to signal process progress * @param magic context pointer attached to 'sdf' * @param tag, value, ... list of tagged parameters. * * @TAGS * @TAG STUNTAG_SOCKET() Bind socket handle to STUN (socket handle). * @TAG STUNTAG_REGISTER_SOCKET() Register socket for eventloop owned by STUN (boolean) * * @return * On success, zero is returned. Upon error, -1 is returned, and @e errno is * set appropriately. * * @ERRORS * @ERROR EFAULT An invalid address is given as argument * @ERROR EPROTONOSUPPORT Not a UDP socket. * @ERROR EINVAL The socket is already bound to an address. * @ERROR EACCESS The address is protected, and the user is not * the super-user. * @ERROR ENOTSOCK Argument is a descriptor for a file, not a socket. * @ERROR EAGAIN Operation in progress. Application should call * stun_bind() again when there is data available on * the socket. */int stun_bind(stun_handle_t *sh, stun_discovery_f sdf, stun_discovery_magic_t *magic, tag_type_t tag, tag_value_t value, ...){ su_socket_t s = INVALID_SOCKET; stun_request_t *req = NULL; stun_discovery_t *sd = NULL; ta_list ta; int s_reg = 0; enter; if (sh == NULL) return errno = EFAULT, -1; if (!sh->sh_pri_addr[0].su_port) { /* no STUN server address, perform a DNS-SRV lookup */ int err; ta_list ta; ta_start(ta, tag, value); SU_DEBUG_5(("Delaying STUN bind for DNS-SRV query.\n")); err = priv_dns_queue_action(sh, stun_action_binding_request, sdf, magic, ta_tags(ta)); ta_end(ta); return err; } ta_start(ta, tag, value); tl_gets(ta_args(ta), STUNTAG_SOCKET_REF(s), STUNTAG_REGISTER_EVENTS_REF(s_reg), TAG_END()); ta_end(ta); sd = stun_discovery_create(sh, stun_action_binding_request, sdf, magic); if (assign_socket(sd, s, s_reg) < 0) return -1; req = stun_request_create(sd); if (stun_make_binding_req(sh, req, req->sr_msg, 0, 0) < 0) { stun_discovery_destroy(sd); stun_free_message(req->sr_msg); return -1; } /* note: we always report success if bind() succeeds */ return priv_stun_bind_send(sh, req, sd);}/** * Returns the address of the public binding allocated by the NAT. * * In case of multiple on path NATs, the binding allocated by * the outermost NAT is returned. * * This function returns the local address seen from outside. * Note that the address is not valid until the event stun_clien_done is launched. */int stun_discovery_get_address(stun_discovery_t *sd, void *addr, socklen_t *return_addrlen){ socklen_t siz; enter; assert(sd && addr); siz = SU_SOCKADDR_SIZE(sd->sd_addr_seen_outside); /* Check if enough memory provided */ if (siz > *return_addrlen) return errno = EFAULT, -1; else *return_addrlen = siz; memcpy(addr, sd->sd_addr_seen_outside, siz); return 0;}static stun_discovery_t *stun_discovery_create(stun_handle_t *sh, stun_action_t action, stun_discovery_f sdf, stun_discovery_magic_t *magic){ stun_discovery_t *sd = NULL; enter; sd = calloc(1, sizeof(stun_discovery_t)); sd->sd_action = action; sd->sd_handle = sh; sd->sd_callback = sdf; sd->sd_magic = magic; sd->sd_lt_cur = 0; sd->sd_lt = STUN_LIFETIME_EST; sd->sd_lt_max = STUN_LIFETIME_MAX; sd->sd_pri_info.ai_addrlen = sizeof sd->sd_pri_addr->su_sin; sd->sd_pri_info.ai_addr = &sd->sd_pri_addr->su_sa; /* Insert this action to the discovery queue */ x_insert(sh->sh_discoveries, sd, sd); return sd;}static int stun_discovery_destroy(stun_discovery_t *sd){ stun_handle_t *sh; enter; if (!sd) return errno = EFAULT, -1; sh = sd->sd_handle; if (sd->sd_timer) su_timer_destroy(sd->sd_timer), sd->sd_timer = NULL; /* if we are in the queue*/ if (x_is_inserted(sd, sd)) x_remove(sd, sd); sd->sd_next = NULL; free(sd); return 0;}/** * Initiates STUN discovery process to find out NAT * characteristics. * * Process partly follows the algorithm defined in RFC3489 section * 10.1. Due the known limitations of RFC3489, some of the tests * are done. * * Note: does not support STUNTAG_DOMAIN() even if specified to * stun_handle_init(). * * @TAGS * @TAG STUNTAG_SOCKET Bind socket for STUN * @TAG STUNTAG_REGISTER_SOCKET Register socket for eventloop owned by STUN * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address * * @return 0 on success, non-zero on error */int stun_test_nattype(stun_handle_t *sh, stun_discovery_f sdf, stun_discovery_magic_t *magic, tag_type_t tag, tag_value_t value, ...){ int err = 0, index = 0, s_reg = 0; ta_list ta; char const *server = NULL; stun_request_t *req = NULL; stun_discovery_t *sd = NULL; su_socket_t s = INVALID_SOCKET; su_sockaddr_t *destination = NULL; 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 get-nat-type req. for DNS-SRV query.\n")); err = priv_dns_queue_action(sh, stun_action_test_nattype, sdf, magic, ta_tags(ta)); ta_end(ta); return err; } ta_start(ta, tag, value); tl_gets(ta_args(ta), STUNTAG_SOCKET_REF(s), STUNTAG_REGISTER_EVENTS_REF(s_reg), STUNTAG_SERVER_REF(server), TAG_END()); ta_end(ta); if (s < 0) return errno = EFAULT, -1; sd = stun_discovery_create(sh, stun_action_test_nattype, sdf, magic); sd->sd_mapped_addr_match = -1; if ((index = assign_socket(sd, s, s_reg)) < 0) return errno = EFAULT, -1; /* If no server given, use default address from stun_handle_init() */ if (!server) { /* memcpy(&sd->sd_pri_info, &sh->sh_pri_info, sizeof(su_addrinfo_t)); */ memcpy(sd->sd_pri_addr, sh->sh_pri_addr, sizeof(su_sockaddr_t)); } else { err = stun_atoaddr(sh->sh_home, AF_INET, &sd->sd_pri_info, server); memcpy(sd->sd_pri_addr, &sd->sd_pri_info.ai_addr, sizeof(su_sockaddr_t)); } destination = (su_sockaddr_t *) sd->sd_pri_addr; req = stun_request_create(sd); if (stun_make_binding_req(sh, req, req->sr_msg, STUNTAG_CHANGE_IP(0), STUNTAG_CHANGE_PORT(0), TAG_END()) < 0) return -1; err = stun_send_binding_request(req, destination); if (err < 0) { stun_free_message(req->sr_msg); return -1; } /* Same Public IP and port, Test III, server ip 0 or 1 should be the same */ req = stun_request_create(sd); if (stun_make_binding_req(sh, req, req->sr_msg, STUNTAG_CHANGE_IP(0), STUNTAG_CHANGE_PORT(1), TAG_END()) < 0) return -1; err = stun_send_binding_request(req, destination); if (err < 0) { stun_free_message(req->sr_msg); return -1; } req = NULL; req = stun_request_create(sd); if (stun_make_binding_req(sh, req, req->sr_msg, STUNTAG_CHANGE_IP(1), STUNTAG_CHANGE_PORT(1), TAG_END()) < 0) return -1; err = stun_send_binding_request(req, destination); if (err < 0) { stun_free_message(req->sr_msg); } return err;}/******************************************************************** * Internal functions *******************************************************************/#if HAVE_OPENSSLstatic int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg){ stun_discovery_t *sd = arg; stun_handle_t *self = sd->sd_handle; stun_msg_t *msg_req, *resp; int z, err = -1; SSL_CTX* ctx; SSL *ssl; X509* server_cert; unsigned char buf[512]; stun_attr_t *password, *username; int state; int events = su_wait_events(w, sd->sd_socket), one = 0; unsigned int onelen; enter; SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self, events & SU_WAIT_CONNECT ? " CONNECTED" : "", events & SU_WAIT_ERR ? " ERR" : "", events & SU_WAIT_IN ? " IN" : "", events & SU_WAIT_OUT ? " OUT" : "")); getsockopt(sd->sd_socket, SOL_SOCKET, SO_ERROR, (void *)&one, &onelen); if (one != 0) { STUN_ERROR(one, SO_ERROR); } if (one || events & SU_WAIT_ERR) { su_wait_destroy(w); su_root_deregister(self->sh_root, sd->sd_index); sd->sd_index = -1; /* mark index as deregistered */ su_timer_reset(sd->sd_timer); SU_DEBUG_3(("%s: shared secret not obtained from server. " \ "Proceed without username/password.\n", __func__)); sd->sd_state = stun_tls_connection_failed; if (sd->sd_callback) sd->sd_callback(sd->sd_magic, self, sd, sd->sd_action, sd->sd_state); return 0; } /* Can be NULL, too */ ssl = self->sh_ssl; msg_req = &self->sh_tls_request; resp = &self->sh_tls_response; state = sd->sd_state; switch (state) { case stun_tls_connecting: /* compose shared secret request */ if (stun_make_sharedsecret_req(msg_req) != 0) { STUN_ERROR(errno, stun_make_sharedsecret_req); stun_free_buffer(&msg_req->enc_buf); return -1; } /* openssl initiation */ SSLeay_add_ssl_algorithms(); SSL_load_error_strings(); ctx = SSL_CTX_new(TLSv1_client_method()); self->sh_ctx = ctx; if (ctx == NULL) { STUN_ERROR(errno, SSL_CTX_new); stun_free_buffer(&msg_req->enc_buf); return -1; } if (SSL_CTX_set_cipher_list(ctx, "AES128-SHA") == 0) { STUN_ERROR(errno, SSL_CTX_set_cipher_list); stun_free_buffer(&msg_req->enc_buf); return -1; } /* Start TLS negotiation */ ssl = SSL_new(ctx); self->sh_ssl = ssl; if (SSL_set_fd(ssl, sd->sd_socket) == 0) { STUN_ERROR(err, connect); stun_free_buffer(&msg_req->enc_buf); return -1; } /* No break here! Continue to SSL_connect. If SSL_continue returns * less than 1 because of nonblocking, have a different state * (ssl_connecting) for it */ case stun_tls_ssl_connecting: events = SU_WAIT_ERR | SU_WAIT_IN; su_root_eventmask(self->sh_root, sd->sd_index, sd->sd_socket, events); z = SSL_connect(ssl); err = SSL_get_error(ssl, z); if (z < 1 && (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)) { sd->sd_state = stun_tls_ssl_connecting; return 0; } else if (z < 1) { su_wait_destroy(w); su_root_deregister(self->sh_root, sd->sd_index); sd->sd_index = -1; /* mark index as deregistered */ stun_free_buffer(&msg_req->enc_buf); sd->sd_state = stun_tls_connection_failed; if (sd->sd_callback) sd->sd_callback(sd->sd_magic, self, sd, sd->sd_action, sd->sd_state); return -1; } /* Inform application about the progress */ sd->sd_state = stun_tls_writing; /* self->sh_callback(self->sh_context, self, self->sh_state); */ events = SU_WAIT_ERR | SU_WAIT_OUT; su_root_eventmask(self->sh_root, sd->sd_index, sd->sd_socket, events); break; case stun_tls_writing: events = SU_WAIT_ERR | SU_WAIT_IN; su_root_eventmask(self->sh_root, sd->sd_index, sd->sd_socket, events); SU_DEBUG_3(("TLS connection using %s\n", SSL_get_cipher(ssl))); server_cert = SSL_get_peer_certificate(ssl); if(server_cert) { SU_DEBUG_3(("\t subject: %s\n", X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0))); SU_DEBUG_3(("\t issuer: %s\n", X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0))); } X509_free(server_cert);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -