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

📄 stun.c

📁 Sofia SIP is an open-source SIP User-Agent library, compliant with the IETF RFC3261 specification.
💻 C
📖 第 1 页 / 共 5 页
字号:
      sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);    return 0;  }  /* We come here as a response to a request send from the sockfdy */  if (req->sr_from_y == 1) {    req->sr_state = stun_req_dispose_me, req = NULL;    new = stun_request_create(sd);    new->sr_from_y = 0;    if (stun_make_binding_req(sh, new, new->sr_msg, 0, 0) < 0)       return -1;    destination = (su_sockaddr_t *) sd->sd_pri_addr;    err = stun_send_binding_request(new, destination);    if (err < 0) {      stun_free_message(new->sr_msg);      return -1;    }    return 0;  }  else if (req->sr_from_y == 0) {    if (req->sr_state != stun_req_timeout) {      /* mapping with X still valid */      sd->sd_lt_cur = sd->sd_lt;      sd->sd_lt = (int) (sd->sd_lt + sd->sd_lt_max) / 2;      SU_DEBUG_1(("%s: Response received from socket X, " \		  "lifetime at least %d sec, next trial: %d sec\n",		  __func__, sd->sd_lt_cur, sd->sd_lt));    }    else {      sd->sd_lt_max = sd->sd_lt;      sd->sd_lt = (int) (sd->sd_lt + sd->sd_lt_cur) / 2;      SU_DEBUG_1(("%s: No response received from socket X, " \		  "lifetime at most %d sec, next trial: %d sec\n",		  __func__, sd->sd_lt_max, sd->sd_lt));    }  }  /* Rock, we come from sockfdx */  process_binding_request(req, binding_response);  li = &req->sr_localinfo;  sa = req->sr_local_addr;  stun_free_message(binding_response);    /* Destroy me with the bad mofo timer */  req->sr_state = stun_req_dispose_me, req = NULL;    new = stun_request_create(sd);  /* Use sockfdy */  new->sr_socket = sockfdy;  new->sr_from_y = 1;  if (stun_make_binding_req(sh, new, new->sr_msg, 0, 0) < 0)     return -1;  stun_add_response_address(new->sr_msg, (struct sockaddr_in *) sa);  /* Create and start timer */  sockfdy_timer = su_timer_create(su_root_task(sh->sh_root), sd->sd_lt);  su_timer_set(sockfdy_timer, stun_test_lifetime_timer_cb, (su_wakeup_arg_t *) new);  return 0;}static int action_bind(stun_request_t *req, stun_msg_t *binding_response){  su_localinfo_t *li = NULL;  su_sockaddr_t *sa = NULL;  stun_discovery_t *sd = req->sr_discovery;  stun_handle_t *sh = req->sr_handle;  stun_action_t action;  enter;  action = get_action(req);  process_binding_request(req, binding_response);  li = &req->sr_localinfo;  sa = req->sr_local_addr;  memcpy(sd->sd_addr_seen_outside, sa, sizeof(su_sockaddr_t));  sd->sd_state = stun_discovery_done;  req->sr_state = stun_req_dispose_me;    if (sd->sd_callback)    sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);  return 0;}/** * Returns a non-zero value if some local interface address * matches address in su. */static int priv_find_matching_localadress(su_sockaddr_t *su){  su_localinfo_t hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;  int af;  char addr[SU_ADDRSIZE];  SU_DEBUG_5(("%s: checking if %s is a local address.\n", __func__, 	      su_inet_ntop(AF_INET, SU_ADDR(su), addr, sizeof(addr))));  hints->li_family = af = su->su_family;  if (su_getlocalinfo(hints, &res) != 0)    return 0;  /* check if any of the address match 'sockaddr'  */  for (li = res; li; li = li->li_next) {    if (li->li_family != af)      continue;          if (memcmp(SU_ADDR(su), SU_ADDR(li->li_addr), SU_ADDRLEN(su)) == 0) {      SU_DEBUG_5(("%s: found matching local address\n", __func__));      break;    }    SU_DEBUG_9(("%s: skipping local address %s.\n", __func__, 		su_inet_ntop(af, SU_ADDR(li->li_addr), addr, sizeof(addr))));  }    su_freelocalinfo(res);  return li != NULL;}/** * Helper function for action_determine_nattype(). */static void priv_mark_discovery_done(stun_discovery_t *sd, 				     stun_handle_t *sh, 				     stun_action_t action, 				     stun_request_t *req){  sd->sd_state = stun_discovery_done;  req->sr_state = stun_req_dispose_me;  if (sd->sd_callback)    sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);}/** * Handles responses related to the NAT type discovery process. *  * The requests for Tests I-III are sent in parallel, so the * callback has to keep track of which requests have been received * and postpone decisions until enough responses have been processed. * * @see stun_test_nattype(). */static int action_determine_nattype(stun_request_t *req, stun_msg_t *binding_response){  stun_handle_t *sh = req->sr_handle;  su_localinfo_t *li = NULL;  stun_discovery_t *sd = req->sr_discovery;  stun_action_t action;  int err;  /* test status: 0 not received, -1 timeout, 1 response received */  int reply_res;  enter;  action = get_action(req);  /* if the NAT type is already detected, ignore this request */  if (!sd || (sd->sd_nattype != stun_nat_unknown)) {    req->sr_state = stun_req_dispose_me;    /* stun_request_destroy(req); */    return 0;  }  /* parse first the response payload */  if (binding_response)    process_binding_request(req, binding_response);  /* get pointer to MAPPED-ADDRESS of req */  li = &req->sr_localinfo;  /* check whether the response timed out or not */  if (req->sr_state == stun_req_timeout)     reply_res = -1;  else    reply_res = 1;  /* note: Test I completed - reply from the destination address   *       where we sent our packet  */  if (req->sr_request_mask == 0 && sd->sd_first == 0) {    sd->sd_first = reply_res;    if (reply_res)      memcpy(sd->sd_addr_seen_outside, li->li_addr, sizeof(su_sockaddr_t));  }  /* note: Test II completed - reply from another address and port  */  else if ((req->sr_request_mask & CHG_IP) &&	   (req->sr_request_mask & CHG_PORT))    sd->sd_second = reply_res;  /* note: Test III completed -  reply from another port  */  else if (req->sr_request_mask & CHG_PORT)    sd->sd_third = reply_res;  /* note: Test IV completed - request and reply to another port */  else if (req->sr_request_mask == 0 && sd->sd_fourth == 2) {    sd->sd_fourth = reply_res;    /* SU_DEBUG_5(("Comparing reported MAPPED ADDRESSES %s:%u vs %s:%u.\n",       inet_ntoa(sd->sd_addr_seen_outside[0].su_sin.sin_addr),       sd->sd_addr_seen_outside[0].su_port,       inet_ntoa(li->li_addr->su_sin.sin_addr),       li->li_addr->su_port)); */    /* note: check whether MAPPED-ADDRESS address has changed (when      *       sending to a different IP:port -> NAT mapping behaviour) */    if (su_cmp_sockaddr(sd->sd_addr_seen_outside, li->li_addr) != 0) {      sd->sd_mapped_addr_match = 0;    }    else {      sd->sd_mapped_addr_match = 1;    }  }  SU_DEBUG_9(("stun natcheck status: 1st=%d, 2nd=%d, 3rd=%d, 4th=%d, mask=%d, sr_state=%d (timeout=%d, done=%d)..\n",	      sd->sd_first, sd->sd_second, sd->sd_third, sd->sd_fourth, req->sr_request_mask, req->sr_state, stun_req_timeout, stun_discovery_done));  /* case 1: no response to Test-I (symmetric response)   *         a FW must be blocking us */  if (sd->sd_first < 0) {    sd->sd_nattype = stun_udp_blocked;    priv_mark_discovery_done(sd, sh, action, req);  }  /* case 2: mapped address matches our local address    *         not behind a NAT, result of test two determinces   *         whether we are behind a symmetric FW or not */  else if (sd->sd_first > 0 &&	   sd->sd_second && 	   priv_find_matching_localadress(sd->sd_addr_seen_outside)) {    if (sd->sd_second > 0)       sd->sd_nattype = stun_open_internet;    else      sd->sd_nattype = stun_sym_udp_fw;    priv_mark_discovery_done(sd, sh, action, req);  }  /* case 3: response ok to Test-II, and behind a NAT    *         do not make conclusions until Test-IV has been scheduled */  else if (sd->sd_first > 0 && 	   sd->sd_second > 0 &&	   sd->sd_fourth) {    if (sd->sd_mapped_addr_match == 1)      sd->sd_nattype = stun_nat_full_cone;    else      sd->sd_nattype = stun_nat_ei_filt_ad_map;    priv_mark_discovery_done(sd, sh, action, req);  }  /* case 4: tests I-III done, perform IV    *         see notes below */  else if (sd->sd_first > 0 && 	   sd->sd_second &&	   sd->sd_third &&	   sd->sd_fourth == 0) {    /*      * No response received, so we now perform Test IV using the address     * learnt from response to Test-I.      *      * Unfortunately running  this test will potentially affect     * results of a subsequent Test-II (depends on NAT binding timeout     * values). To get around this, the STUN server would ideally have      * a dedicated IP:port for Test-IV. But within the currents specs,     * we need to reuse one of the IP:port addresses already used in      * Test-II by the STUN server to send us packets.     */    SU_DEBUG_7(("Sending STUN NAT type Test-IV request to %s.\n",	       inet_ntoa(sd->sd_sec_addr[0].su_sin.sin_addr)));        sd->sd_fourth = 2; /* request, -1, 0, 1 reserved for results */    req->sr_state = stun_req_dispose_me;    req = stun_request_create(sd);    err = stun_make_binding_req(sh, req, req->sr_msg,				STUNTAG_CHANGE_IP(0),				STUNTAG_CHANGE_PORT(0),				TAG_END());    if (err == 0) {      err = stun_send_binding_request(req, sd->sd_sec_addr);    }    if (err < 0) {      SU_DEBUG_0(("WARNING: Failure in performing STUN Test-IV check. "		  "The results related to mapping characteristics may be incorrect."));      stun_free_message(req->sr_msg);      sd->sd_fourth = -1;      /* call function again, sd_fourth stops the recursion */      action_determine_nattype(req, binding_response);      return -1;    }        return 0; /* we don't want to dispose this req */  }  /* case 5: no response Test-II, and success with III   *         the NAT is filtering packets from different IP   *         do not make conclusions until Test-IV has been scheduled */  else if (sd->sd_first > 0 && 	   sd->sd_second < 0 &&	   sd->sd_third > 0 &&	   sd->sd_fourth) {    if (sd->sd_mapped_addr_match == 1)      sd->sd_nattype = stun_nat_res_cone;    else      sd->sd_nattype = stun_nat_ad_filt_ad_map;    priv_mark_discovery_done(sd, sh, action, req);  }  /* case 6: no response to Test-II nor III    *         the NAT is filtering packets from different port   *         do not make conclusions until Test-IV has been scheduled */  else if (sd->sd_first > 0 && 	   sd->sd_second < 0 &&	   sd->sd_third < 0 &&	   sd->sd_fourth) {    if (sd->sd_mapped_addr_match == 1)      sd->sd_nattype = stun_nat_port_res_cone;    else      sd->sd_nattype = stun_nat_adp_filt_ad_map;    priv_mark_discovery_done(sd, sh, action, req);  }  /* this request of the discovery process can be disposed */  req->sr_state = stun_req_dispose_me;  return 0;}static void stun_sendto_timer_cb(su_root_magic_t *magic, 				 su_timer_t *t,				 su_timer_arg_t *arg){  stun_request_t *req = arg;  stun_handle_t *sh = req->sr_handle;  stun_discovery_t *sd = req->sr_discovery;  stun_action_t action = get_action(req);  long timeout = 0;  enter;  if (req->sr_state == stun_req_dispose_me) {    stun_request_destroy(req);    SU_DEBUG_7(("%s: timer destroyed.\n", __func__));    return;  }  ++req->sr_retry_count;  /* check if max retry count has been exceeded; or if    * action type is NAT type check (XXX: the request attributes   * are not passed correctly to resend function) */  if (req->sr_retry_count >= sh->sh_max_retries ||      action == stun_action_test_nattype) {    errno = ETIMEDOUT;    STUN_ERROR(errno, stun_sendto_timer_cb);    stun_free_message(req->sr_msg);    free(req->sr_msg), req->sr_msg = NULL;    /* Either the server was dead, address wrong or STUN_UDP_BLOCKED */    /* sd->sd_nattype = stun_udp_blocked; */    req->sr_state = stun_req_timeout;        /* If the action is binding request, we are done. If action was       NAT type determination, process with the state machine. */    switch (action) {    case stun_action_binding_request:      sd->sd_state = stun_discovery_timeout;      req->sr_state = stun_req_dispose_me;      /* Use per discovery specific callback */      if (sd->sd_callback)	sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);      break;    case stun_action_test_nattype:      action_determine_nattype(req, NULL);      break;          case stun_action_test_lifetime:      process_test_lifetime(req, NULL);      break;          case stun_action_keepalive:      sd->sd_state = stun_discovery_timeout;      /* Use per discovery specific callback */      if (sd->sd_callback)	sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);      stun_keepalive_destroy(sh, sd->sd_socket);            break;    default:      break;    }    /* Destroy me immediately */    req->sr_state = stun_req_dispose_me;    timeout = 0;  }  else {    SU_DEBUG_3(("%s: Timeout no. %d, retransmitting.\n",		__func__, req->sr_retry_count));    /* Use pre-defined destination address for re-sends */    if (stun_send_message(req->sr_socket, req->sr_destination,			  req->sr_msg, &(sh->sh_passwd)) < 0) {      stun_free_message(req->sr_msg);      free(req->sr_msg), req->sr_msg = NULL;      return;    }    timeout = req->sr_timeout *= 2;  }    su_timer_set_at(t, stun_sendto_timer_cb, (su_wakeup_arg_t *) req,		  su_time_add(su_now(), timeout));    return;}/** This function sends a binding request to the address at serv (ip, *  port). which could be the original or alternative socket addresses *  of the STUN server. Local address is provided in cli, and *  resulting mapped address is also saved in cli. *  Return 0 if successful, -1 if failed * * @return * On success, zero is returned.  Upon error, -1 is returned, and @e errno is * set appropriately. *  * @ERRORS * @ERROR EBADF           @a sockfd is not a valid deseriptor. * @ERROR EPROTONOSUPPORT @a sockfd is not an 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. * @ERROR ETIMEDOUT       Request timed out. *  */   static int stun_send_binding_request(stun_request_t *req,				     su_sockaddr_t  *srvr_addr){  su_timer_t *sendto_timer = NULL;  su_socket_t s;  stun_handle_t *sh = req->sr_handle;  stun_msg_t *msg =  req->sr_msg;  assert (sh && srvr_addr);  enter;  s = req->sr_socket;  memcpy(req->sr_destination, srvr_addr, sizeof(su_sockaddr_t));  if (stun_send_message(s, srvr_addr, msg, &(sh->sh_passwd)) < 0) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -