/* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_stun_sock *stun_sock; pj_ssize_t size; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock || !stun_sock->active_sock) { /* We have been shutdown, but this callback may still get called * by retransmit timer. */ return PJ_EINVALIDOP; } pj_assert(token==INTERNAL_MSG_TOKEN); PJ_UNUSED_ARG(token); size = pkt_size; return pj_activesock_sendto(stun_sock->active_sock, &stun_sock->int_send_key, pkt, &size, 0, dst_addr, addr_len); }
/* * Callback notification from STUN session when it wants to send * a STUN message towards the client. */ static pj_status_t stun_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_turn_allocation *alloc; PJ_UNUSED_ARG(token); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); return alloc->transport->sendto(alloc->transport, pkt, pkt_size, 0, dst_addr, addr_len); }
/* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_stun_sock *stun_sock; pj_ssize_t size; pj_status_t status; pj_bool_t is_stun = PJ_FALSE; pj_bool_t is_tnl_data = PJ_FALSE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock || !stun_sock->active_sock) { /* We have been shutdown, but this callback may still get called * by retransmit timer. */ return PJ_EINVALIDOP; } pj_assert(token==INTERNAL_MSG_TOKEN); PJ_UNUSED_ARG(token); /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, /*PJ_STUN_IS_DATAGRAM | */PJ_STUN_CHECK_PACKET); if (status == PJ_SUCCESS) is_stun = PJ_TRUE; else is_tnl_data = (((pj_uint8_t*)pkt)[pkt_size] == 1); size = pkt_size; if(is_stun || !is_tnl_data) { pj_ioqueue_op_key_t *op_key = (pj_ioqueue_op_key_t*)pj_mem_alloc(sizeof(pj_ioqueue_op_key_t)); pj_ioqueue_op_key_init(op_key, sizeof(pj_ioqueue_op_key_t)); return pj_activesock_sendto(stun_sock->active_sock, op_key, pkt, &size, PJ_IOQUEUE_URGENT_DATA, dst_addr, addr_len); } else { return pj_activesock_sendto(stun_sock->active_sock, &stun_sock->send_key, pkt, &size, 0, dst_addr, addr_len); } }
/* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_stun_sock *stun_sock; pj_ssize_t size; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); pj_assert(token==INTERNAL_MSG_TOKEN); PJ_UNUSED_ARG(token); size = pkt_size; return pj_activesock_sendto(stun_sock->active_sock, &stun_sock->int_send_key, pkt, &size, 0, dst_addr, addr_len); }
/* * Callback to send outgoing packet from STUN session. */ static pj_status_t on_send_msg(pj_stun_session *stun_sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { nat_detect_session *sess; pj_ssize_t pkt_len; pj_status_t status; PJ_UNUSED_ARG(token); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pkt_len = pkt_size; status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0, dst_addr, addr_len); return status; }
/* * Callback notification from STUN session when it receives STUN * indications. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_xor_peer_addr_attr *peer_attr; pj_stun_data_attr *data_attr; pj_turn_allocation *alloc; pj_turn_permission *perm; pj_ssize_t len; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Only expect Send Indication */ if (msg->hdr.type != PJ_STUN_SEND_INDICATION) { /* Ignore */ return PJ_SUCCESS; } /* Get XOR-PEER-ADDRESS attribute */ peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); /* MUST have XOR-PEER-ADDRESS attribute */ if (!peer_attr) return PJ_SUCCESS; /* Get DATA attribute */ data_attr = (pj_stun_data_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0); /* Create/update/refresh the permission */ perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (perm == NULL) { perm = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); } refresh_permission(perm); /* Return if we don't have data */ if (data_attr == NULL) return PJ_SUCCESS; /* Relay the data to peer */ len = data_attr->length; pj_sock_sendto(alloc->relay.tp.sock, data_attr->data, &len, 0, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); return PJ_SUCCESS; }
/* * Callback notification from STUN session when it receives STUN * requests. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Refuse to serve any request if we've been shutdown */ if (alloc->relay.lifetime == 0) { /* Reject with 437 if we're shutting down */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); return PJ_SUCCESS; } if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) { /* * Handle REFRESH request */ pj_stun_lifetime_attr *lifetime; pj_stun_bandwidth_attr *bandwidth; /* Get LIFETIME attribute */ lifetime = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); /* Get BANDWIDTH attribute */ bandwidth = (pj_stun_bandwidth_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0); /* TODO: process bandwidth */ PJ_UNUSED_ARG(bandwidth); if (lifetime && lifetime->value==0) { /* * This is deallocation request. */ alloc->relay.lifetime = 0; /* Respond first */ send_reply_ok(alloc, rdata); /* Shutdown allocation */ PJ_LOG(4,(alloc->obj_name, "Client %s request to dealloc, shutting down", alloc->info)); alloc_shutdown(alloc); } else { /* * This is a refresh request. */ /* Update lifetime */ if (lifetime) { alloc->relay.lifetime = lifetime->value; } /* Update bandwidth */ // TODO: /* Update expiration timer */ resched_timeout(alloc); /* Send reply */ send_reply_ok(alloc, rdata); } } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { /* * ChannelBind request. */ pj_stun_channel_number_attr *ch_attr; pj_stun_xor_peer_addr_attr *peer_attr; pj_turn_permission *p1, *p2; ch_attr = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); if (!ch_attr || !peer_attr) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); return PJ_SUCCESS; } /* Find permission with the channel number */ p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value)); /* If permission is found, this is supposed to be a channel bind * refresh. Make sure it's for the same peer. */ if (p1) { if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) { /* Address mismatch. Send 400 */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address mismatch"); return PJ_SUCCESS; } /* Refresh permission */ refresh_permission(p1); /* Send response */ send_reply_ok(alloc, rdata); /* Done */ return PJ_SUCCESS; } /* If permission is not found, create a new one. Make sure the peer * has not alreadyy assigned with a channel number. */ p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address already assigned a channel number"); return PJ_SUCCESS; } /* Create permission if it doesn't exist */ if (!p2) { p2 = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (!p2) return PJ_SUCCESS; } /* Assign channel number to permission */ p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value); /* Register to hash table */ pj_assert(sizeof(p2->channel)==2); pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel, sizeof(p2->channel), 0, p2); /* Update */ refresh_permission(p2); /* Reply */ send_reply_ok(alloc, rdata); return PJ_SUCCESS; } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { /* Respond with 437 (section 6.3 turn-07) */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); } else { /* Respond with Bad Request? */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); } return PJ_SUCCESS; }
/* * Callback upon request completion. */ static void on_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { nat_detect_session *sess; pj_stun_sockaddr_attr *mattr = NULL; pj_stun_changed_addr_attr *ca = NULL; pj_uint32_t *tsx_id; int cmp; unsigned test_id; PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pj_grp_lock_acquire(sess->grp_lock); /* Find errors in the response */ if (status == PJ_SUCCESS) { /* Check error message */ if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { pj_stun_errcode_attr *eattr; int err_code; eattr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr != NULL) err_code = eattr->err_code; else err_code = PJ_STUN_SC_SERVER_ERROR; status = PJ_STATUS_FROM_STUN_CODE(err_code); } else { /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */ mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mattr == NULL) { mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mattr == NULL) { status = PJNATH_ESTUNNOMAPPEDADDR; } /* Get CHANGED-ADDRESS attribute */ ca = (pj_stun_changed_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); if (ca == NULL) { status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR); } } } /* Save the result */ tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id; test_id = tsx_id[2]; if (test_id >= ST_MAX) { PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response", test_id)); end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR), PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d", test_names[test_id], status)); sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; if (status == PJ_SUCCESS) { pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4, sizeof(pj_sockaddr_in)); pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4, sizeof(pj_sockaddr_in)); } /* Send Test 1B only when Test 2 completes. Must not send Test 1B * before Test 2 completes to avoid creating mapping on the NAT. */ if (!sess->result[ST_TEST_1B].executed && sess->result[ST_TEST_2].complete && sess->result[ST_TEST_2].status != PJ_SUCCESS && sess->result[ST_TEST_1].complete && sess->result[ST_TEST_1].status == PJ_SUCCESS) { cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0); } if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess)) goto on_return; /* Handle the test result according to RFC 3489 page 22: +--------+ | Test | | 1 | +--------+ | | V /\ /\ N / \ Y / \ Y +--------+ UDP <-------/Resp\--------->/ IP \------------->| Test | Blocked \ ? / \Same/ | 2 | \ / \? / +--------+ \/ \/ | | N | | V V /\ +--------+ Sym. N / \ | Test | UDP <---/Resp\ | 2 | Firewall \ ? / +--------+ \ / | \/ V |Y /\ /\ | Symmetric N / \ +--------+ N / \ V NAT <--- / IP \<-----| Test |<--- /Resp\ Open \Same/ | 1B | \ ? / Internet \? / +--------+ \ / \/ \/ | |Y | | | V | Full | Cone V /\ +--------+ / \ Y | Test |------>/Resp\---->Restricted | 3 | \ ? / +--------+ \ / \/ |N | Port +------>Restricted Figure 2: Flow for type discovery process */ switch (sess->result[ST_TEST_1].status) { case PJNATH_ESTUNTIMEDOUT: /* * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); break; case PJ_SUCCESS: /* * Test 1 is successful. Further tests are needed to detect * NAT type. Compare the MAPPED-ADDRESS with the local address. */ cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp==0) { /* * MAPPED-ADDRESS and local address is equal. Need one more * test to determine NAT type. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is also successful. We're in the open. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed out. We're behind somekind of UDP * firewall. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP); break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } else { /* * MAPPED-ADDRESS is different than local address. * We're behind NAT. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is successful. We're behind a full-cone NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed-out Check result of test 1B.. */ switch (sess->result[ST_TEST_1B].status) { case PJ_SUCCESS: /* * Compare the MAPPED-ADDRESS of test 1B with the * MAPPED-ADDRESS returned in test 1.. */ cmp = pj_memcmp(&sess->result[ST_TEST_1].ma, &sess->result[ST_TEST_1B].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) { /* * MAPPED-ADDRESS is different, we're behind a * symmetric NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC); } else { /* * MAPPED-ADDRESS is equal. We're behind a restricted * or port-restricted NAT, depending on the result of * test 3. */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* * Test 3 is successful, we're behind a restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 3 failed, we're behind a port restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_PORT_RESTRICTED); break; default: /* * Got other error with test 3. */ end_session(sess, sess->result[ST_TEST_3].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; case PJNATH_ESTUNTIMEDOUT: /* * Strangely test 1B has failed. Maybe connectivity was * lost? Or perhaps port 3489 (the usual port number in * CHANGED-ADDRESS) is blocked? */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* Although test 1B failed, test 3 was successful. * It could be that port 3489 is blocked, while the * NAT itself looks to be a Restricted one. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; default: /* Can't distinguish between Symmetric and Port * Restricted, so set the type to Unknown */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * Got other error with test 1B. */ end_session(sess, sess->result[ST_TEST_1B].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; default: /* * We've got other error with Test 1. */ end_session(sess, sess->result[ST_TEST_1].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } on_return: pj_grp_lock_release(sess->grp_lock); }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(4,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock) return; PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(2,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) { op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; PJ_LOG(2, (THIS_FILE, "sess_on_rquest_complete() Operation is PJ_STUN_SOCK_MAPPED_ADDR_CHANGE.")); } } // 2013-10-16 DEAN // 2013-10-21 DEAN { int addr_len = sizeof(stun_sock->current_local_addr); char addrinfo1[PJ_INET6_ADDRSTRLEN+10]; char addrinfo2[PJ_INET6_ADDRSTRLEN+10]; pj_sock_getsockname(stun_sock->sock_fd, &stun_sock->current_local_addr, &addr_len); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo1, sizeof(addrinfo1), 3))); /* * Find out which interface is used to send to the server. */ status = get_local_interface(&stun_sock->srv_addr, &((pj_sockaddr_in *)(&stun_sock->current_local_addr))->sin_addr); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo2, sizeof(addrinfo2), 3))); } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); PJ_LOG(5, (THIS_FILE, "sess_on_request_complete() resched=%d.", resched)); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }