📄 stun.c
字号:
z = SSL_write(ssl, msg_req->enc_buf.data, msg_req->enc_buf.size); if (z < 0) { err = SSL_get_error(ssl, z); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0; else { STUN_ERROR(errno, SSL_write); stun_free_buffer(&msg_req->enc_buf); return -1; } } sd->sd_state = stun_tls_reading; break; case stun_tls_reading: events = SU_WAIT_ERR | SU_WAIT_OUT; su_root_eventmask(self->sh_root, sd->sd_index, sd->sd_socket, events); SU_DEBUG_5(("Shared Secret Request sent to server:\n")); debug_print(&msg_req->enc_buf); z = SSL_read(ssl, buf, sizeof(buf)); if (z <= 0) { err = SSL_get_error(ssl, z); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0; else { stun_free_buffer(&msg_req->enc_buf); return -1; } } /* We end up here after there's something to read from the * socket */ resp->enc_buf.size = z; resp->enc_buf.data = malloc(z); memcpy(resp->enc_buf.data, buf, z); SU_DEBUG_5(("Shared Secret Response received from server:\n")); debug_print(&resp->enc_buf); /* closed TLS connection */ SSL_shutdown(ssl); su_close(sd->sd_socket), sd->sd_socket = INVALID_SOCKET; SSL_free(self->sh_ssl), ssl = NULL; SSL_CTX_free(self->sh_ctx), ctx = NULL; stun_free_buffer(&msg_req->enc_buf); /* process response */ if (stun_parse_message(resp) < 0) { perror("stun_parse_message"); stun_free_buffer(&resp->enc_buf); return -1; } switch(resp->stun_hdr.msg_type) { case SHARED_SECRET_RESPONSE: username = stun_get_attr(resp->stun_attr, USERNAME); password = stun_get_attr(resp->stun_attr, PASSWORD); if (username != NULL && password != NULL) { /* move result to se */ stun_copy_buffer(&self->sh_username, username->pattr); stun_copy_buffer(&self->sh_passwd, password->pattr); } break; case SHARED_SECRET_ERROR_RESPONSE: if (stun_process_error_response(resp) < 0) { SU_DEBUG_5(("Error in Shared Secret Error Response.\n")); } stun_free_buffer(&resp->enc_buf); return -1; break; default: break; } su_wait_destroy(w); su_root_deregister(self->sh_root, sd->sd_index); sd->sd_index = -1; /* mark index as deregistered */ self->sh_use_msgint = 1; sd->sd_state = stun_tls_done; if (sd->sd_callback) sd->sd_callback(sd->sd_magic, self, sd, sd->sd_action, sd->sd_state); break; default: return -1; } return 0;}#endif /* HAVE_OPENSSL */#if HAVE_OPENSSLstatic void stun_tls_connect_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg){ stun_discovery_t *sd = arg; stun_handle_t *sh = sd->sd_handle; enter; su_timer_destroy(t); if (t == sd->sd_timer) { sd->sd_timer = NULL; } SU_DEBUG_7(("%s: timer destroyed.\n", __func__)); if (sd->sd_state != stun_tls_connecting) return; SU_DEBUG_7(("%s: connect() timeout.\n", __func__)); su_root_deregister(sh->sh_root, sd->sd_index); sd->sd_index = -1; /* mark index as deregistered */ sd->sd_state = stun_tls_connection_timeout; sd->sd_callback(sd->sd_magic, sh, sd, sd->sd_action, sd->sd_state); return;}#endif /* HAVE_OPENSSL *//** Compose a STUN message of the format defined by stun_msg_t * result encoded in enc_buf ready for sending as well. */int stun_make_sharedsecret_req(stun_msg_t *msg){ int i, len; uint16_t tmp; /* compose header */ msg->stun_hdr.msg_type = SHARED_SECRET_REQUEST; msg->stun_hdr.msg_len = 0; /* actual len computed by stun_send_message */ for (i = 0; i < STUN_TID_BYTES; i++) { msg->stun_hdr.tran_id[i] = (1 + rand() % RAND_MAX_16); } /* no buffer assigned yet */ stun_init_buffer(&msg->enc_buf); msg->enc_buf.data = malloc(20); msg->enc_buf.size = 20; tmp = htons(msg->stun_hdr.msg_type); len = 0; memcpy(msg->enc_buf.data, &tmp, sizeof(tmp)); len+=sizeof(tmp); tmp = htons(msg->stun_hdr.msg_len); memcpy(msg->enc_buf.data+len, &tmp, sizeof(tmp)); len+=sizeof(tmp); memcpy(msg->enc_buf.data+len, msg->stun_hdr.tran_id, STUN_TID_BYTES); len+=STUN_TID_BYTES; return 0;}/* Return action of the request. If no request, return default value */su_inlinestun_action_t get_action(stun_request_t *req){ stun_discovery_t *sd = NULL; /* XXX -- if no sr_handle something is leaking... */ if (!req || !req->sr_discovery || !req->sr_handle) return stun_action_no_action; sd = req->sr_discovery; return sd->sd_action;}/* Find request from the request queue, based on TID */su_inlinestun_request_t *find_request(stun_handle_t *self, void *id){ void *match; stun_request_t *req = NULL; int len = STUN_TID_BYTES; for (req = self->sh_requests; req; req = req->sr_next) { match = req->sr_msg->stun_hdr.tran_id; if (memcmp(match, id, len) == 0) { return req; } } return NULL;}/** Process socket event */static int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg){ stun_discovery_t *sd = arg; stun_handle_t *self = sd->sd_handle; int retval = -1, err = -1, dgram_len; char addr[SU_ADDRSIZE]; stun_msg_t binding_response, *msg; unsigned char dgram[512] = { 0 }; su_sockaddr_t recv; socklen_t recv_len; su_socket_t s = sd->sd_socket; int events = su_wait_events(w, s); enter; SU_DEBUG_7(("%s(%p): events%s%s%s\n", __func__, (void *)self, events & SU_WAIT_IN ? " IN" : "", events & SU_WAIT_OUT ? " OUT" : "", events & SU_WAIT_ERR ? " ERR" : "")); if (!(events & SU_WAIT_IN || events & SU_WAIT_OUT)) { /* su_wait_destroy(w); */ /* su_root_deregister(self->sh_root, self->ss_root_index); */ /* self->sh_state = stun_bind_error; */ return 0; } /* receive response */ recv_len = sizeof(recv); dgram_len = su_recvfrom(s, dgram, sizeof(dgram), 0, &recv, &recv_len); err = errno; if ((dgram_len < 0) && (err != EAGAIN)) { /* su_wait_destroy(w); */ /* su_root_deregister(self->sh_root, self->ss_root_index); */ STUN_ERROR(err, recvfrom); /* stun_free_message(binding_request); */ return err; } else if (dgram_len <= 0) { STUN_ERROR(err, recvfrom); /* No data available yet, wait for the event. */ return 0; } /* Message received. */ binding_response.enc_buf.data = (unsigned char *) malloc(dgram_len); binding_response.enc_buf.size = dgram_len; memcpy(binding_response.enc_buf.data, dgram, dgram_len); SU_DEBUG_5(("%s: response from server %s:%u\n", __func__, su_inet_ntop(recv.su_family, SU_ADDR(&recv), addr, sizeof(addr)), ntohs(recv.su_port))); debug_print(&binding_response.enc_buf); /* Parse here the incoming message. */ if (stun_parse_message(&binding_response) < 0) { stun_free_message(&binding_response); SU_DEBUG_5(("%s: Error parsing response.\n", __func__)); return retval; } /* Based on the decoded payload, find the corresponding request * (based on TID). */ do_action(self, &binding_response); if (binding_response.enc_buf.size) free(binding_response.enc_buf.data); { stun_attr_t **a, *b; msg = &binding_response; for (a = &msg->stun_attr; *a;) { if ((*a)->pattr) free((*a)->pattr); if ((*a)->enc_buf.data) free((*a)->enc_buf.data); b = *a; b = b->next; free(*a); *a = NULL; *a = b; } } return 0;}/** Choose the right state machine */static int do_action(stun_handle_t *sh, stun_msg_t *msg){ stun_request_t *req = NULL; stun_action_t action = stun_action_no_action; void *id; enter; if (!sh) return errno = EFAULT, -1; id = msg->stun_hdr.tran_id; req = find_request(sh, id); if (!req) { SU_DEBUG_7(("warning: unable to find matching TID for response\n")); return 0; } action = get_action(req); /* Based on the action, use different state machines */ switch (action) { case stun_action_binding_request: action_bind(req, msg); break; case stun_action_test_nattype: action_determine_nattype(req, msg); break; case stun_action_test_lifetime: process_test_lifetime(req, msg); break; case stun_action_keepalive: SU_DEBUG_3(("%s: Response to keepalive received.\n", __func__)); req->sr_state = stun_req_dispose_me; break; case stun_action_no_action: SU_DEBUG_3(("%s: Unknown response. No matching request found.\n", __func__)); req->sr_state = stun_req_dispose_me; break; default: SU_DEBUG_3(("%s: bad action.\n", __func__)); req->sr_state = stun_req_error; req->sr_state = stun_req_dispose_me; break; } return 0;}static int process_binding_request(stun_request_t *req, stun_msg_t *binding_response){ int retval = -1, clnt_addr_len; stun_attr_t *mapped_addr, *chg_addr; stun_handle_t *self = req->sr_handle; su_localinfo_t *clnt_info = &req->sr_localinfo; 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(su_sockaddr_t)); 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_req_error; break; } return retval;}static void stun_test_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;}static int process_test_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_req_timeout) && (req->sr_from_y == -1)) { SU_DEBUG_0(("%s: lifetime determination failed.\n", __func__)); 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); return 0; } if (abs(sd->sd_lt_cur - sd->sd_lt) <= STUN_LIFETIME_CI) { sd->sd_state = stun_discovery_done; req->sr_state = stun_req_dispose_me; /* Use per discovery specific callback */ if (sd->sd_callback)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -