📄 stun.c
字号:
su_sockaddr_t *clnt_addr = clnt_info->li_addr; stun_msg_t *binding_request; stun_discovery_t *sd = req->sr_discovery; enter; binding_request = req->sr_msg; switch (binding_response->stun_hdr.msg_type) { case BINDING_RESPONSE: if (stun_validate_message_integrity(binding_response, &self->sh_passwd) < 0) { stun_free_message(binding_request); stun_free_message(binding_response); return retval; } memset(clnt_addr, 0, sizeof(struct sockaddr_in)); clnt_addr_len = sizeof(su_sockaddr_t); mapped_addr = stun_get_attr(binding_response->stun_attr, MAPPED_ADDRESS); if (mapped_addr != NULL) { memcpy(clnt_addr, mapped_addr->pattr, clnt_addr_len); retval = 0; } /* update alternative server address */ if (sd->sd_sec_addr->su_family == 0) { /* alternative server address not present */ chg_addr = stun_get_attr(binding_response->stun_attr, CHANGED_ADDRESS); if (chg_addr != NULL) memcpy(sd->sd_sec_addr, chg_addr->pattr, sizeof(struct sockaddr_in)); } break; case BINDING_ERROR_RESPONSE: default: if (stun_process_error_response(binding_response) < 0) { SU_DEBUG_3(("%s: Error in Binding Error Response.\n", __func__)); } req->sr_state = stun_bind_error; break; } return retval;}void stun_get_lifetime_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg){ stun_request_t *req = arg; stun_discovery_t *sd = req->sr_discovery; su_sockaddr_t *destination; int err; enter; su_timer_destroy(t); destination = (su_sockaddr_t *) sd->sd_pri_addr; err = stun_send_binding_request(req, destination); if (err < 0) { stun_free_message(req->sr_msg); return; } return;}int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response){ stun_discovery_t *sd = req->sr_discovery; stun_request_t *new; stun_handle_t *sh = req->sr_handle; su_localinfo_t *li; su_sockaddr_t *sa; su_timer_t *sockfdy_timer = NULL; su_socket_t sockfdy = sd->sd_socket2; int err; stun_action_t action = get_action(req); su_sockaddr_t *destination; /* Even the first message could not be delivered */ if ((req->sr_state == stun_request_timeout) && (req->sr_from_y == -1)) { SU_DEBUG_0(("%s: lifetime determination failed.\n", __func__)); sd->sd_state = stun_discovery_timeout; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; return 0; } if (abs(sd->sd_lt_cur - sd->sd_lt) <= STUN_LIFETIME_CI) { sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; 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_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_discovery_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_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_get_lifetime_timer_cb, (su_wakeup_arg_t *) new); return 0;}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_bind_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; return 0;}int action_determine_nattype(stun_request_t *req, stun_msg_t *binding_response){ su_sockaddr_t local; socklen_t locallen; stun_handle_t *sh = req->sr_handle; su_localinfo_t *li = NULL; stun_discovery_t *sd = req->sr_discovery; su_socket_t s = sd->sd_socket; stun_action_t action; int err; 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_dispose_me; /* stun_request_destroy(req); */ return 0; } /* parse first the payload */ if (binding_response) process_binding_request(req, binding_response); /* mapped address */ li = &req->sr_localinfo; if (req->sr_request_mask == 0) { sd->sd_first = 1; memcpy(sd->sd_addr_seen_outside, li->li_addr, sizeof(su_sockaddr_t)); } else if (req->sr_request_mask & (CHG_IP | CHG_PORT)) sd->sd_second = 1; else if (req->sr_request_mask & CHG_PORT) sd->sd_third = 1; memset(&local, 0, sizeof(local)); locallen = sizeof(local); err = getsockname(s, (struct sockaddr *) &local, &locallen); if (err < 0) STUN_ERROR(err, getsockname); if ((req->sr_state == stun_discovery_timeout)) { if (sd->sd_first && sd->sd_second && sd->sd_third && sd->sd_fourth) { sd->sd_nattype = stun_nat_port_res_cone; sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } else if (sd->sd_first && sd->sd_second && sd->sd_fourth) { /* Sudden network problem */ sd->sd_nattype = stun_nat_unknown; sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } else if (sd->sd_first && sd->sd_second) { if (memcmp(li->li_addr, li->li_addr, 8) == 0) { sd->sd_nattype = stun_sym_udp_fw; sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } else { sd->sd_fourth = 1; /* The request will be destroyed by the timer */ req->sr_state = stun_dispose_me; req = NULL; req = stun_request_create(sd); if (stun_make_binding_req(sh, req, req->sr_msg, 0, 0) < 0) return -1; err = stun_send_binding_request(req, sd->sd_sec_addr); if (err < 0) { stun_free_message(req->sr_msg); return -1; } return 0; } } else if (sd->sd_first) { sd->sd_nattype = stun_udp_blocked; sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } } else { if (sd->sd_first && sd->sd_second && sd->sd_third && sd->sd_fourth) { if (memcmp(li->li_addr, sd->sd_addr_seen_outside, 8) == 0) { /* Response: Type 6 - Restricted */ sd->sd_nattype = stun_nat_res_cone; } else { sd->sd_nattype = stun_nat_sym; } sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } if (sd->sd_first && sd->sd_second) { if (memcmp(li->li_addr, sd->sd_addr_seen_outside, 8) == 0) sd->sd_nattype = stun_open_internet; else sd->sd_nattype = stun_nat_full_cone; sd->sd_state = stun_discovery_done; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; /* stun_request_destroy(req); */ /* stun_discovery_destroy(sd); */ return 0; } else if (sd->sd_first) { if (memcmp(&local, li->li_addr, 8) == 0) return 0; } } /* The discovery process is still ongoing, but I can be killed */ req->sr_state = stun_dispose_me; return 0;}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_dispose_me) { su_timer_destroy(t); 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 */ if (req->sr_retry_count >= sh->sh_max_retries) { 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_request_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; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); req->sr_state = stun_dispose_me; break; case stun_action_get_nattype: action_determine_nattype(req, NULL); break; case stun_action_get_lifetime: process_get_lifetime(req, NULL); break; case stun_action_keepalive: sd->sd_state = stun_discovery_timeout; sh->sh_callback(sh->sh_context, sh, req, sd, action, sd->sd_state); stun_keepalive_destroy(sh, sd->sd_socket); break; default: break; return; } /* Destroy me immediately */ req->sr_state = stun_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. * */ int stun_send_binding_request(stun_request_t *req, su_sockaddr_t *srvr_addr){ su_timer_t *sendto_timer = NULL; int 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) { return -1; } /* Create and start timer */ sendto_timer = su_timer_create(su_root_task(sh->sh_root), STUN_SENDTO_TIMEOUT); su_timer_set(sendto_timer, stun_sendto_timer_cb, (su_wakeup_arg_t *) req); req->sr_state = stun_discovery_processing; return 0;}/** Compose a STUN message of the format defined by stun_msg_t */int stun_make_binding_req(stun_handle_t *sh, stun_request_t *req, stun_msg_t *msg, tag_type_t tag, tag_value_t value, ...){ int i; stun_attr_t *tmp, **p; int bits = 0; int chg_ip = 0, chg_port = 0; ta_list ta; enter; ta_start(ta, tag, value); tl_gets(ta_args(ta), STUNTAG_CHANGE_IP_REF(chg_ip), STUNTAG_CHANGE_PORT_REF(chg_port), TAG_END()); ta_end(ta); if (chg_ip) bits |= CHG_IP; if (chg_port) bits |= CHG_PORT; if (req)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -