/* Respond to ALLOCATE request */ static pj_status_t send_allocate_response(pj_turn_allocation *alloc, pj_stun_session *srv_sess, pj_turn_transport *transport, const pj_stun_rx_data *rdata) { pj_stun_tx_data *tdata; pj_status_t status; /* Respond the original ALLOCATE request */ status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add XOR-RELAYED-ADDRESS attribute */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->relay.hkey.addr, pj_sockaddr_get_len(&alloc->relay.hkey.addr)); /* Add LIFETIME. */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, (unsigned)alloc->relay.lifetime); /* Add BANDWIDTH */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); /* Add RESERVATION-TOKEN */ PJ_TODO(ADD_RESERVATION_TOKEN); /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); /* Send the response */ return pj_stun_session_send_msg(srv_sess, transport, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); }
/* Create and send successful response */ static void send_reply_ok(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata) { pj_status_t status; unsigned interval; pj_stun_tx_data *tdata; status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating STUN success response", status); return; } /* Calculate time to expiration */ if (alloc->relay.lifetime != 0) { pj_time_val now; pj_gettimeofday(&now); interval = alloc->relay.expiry.sec - now.sec; } else { interval = 0; } /* Add LIFETIME if this is not ChannelBind. */ if (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)!=PJ_STUN_CHANNEL_BIND_METHOD){ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, interval); /* Add BANDWIDTH if lifetime is not zero */ if (interval != 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); } } status = pj_stun_session_send_msg(alloc->sess, NULL, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN success response", status); return; } }
static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_timestamp u64; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_PRIORITY, 0x6e0001ff); if (status != PJ_SUCCESS) goto on_error; u64.u32.hi = 0x932ff9b1; u64.u32.lo = 0x51263b36; status = pj_stun_msg_add_uint64_attr(pool, msg, PJ_STUN_ATTR_ICE_CONTROLLED, &u64); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&s1, v->username)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint1()", status); return NULL; }
static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_timestamp u64; pj_str_t s1; pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_PRIORITY, 0x6e0001ff); u64.u32.hi = 0x932ff9b1; u64.u32.lo = 0x51263b36; pj_stun_msg_add_uint64_attr(pool, msg, PJ_STUN_ATTR_ICE_CONTROLLED, &u64); pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&s1, v->username)); pj_stun_msg_add_msgint_attr(pool, msg); pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); return msg; }
static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr mapped_addr; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); if (status != PJ_SUCCESS) goto on_error; status = pj_sockaddr_init(pj_AF_INET6(), &mapped_addr, pj_cstr(&s1, "2001:db8:1234:5678:11:2233:4455:6677"), 32853); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint3()", status); return NULL; }
/* * Send REFRESH */ static void send_refresh(pj_tcp_session *sess, int lifetime) { pj_stun_tx_data *tdata; pj_status_t status; PJ_ASSERT_ON_FAIL(sess->state==PJ_TCP_STATE_READY, return); /* Create a bare REFRESH request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_REFRESH_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Add LIFETIME */ if (lifetime >= 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, lifetime); } #if TCP_TODO /* Send request */ if (lifetime == 0) { pj_tcp_session_set_state(sess, PJ_TCP_STATE_DISCONNECTING); } status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, (sess->conn_type==PJ_TCP_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) goto on_error; #endif return; on_error: if (lifetime == 0) { pj_tcp_session_set_state(sess, PJ_TCP_STATE_DISCONNECTED); sess_shutdown(sess, status); } }
static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr_in mapped_addr; pj_str_t s1; pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "127.0.0.1"), 32853); pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr_in)); pj_stun_msg_add_msgint_attr(pool, msg); pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); return msg; }
static pj_stun_msg* create_success_response(test_server *test_srv, turn_allocation *alloc, pj_stun_msg *req, pj_pool_t *pool, unsigned lifetime, pj_str_t *auth_key) { pj_stun_msg *resp; pj_str_t tmp; pj_status_t status; /* Create response */ status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp); if (status != PJ_SUCCESS) { return NULL; } /* Add TURN_NONCE */ pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE)); /* Add LIFETIME */ pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime); if (lifetime != 0) { /* Add XOR-RELAYED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr, pj_sockaddr_get_len(&alloc->alloc_addr)); /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr, pj_sockaddr_get_len(&alloc->client_addr)); } /* Add blank MESSAGE-INTEGRITY */ pj_stun_msg_add_msgint_attr(pool, resp); /* Set auth key */ pj_stun_create_key(pool, auth_key, &test_srv->domain, &test_srv->username, PJ_STUN_PASSWD_PLAIN, &test_srv->passwd); return resp; }
static pj_status_t apply_msg_options(pj_stun_session *sess, pj_pool_t *pool, const pj_stun_req_cred_info *auth_info, pj_stun_msg *msg) { pj_status_t status = 0; pj_str_t realm, username, nonce, auth_key; /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute * to the request. The server SHOULD include a SOFTWARE attribute in all * responses. * * If magic value is not PJ_STUN_MAGIC, only apply the attribute for * responses. */ if (sess->srv_name.slen && pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL && (PJ_STUN_IS_RESPONSE(msg->hdr.type) || (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) { pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, &sess->srv_name); } if (pj_stun_auth_valid_for_msg(msg) && auth_info) { realm = auth_info->realm; username = auth_info->username; nonce = auth_info->nonce; auth_key = auth_info->auth_key; } else { realm.slen = username.slen = nonce.slen = auth_key.slen = 0; } /* Create and add USERNAME attribute if needed */ if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, &username); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add REALM only when long term credential is used */ if (realm.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_REALM, &realm); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add NONCE when desired */ if (nonce.slen && (PJ_STUN_IS_REQUEST(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_NONCE, &nonce); } /* Add MESSAGE-INTEGRITY attribute */ if (username.slen && auth_key.slen) { status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add FINGERPRINT attribute if necessary */ if (sess->use_fingerprint) { status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } return PJ_SUCCESS; }
/* Perform test */ static pj_status_t send_test(nat_detect_session *sess, enum test_type test_id, const pj_sockaddr_in *alt_addr, pj_uint32_t change_flag) { pj_uint32_t magic, tsx_id[3]; pj_status_t status; sess->result[test_id].executed = PJ_TRUE; /* Randomize tsx id */ do { magic = pj_rand(); } while (magic == PJ_STUN_MAGIC); tsx_id[0] = pj_rand(); tsx_id[1] = pj_rand(); tsx_id[2] = test_id; /* Create BIND request */ status = pj_stun_session_create_req(sess->stun_sess, PJ_STUN_BINDING_REQUEST, magic, (pj_uint8_t*)tsx_id, &sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; /* Add CHANGE-REQUEST attribute */ status = pj_stun_msg_add_uint_attr(sess->pool, sess->result[test_id].tdata->msg, PJ_STUN_ATTR_CHANGE_REQUEST, change_flag); if (status != PJ_SUCCESS) goto on_error; /* Configure alternate address */ if (alt_addr) sess->cur_server = (pj_sockaddr_in*) alt_addr; else sess->cur_server = &sess->server; PJ_LOG(5,(sess->pool->obj_name, "Performing %s to %s:%d", test_names[test_id], pj_inet_ntoa(sess->cur_server->sin_addr), pj_ntohs(sess->cur_server->sin_port))); /* Send the request */ status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, PJ_TRUE, sess->cur_server, sizeof(pj_sockaddr_in), sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; return status; }
/* Perform test */ static pj_status_t send_test(nat_detect_session *sess, enum test_type test_id, const pj_sockaddr *alt_addr, pj_uint32_t change_flag) { pj_uint32_t magic, tsx_id[3]; char addr[PJ_INET6_ADDRSTRLEN]; pj_status_t status; sess->result[test_id].executed = PJ_TRUE; /* Randomize tsx id */ do { magic = pj_rand(); } while (magic == PJ_STUN_MAGIC); tsx_id[0] = pj_rand(); tsx_id[1] = pj_rand(); tsx_id[2] = test_id; /* Create BIND request */ status = pj_stun_session_create_req(sess->stun_sess, PJ_STUN_BINDING_REQUEST, magic, (pj_uint8_t*)tsx_id, &sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; /* Add CHANGE-REQUEST attribute */ status = pj_stun_msg_add_uint_attr(sess->pool, sess->result[test_id].tdata->msg, PJ_STUN_ATTR_CHANGE_REQUEST, change_flag); if (status != PJ_SUCCESS) goto on_error; /* Configure alternate address, synthesize it if necessary */ if (alt_addr) { status = pj_sockaddr_synthesize(sess->server.addr.sa_family, &sess->cur_addr, alt_addr); if (status != PJ_SUCCESS) goto on_error; sess->cur_server = &sess->cur_addr; } else { sess->cur_server = &sess->server; } PJ_LOG(5,(sess->pool->obj_name, "Performing %s to %s:%d", test_names[test_id], pj_sockaddr_print(sess->cur_server, addr, sizeof(addr), 0), pj_sockaddr_get_port(sess->cur_server))); /* Send the request */ status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, PJ_TRUE, sess->cur_server, pj_sockaddr_get_len(sess->cur_server), sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; return status; }