/* DNS resolver callback */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { pj_stun_sock *stun_sock = (pj_stun_sock*) user_data; pj_grp_lock_acquire(stun_sock->grp_lock); /* Clear query */ stun_sock->q = NULL; /* Handle error */ if (status != PJ_SUCCESS) { sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status); pj_grp_lock_release(stun_sock->grp_lock); return; } pj_assert(rec->count); pj_assert(rec->entry[0].server.addr_count); PJ_TODO(SUPPORT_IPV6_IN_RESOLVER); pj_assert(stun_sock->af == pj_AF_INET()); /* Set the address */ pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL, rec->entry[0].port); stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0]; /* Start sending Binding request */ get_mapped_addr(stun_sock); pj_grp_lock_release(stun_sock->grp_lock); }
/* Start sending STUN Binding request */ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) { pj_stun_tx_data *tdata; pj_status_t status; /* Increment request counter and create STUN Binding request */ ++stun_sock->tsx_id[5]; status = pj_stun_session_create_req(stun_sock->stun_sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, (const pj_uint8_t*)stun_sock->tsx_id, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Send request */ status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, pj_sockaddr_get_len(&stun_sock->srv_addr), tdata); if (status != PJ_SUCCESS && status != PJ_EPENDING) goto on_error; return PJ_SUCCESS; on_error: sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status); return status; }
/* * Initialize. */ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver, const pj_stun_auth_cred *cred, const pj_turn_alloc_param *param) { pj_status_t status; PJ_ASSERT_RETURN(turn_sock && domain, PJ_EINVAL); PJ_ASSERT_RETURN(turn_sock->sess, PJ_EINVALIDOP); /* Copy alloc param. We will call session_alloc() only after the * server address has been resolved. */ if (param) { pj_turn_alloc_param_copy(turn_sock->pool, &turn_sock->alloc_param, param); } else { pj_turn_alloc_param_default(&turn_sock->alloc_param); } /* Set credental */ if (cred) { status = pj_turn_session_set_credential(turn_sock->sess, cred); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error setting credential", status); return status; } } /* Resolve server */ status = pj_turn_session_set_server(turn_sock->sess, domain, default_port, resolver); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error setting TURN server", status); return status; } /* Done for now. The next work will be done when session state moved * to RESOLVED state. */ return PJ_SUCCESS; }
/* * Notification when outgoing TCP socket has been connected. */ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { pj_turn_sock *turn_sock; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); if (!turn_sock) return PJ_FALSE; /* TURN session may have already been destroyed here. * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). */ if (!turn_sock->sess) { sess_fail(turn_sock, "TURN session already destroyed", status); return PJ_FALSE; } if (status != PJ_SUCCESS) { sess_fail(turn_sock, "TCP connect() error", status); return PJ_FALSE; } if (turn_sock->conn_type != PJ_TURN_TP_UDP) { PJ_LOG(5,(turn_sock->obj_name, "TCP connected")); } /* Kick start pending read operation */ status = pj_activesock_start_read(asock, turn_sock->pool, turn_sock->setting.max_pkt_size, 0); /* Init send_key */ pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key)); /* Send Allocate request */ status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error sending ALLOCATE", status); return PJ_FALSE; } return PJ_TRUE; }
/* * Notification from ioqueue when incoming UDP packet is received. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_turn_sock *turn_sock; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_grp_lock_acquire(turn_sock->grp_lock); if (status == PJ_SUCCESS && turn_sock->sess && !turn_sock->is_destroying) { /* Report incoming packet to TURN session, repeat while we have * "packet" in the buffer (required for stream-oriented transports) */ unsigned pkt_len; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); parsed_len = (unsigned)size; pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); /* parsed_len may be zero if we have parsing error, so use our * previous calculation to exhaust the bad packet. */ if (parsed_len == 0) parsed_len = pkt_len; if (parsed_len < (unsigned)size) { *remainder = size - parsed_len; pj_memmove(data, ((char*)data)+parsed_len, *remainder); } else { *remainder = 0; } size = *remainder; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) { sess_fail(turn_sock, "TCP connection closed", status); ret = PJ_FALSE; goto on_return; } on_return: pj_grp_lock_release(turn_sock->grp_lock); return ret; }
/* * Notification from ioqueue when incoming UDP packet is received. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_turn_sock *turn_sock; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_lock_acquire(turn_sock->lock); if (status == PJ_SUCCESS && turn_sock->sess) { /* Report incoming packet to TURN session, repeat while we have * "packet" in the buffer (required for stream-oriented transports) */ unsigned pkt_len; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); parsed_len = (unsigned)size; pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); /* parsed_len may be zero if we have parsing error, so use our * previous calculation to exhaust the bad packet. */ if (parsed_len == 0) parsed_len = pkt_len; if (parsed_len < (unsigned)size) { *remainder = size - parsed_len; pj_memmove(data, ((char*)data)+parsed_len, *remainder); } else { *remainder = 0; } size = *remainder; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } /* Assigned remainder as size. Because ioqueue may skip the packet if never enter while loop. */ if (pkt_len == 0) *remainder = size; PJ_LOG(5, (__FILE__, "on_data_read() leaving still remainder=[%d].", *remainder)); } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) { // DEAN don't destroy TURN session, if connection aborted. // To avoid the situation of ip changing caused crash. if (status != 130053) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_data_read() read failed status=%d", status)); sess_fail(turn_sock, "TURN TCP connection closed", status); } ret = PJ_FALSE; goto on_return; } on_return: pj_lock_release(turn_sock->lock); return ret; }
/* * Notification when outgoing TCP socket has been connected. */ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { pj_turn_sock *turn_sock; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); if (!turn_sock) return PJ_FALSE; /* TURN session may have already been destroyed here. * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). */ if (!turn_sock->sess) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_connect_complete() turn_sock->sess is NULL status=%d", status)); sess_fail(turn_sock, "TURN session already destroyed", status); return PJ_FALSE; } if (status != PJ_SUCCESS) { // DEAN assigned next turn server if (turn_sock->turn_cnt <= ++turn_sock->curr_turn) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_connect_complete() turn_sock->turn_cnt <= ++turn_sock->curr_turn (%d,%d)", turn_sock->turn_cnt, turn_sock->curr_turn)); sess_fail(turn_sock, "TCP connect() error", status); turn_sock->curr_turn = 0; PJ_LOG(3, (__FILE__, "Failed(%d) connect to all turn servers.", status)); } else { set_state(turn_sock->sess, PJ_TURN_STATE_NULL); PJ_LOG(3, (__FILE__, "Failed(%d) connect to turn server [%.*s:%d].", status, turn_sock->turn.domain->slen, turn_sock->turn.domain->ptr, turn_sock->turn.default_port)); turn_sock->turn.domain = &turn_sock->turn_list[turn_sock->curr_turn].server; turn_sock->turn.default_port = turn_sock->turn_list[turn_sock->curr_turn].port; PJ_LOG(3, (__FILE__, "Try another turn server [%.*s:%d].", turn_sock->turn.domain->slen, turn_sock->turn.domain->ptr, turn_sock->turn.default_port)); /* Resolve server */ status = pj_turn_session_set_server(turn_sock->sess, turn_sock->turn.domain, turn_sock->turn.default_port, turn_sock->turn.resolver); if (status != PJ_SUCCESS) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_connect_complete() pj_turn_session_set_server failed status=%d", status)); sess_fail(turn_sock, "Error setting TURN server", status); return status; } } return PJ_FALSE; } if (turn_sock->conn_type != PJ_TURN_TP_UDP) { PJ_LOG(5,(turn_sock->obj_name, "TCP connected")); } /* Kick start pending read operation */ status = pj_activesock_start_read(asock, turn_sock->pool, PJ_TURN_MAX_PKT_LEN, 0); /* Init send_key */ pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key)); /* Send Allocate request */ status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param); if (status != PJ_SUCCESS) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_connect_complete() pj_turn_session_alloc failed status=%d", status)); sess_fail(turn_sock, "Error sending ALLOCATE", status); return PJ_FALSE; } return PJ_TRUE; }
/* * 2013-05-08 DEAN modifedf by adding parameters. * Initialize. */ PJ_DEF(pj_status_t) pj_turn_sock_alloc2(pj_turn_sock *turn_sock, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver, const pj_stun_auth_cred *cred, const pj_turn_alloc_param *param, int curr_turn, int turn_cnt, pj_turn_server turn_list[]) { pj_status_t status; PJ_ASSERT_RETURN(turn_sock && domain && turn_cnt <= MAX_TURN_SERVER_COUNT, PJ_EINVAL); PJ_ASSERT_RETURN(turn_sock->sess, PJ_EINVALIDOP); /* Copy alloc param. We will call session_alloc() only after the * server address has been resolved. */ if (param) { pj_turn_alloc_param_copy(turn_sock->pool, &turn_sock->alloc_param, param); } else { pj_turn_alloc_param_default(&turn_sock->alloc_param); } /* Set credential */ if (cred) { PJ_LOG(4, (THIS_FILE, "pj_turn_sock_alloc2() turn tp type=%d", turn_sock->conn_type)); PJ_LOG(4, (THIS_FILE, "pj_turn_sock_alloc2() turn server=%.*s:%d", domain->slen, domain->ptr, default_port)); PJ_LOG(4, (THIS_FILE, "pj_turn_sock_alloc2() turn realm=%.*s", cred->data.static_cred.realm.slen, cred->data.static_cred.realm.ptr)); PJ_LOG(4, (THIS_FILE, "pj_turn_sock_alloc2() turn username=%.*s", cred->data.static_cred.username.slen, cred->data.static_cred.username.ptr)); PJ_LOG(4, (THIS_FILE, "pj_turn_sock_alloc2() turn password=%.*s", cred->data.static_cred.data.slen, cred->data.static_cred.data.ptr)); status = pj_turn_session_set_credential(turn_sock->sess, cred); if (status != PJ_SUCCESS) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in pj_turn_sock_alloc2() pj_turn_session_set_credential failed=%d", status)); sess_fail(turn_sock, "Error setting credential", status); return status; } } // 2014-04-19 DEAN, for retry another turn use turn_sock->turn.domain = domain; turn_sock->turn.default_port = default_port; turn_sock->turn.resolver = resolver; turn_sock->curr_turn = curr_turn; turn_sock->turn_cnt = turn_cnt; memcpy(turn_sock->turn_list, turn_list, sizeof(pj_turn_server)*turn_cnt); /* Resolve server */ status = pj_turn_session_set_server(turn_sock->sess, domain, default_port, resolver); if (status != PJ_SUCCESS) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in pj_turn_sock_alloc2() pj_turn_session_set_server failed=%d", status)); sess_fail(turn_sock, "Error setting TURN server", status); return status; } /* Done for now. The next work will be done when session state moved * to RESOLVED state. */ return PJ_SUCCESS; }
/* 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); }