📄 stun.c
字号:
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 + -