⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stun.c

📁 Sofia SIP is an open-source SIP User-Agent library, compliant with the IETF RFC3261 specification.
💻 C
📖 第 1 页 / 共 5 页
字号:
				 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 + -