📄 stun.c
字号:
#if defined(HAVE_OPENSSL)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; 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__, 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, getsockopt); } 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 */ /* Destroy the timeout timer */ /* su_timer_destroy(sd->sd_connect_timer); */ SU_DEBUG_3(("%s: shared secret not obtained from server. " \ "Proceed without username/password.\n", __func__)); sd->sd_state = stun_tls_connection_failed; self->sh_callback(self->sh_context, self, NULL, 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; self->sh_callback(self->sh_context, self, NULL, 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); 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); sd->sd_state = stun_tls_closing; break; case stun_tls_closing: su_close(sd->sd_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; self->sh_callback(self->sh_context, self, NULL, sd, sd->sd_action, sd->sd_state); break; default: return -1; } return 0;}#elseint stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg){ return 0;}#endif /* HAVE_OPENSSL */#if defined(HAVE_OPENSSL)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_destroy_timer(t); 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; sh->sh_callback(sh->sh_context, sh, NULL, sd, sd->sd_action, sd->sd_state); return;}#elsevoid stun_tls_connect_timer_cb(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg){}#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 < 8; 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); for (i = 0; i < 8; i++) { tmp = htons(msg->stun_hdr.tran_id[i]); memcpy(msg->enc_buf.data+len, &tmp, sizeof(tmp)); len+=sizeof(tmp); } return 0;}/* Return action of the request. If no request, return default value */static 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 */static inlinestun_request_t *find_request(stun_handle_t *self, uint16_t *id){ uint16_t *match; stun_request_t *req = NULL; int len = sizeof(uint16_t)*8; 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 */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 ipaddr[SU_ADDRSIZE + 2]; stun_msg_t binding_response; 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__, 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; */ self->sh_callback(self->sh_context, self, NULL, NULL, stun_action_no_action, stun_bind_error); return 0; }/* s = self->sh_bind_socket; */ /* receive response */ recv_len = sizeof(recv); dgram_len = recvfrom(s, dgram, sizeof(dgram), 0, (struct sockaddr *) &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__, inet_ntop(recv.su_family, SU_ADDR(&recv), ipaddr, sizeof(ipaddr)), 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); return 0;}/** Choose the right state machine */int do_action(stun_handle_t *sh, stun_msg_t *msg){ stun_request_t *req = NULL; stun_action_t action = stun_action_no_action; uint16_t *id; enter; if (!sh) return errno = EFAULT, -1; id = msg->stun_hdr.tran_id; req = find_request(sh, id); if (!req) 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_get_nattype: action_determine_nattype(req, msg); break; case stun_action_get_lifetime: process_get_lifetime(req, msg); break; case stun_action_keepalive: SU_DEBUG_3(("%s: Response to keepalive received.\n", __func__)); req->sr_state = stun_dispose_me; break; case stun_action_no_action: SU_DEBUG_3(("%s: Unknown response. No matching request found.\n", __func__)); req->sr_state = stun_request_not_found; sh->sh_callback(sh->sh_context, sh, req, NULL, stun_action_no_action, req->sr_state); req->sr_state = stun_dispose_me; break; default: SU_DEBUG_3(("%s: bad action.\n", __func__)); req->sr_state = stun_error; sh->sh_callback(sh->sh_context, sh, req, NULL, stun_action_no_action, req->sr_state); req->sr_state = stun_dispose_me; break; } return 0;}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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -