/* Send application data */ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, const void *pkt, unsigned pkt_len, unsigned flag, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_ssize_t size; pj_status_t status; pj_bool_t is_stun = PJ_FALSE; pj_bool_t is_tnl_data = PJ_FALSE; PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); pj_grp_lock_acquire(stun_sock->grp_lock); if (!stun_sock->active_sock) { /* We have been shutdown, but this callback may still get called * by retransmit timer. */ pj_grp_lock_release(stun_sock->grp_lock); return PJ_EINVALIDOP; } if (send_key==NULL) send_key = &stun_sock->send_key; size = pkt_len; if (!stun_sock || !stun_sock->active_sock) return PJ_EINVALIDOP; /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)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_len] == 1); 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)); status = pj_activesock_sendto(stun_sock->active_sock, op_key, pkt, &size, (flag | PJ_IOQUEUE_URGENT_DATA), dst_addr, addr_len); } else { status = pj_activesock_sendto(stun_sock->active_sock, &stun_sock->send_key, pkt, &size, flag, dst_addr, addr_len); } pj_grp_lock_release(stun_sock->grp_lock); return status; }
/* * Callback from TURN session to send outgoing packet. */ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_ssize_t len = pkt_len; pj_status_t status; pj_bool_t is_stun = PJ_FALSE; pj_bool_t is_tnl_data = PJ_FALSE; if (turn_sock == NULL) { /* We've been destroyed */ // https://trac.pjsip.org/repos/ticket/1316 //pj_assert(!"We should shutdown gracefully"); return PJ_EINVALIDOP; } PJ_UNUSED_ARG(dst_addr); PJ_UNUSED_ARG(dst_addr_len); /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_len, 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_len] == 1); if(is_stun || !is_tnl_data) { pj_ioqueue_op_key_t *op_key = (pj_ioqueue_op_key_t*)malloc(sizeof(pj_ioqueue_op_key_t)); pj_ioqueue_op_key_init(op_key, sizeof(pj_ioqueue_op_key_t)); status = pj_activesock_send(turn_sock->active_sock, op_key, pkt, &len, PJ_IOQUEUE_URGENT_DATA); } else { status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key, pkt, &len, 0); } if (status != PJ_SUCCESS && status != PJ_EPENDING) { show_err(turn_sock, "socket send()", status); } return status; }
/* 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); } }
static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { test_server *test_srv; pj_pool_t *pool; turn_allocation *alloc; pj_stun_msg *req, *resp = NULL; pj_str_t auth_key = { NULL, 0 }; char client_info[PJ_INET6_ADDRSTRLEN+10]; unsigned i; pj_ssize_t len; if (status != PJ_SUCCESS) return PJ_TRUE; pj_sockaddr_print(src_addr, client_info, sizeof(client_info), 3); test_srv = (test_server*) pj_activesock_get_user_data(asock); pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL); /* Find the client */ for (i=0; i<test_srv->turn_alloc_cnt; i++) { if (pj_sockaddr_cmp(&test_srv->turn_alloc[i].client_addr, src_addr)==0) break; } if (pj_stun_msg_check((pj_uint8_t*)data, size, PJ_STUN_NO_FINGERPRINT_CHECK)!=PJ_SUCCESS) { /* Not STUN message, this probably is a ChannelData */ pj_turn_channel_data cd; const pj_turn_channel_data *pcd = (const pj_turn_channel_data*)data; pj_ssize_t sent; if (i==test_srv->turn_alloc_cnt) { /* Invalid data */ PJ_LOG(1,(THIS_FILE, "TURN Server received strayed data")); goto on_return; } alloc = &test_srv->turn_alloc[i]; cd.ch_number = pj_ntohs(pcd->ch_number); cd.length = pj_ntohs(pcd->length); /* For UDP check the packet length */ if (size < cd.length+sizeof(cd)) { PJ_LOG(1,(THIS_FILE, "TURN Server: ChannelData discarded: UDP size error")); goto on_return; } /* Lookup peer */ for (i=0; i<alloc->perm_cnt; ++i) { if (alloc->chnum[i] == cd.ch_number) break; } if (i==alloc->perm_cnt) { PJ_LOG(1,(THIS_FILE, "TURN Server: ChannelData discarded: invalid channel number")); goto on_return; } /* Relay the data to peer */ sent = cd.length; pj_activesock_sendto(alloc->sock, &alloc->send_key, pcd+1, &sent, 0, &alloc->perm[i], pj_sockaddr_get_len(&alloc->perm[i])); /* Done */ goto on_return; } status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK, &req, NULL, NULL); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,("", "STUN message decode error from client %s: %s", client_info, errmsg)); goto on_return; } if (i==test_srv->turn_alloc_cnt) { /* New client */ //pj_str_t ip_addr; pj_stun_username_attr *uname; pj_activesock_cb alloc_sock_cb; turn_allocation *alloc; /* Must be Allocate request */ if (req->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { PJ_LOG(1,(THIS_FILE, "Invalid %s %s from client %s", pj_stun_get_method_name(req->hdr.type), pj_stun_get_class_name(req->hdr.type), client_info)); if (PJ_STUN_IS_REQUEST(req->hdr.type)) pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp); goto send_pkt; } test_srv->turn_stat.rx_allocate_cnt++; /* Skip if we're not responding to Allocate request */ if (!test_srv->turn_respond_allocate) return PJ_TRUE; /* Check if we have too many clients */ if (test_srv->turn_alloc_cnt == MAX_TURN_ALLOC) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp); goto send_pkt; } /* Get USERNAME attribute */ uname = (pj_stun_username_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0); /* Reject if it doesn't have MESSAGE-INTEGRITY or USERNAME attributes or * the user is incorrect */ if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0) == NULL || uname==NULL || pj_stricmp2(&uname->value, TURN_USERNAME) != 0) { pj_str_t tmp; pj_stun_msg_create_response(pool, req, PJ_STUN_SC_UNAUTHORIZED, NULL, &resp); pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_REALM, &test_srv->domain); pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE)); goto send_pkt; } pj_bzero(&alloc_sock_cb, sizeof(alloc_sock_cb)); alloc_sock_cb.on_data_recvfrom = &alloc_on_data_recvfrom; /* Create allocation */ alloc = &test_srv->turn_alloc[test_srv->turn_alloc_cnt]; alloc->perm_cnt = 0; alloc->test_srv = test_srv; pj_memcpy(&alloc->client_addr, src_addr, addr_len); pj_ioqueue_op_key_init(&alloc->send_key, sizeof(alloc->send_key)); alloc->pool = pj_pool_create(test_srv->stun_cfg->pf, "alloc", 512, 512, NULL); /* Create relay socket */ pj_sockaddr_in_init(&alloc->alloc_addr.ipv4, NULL, 0); pj_gethostip(pj_AF_INET(), &alloc->alloc_addr); status = pj_activesock_create_udp(alloc->pool, &alloc->alloc_addr, NULL, test_srv->stun_cfg->ioqueue, &alloc_sock_cb, alloc, &alloc->sock, &alloc->alloc_addr); if (status != PJ_SUCCESS) { pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } //pj_sockaddr_set_str_addr(pj_AF_INET(), &alloc->alloc_addr, &ip_addr); pj_activesock_set_user_data(alloc->sock, alloc); status = pj_activesock_start_recvfrom(alloc->sock, alloc->pool, 1500, 0); if (status != PJ_SUCCESS) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } /* Create Data indication */ status = pj_stun_msg_create(alloc->pool, PJ_STUN_DATA_INDICATION, PJ_STUN_MAGIC, NULL, &alloc->data_ind); if (status != PJ_SUCCESS) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } pj_stun_msg_add_sockaddr_attr(alloc->pool, alloc->data_ind, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, &alloc->alloc_addr, pj_sockaddr_get_len(&alloc->alloc_addr)); pj_stun_msg_add_binary_attr(alloc->pool, alloc->data_ind, PJ_STUN_ATTR_DATA, (pj_uint8_t*)"", 1); /* Create response */ resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key); if (resp == NULL) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } ++test_srv->turn_alloc_cnt; } else { alloc = &test_srv->turn_alloc[i]; if (req->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { test_srv->turn_stat.rx_allocate_cnt++; /* Skip if we're not responding to Allocate request */ if (!test_srv->turn_respond_allocate) return PJ_TRUE; resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_REFRESH_REQUEST) { pj_stun_lifetime_attr *lf_attr; test_srv->turn_stat.rx_refresh_cnt++; /* Skip if we're not responding to Refresh request */ if (!test_srv->turn_respond_refresh) return PJ_TRUE; lf_attr = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0); if (lf_attr && lf_attr->value != 0) { resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key); pj_array_erase(test_srv->turn_alloc, sizeof(test_srv->turn_alloc[0]), test_srv->turn_alloc_cnt, i); --test_srv->turn_alloc_cnt; } else resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_CREATE_PERM_REQUEST) { for (i=0; i<req->attr_count; ++i) { if (req->attr[i]->type == PJ_STUN_ATTR_XOR_PEER_ADDR) { pj_stun_xor_peer_addr_attr *pa = (pj_stun_xor_peer_addr_attr*)req->attr[i]; unsigned j; for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (j==alloc->perm_cnt && alloc->perm_cnt < MAX_TURN_PERM) { char peer_info[PJ_INET6_ADDRSTRLEN]; pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3); pj_sockaddr_cp(&alloc->perm[alloc->perm_cnt], &pa->sockaddr); ++alloc->perm_cnt; PJ_LOG(5,("", "Permission %s added to client %s, perm_cnt=%d", peer_info, client_info, alloc->perm_cnt)); } } } resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_SEND_INDICATION) { pj_stun_xor_peer_addr_attr *pa; pj_stun_data_attr *da; test_srv->turn_stat.rx_send_ind_cnt++; pa = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); da = (pj_stun_data_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_DATA, 0); if (pa && da) { unsigned j; char peer_info[PJ_INET6_ADDRSTRLEN]; pj_ssize_t sent; pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3); for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (j==alloc->perm_cnt) { PJ_LOG(5,("", "SendIndication to %s is rejected (no permission)", peer_info, client_info, alloc->perm_cnt)); } else { PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s, " "perm_cnt=%d", da->length, client_info, peer_info, alloc->perm_cnt)); sent = da->length; pj_activesock_sendto(alloc->sock, &alloc->send_key, da->data, &sent, 0, &pa->sockaddr, pj_sockaddr_get_len(&pa->sockaddr)); } } else { PJ_LOG(1,(THIS_FILE, "Invalid Send Indication from %s", client_info)); } } else if (req->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { pj_stun_xor_peer_addr_attr *pa; pj_stun_channel_number_attr *cna; unsigned j, cn; pa = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); cna = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); cn = PJ_STUN_GET_CH_NB(cna->value); resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (i==alloc->perm_cnt) { if (alloc->perm_cnt==MAX_TURN_PERM) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp); goto send_pkt; } pj_sockaddr_cp(&alloc->perm[i], &pa->sockaddr); ++alloc->perm_cnt; } alloc->chnum[i] = cn; resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (PJ_STUN_IS_REQUEST(req->hdr.type)) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp); } } send_pkt: if (resp) { status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 0, &auth_key, &size); if (status != PJ_SUCCESS) goto on_return; len = size; status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len, 0, src_addr, addr_len); } on_return: pj_pool_release(pool); return PJ_TRUE; }
/* Callback from active socket when incoming packet is received */ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { pj_stun_sock *stun_sock; pj_stun_msg_hdr *hdr; pj_uint16_t type; stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); /* Log socket error */ if (status != PJ_SUCCESS) { PJ_PERROR(2,(stun_sock->obj_name, status, "recvfrom() error")); return PJ_TRUE; } /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); if (status != PJ_SUCCESS) { /* Not STUN -- give it to application */ goto process_app_data; } /* Treat packet as STUN header and copy the STUN message type. * We don't want to access the type directly from the header * since it may not be properly aligned. */ hdr = (pj_stun_msg_hdr*) data; pj_memcpy(&type, &hdr->type, 2); type = pj_ntohs(type); /* If the packet is a STUN Binding response and part of the * transaction ID matches our internal ID, then this is * our internal STUN message (Binding request or keep alive). * Give it to our STUN session. */ if (!PJ_STUN_IS_RESPONSE(type) || PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) { /* Not STUN Binding response, or STUN transaction ID mismatch. * This is not our message too -- give it to application. */ goto process_app_data; } /* This is our STUN Binding response. Give it to the STUN session */ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, PJ_STUN_IS_DATAGRAM, NULL, NULL, src_addr, addr_len); return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE; process_app_data: if (stun_sock->cb.on_rx_data) { pj_bool_t ret; ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size, src_addr, addr_len); return ret; } return PJ_TRUE; }
/* * This callback is called by UDP listener on incoming packet. This is * the first entry for incoming packet (from client) to the server. From * here, the packet may be handed over to an allocation if an allocation * is found for the client address, or handed over to owned STUN session * if an allocation is not found. */ PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, pj_turn_pkt *pkt) { pj_turn_allocation *alloc; /* Get TURN allocation from the source address */ pj_lock_acquire(srv->core.lock); alloc = (pj_turn_allocation*) pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL); pj_lock_release(srv->core.lock); /* If allocation is found, just hand over the packet to the * allocation. */ if (alloc) { pj_turn_allocation_on_rx_client_pkt(alloc, pkt); } else { /* Otherwise this is a new client */ unsigned options; pj_size_t parsed_len; pj_status_t status; /* Check that this is a STUN message */ options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_msg_check(pkt->pkt, pkt->len, options); if (status != PJ_SUCCESS) { /* If the first byte are not STUN, drop the packet. First byte * of STUN message is always 0x00 or 0x01. Otherwise wait for * more data as the data might have come from TCP. * * Also drop packet if it's unreasonably too big, as this might * indicate invalid data that's building up in the buffer. * * Or if packet is a datagram. */ if ((*pkt->pkt != 0x00 && *pkt->pkt != 0x01) || pkt->len > 1600 || (options & PJ_STUN_IS_DATAGRAM)) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pkt->len = 0; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Non-STUN packet from %s is dropped: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } return; } /* Special handling for Binding Request. We won't give it to the * STUN session since this request is not authenticated. */ if (pkt->pkt[1] == 1) { handle_binding_request(pkt, options); return; } /* Hand over processing to STUN session. This will trigger * on_rx_stun_request() callback to be called if the STUN * message is a request. */ options &= ~PJ_STUN_CHECK_PACKET; parsed_len = 0; status = pj_stun_session_on_rx_pkt(srv->core.stun_sess, pkt->pkt, pkt->len, options, pkt->transport, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Error processing STUN packet from %s: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } } }
/** * Notify TCP client session upon receiving a packet from server. * The packet maybe a STUN packet or ChannelData packet. */ PJ_DEF(pj_status_t) pj_tcp_session_on_rx_pkt(pj_tcp_session *sess, void *pkt, unsigned pkt_len, pj_size_t *parsed_len) { pj_bool_t is_stun = PJ_FALSE; pj_status_t stun_check; pj_status_t status; char buf[PJ_INET6_ADDRSTRLEN+20]; pj_uint16_t data_len; /* Packet could be ChannelData or STUN message (response or * indication). */ /* Start locking the session */ pj_lock_acquire(sess->lock); /* Quickly check if this is STUN message */ //is_stun = (((pj_uint16_t*)pkt)[0] != NATNL_UDT_HEADER_MAGIC); stun_check = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_len, /*PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET*/ 0); if (stun_check == PJ_SUCCESS) is_stun = PJ_TRUE; if (is_stun) { #if 1 /* This looks like STUN, give it to the STUN session */ unsigned options; pj_size_t msg_len; options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; msg_len = GETVAL16H((const pj_uint8_t*)pkt, 2); *parsed_len = (msg_len+sizeof(pj_stun_msg_hdr) <= pkt_len) ? msg_len+sizeof(struct pj_stun_msg_hdr) : 0; /* Notify application */ if (sess->cb.on_rx_data) { (*sess->cb.on_rx_data)(sess, pkt, pkt_len, sess->peer_addr, sess->peer_addr_len); } //DumpPacket(pkt, pkt_len, 0, 5); status = PJ_SUCCESS; #else /* This looks like STUN, give it to the STUN session */ unsigned options; options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, options, NULL, parsed_len, sess->peer_addr, sess->peer_addr_len); #endif PJ_LOG(5, (THIS_FILE, "pj_tcp_session_on_rx_pkt() is_stun %s", sess->peer_addr == NULL ? "NULL" : pj_sockaddr_print(sess->peer_addr, buf, PJ_INET6_ADDRSTRLEN, 3))); } else { PJ_LOG(5, (THIS_FILE, "pj_tcp_session_on_rx_pkt() not_stun %s", sess->peer_addr == NULL ? "NULL" : pj_sockaddr_print(sess->peer_addr, buf, PJ_INET6_ADDRSTRLEN, 3))); //DumpPacket(pkt, pkt_len, 0, 7); if (((pj_uint16_t*)pkt)[0] != NATNL_UDT_HEADER_MAGIC) { if (pkt_len < NATNL_DTLS_HEADER_SIZE) { if (parsed_len) { *parsed_len = 0; } return PJ_ETOOSMALL; } /* Check that size is sane */ data_len = pj_ntohs(((pj_uint16_t*)(((pj_uint8_t*)pkt)+11))[0]); if (pkt_len < data_len+NATNL_DTLS_HEADER_SIZE) { if (parsed_len) { /* Insufficient fragment */ *parsed_len = 0; } status = PJ_ETOOSMALL; goto on_return; } else { if (parsed_len) { /* Apply padding too */ *parsed_len = data_len+NATNL_DTLS_HEADER_SIZE; } } } else { if (pkt_len < NATNL_UDT_HEADER_SIZE) { if (parsed_len) { *parsed_len = 0; } return PJ_ETOOSMALL; } /* Check that size is sane */ data_len = pj_ntohs(((pj_uint16_t*)pkt)[1]); if (pkt_len < data_len+NATNL_UDT_HEADER_SIZE) { if (parsed_len) { /* Insufficient fragment */ *parsed_len = 0; } status = PJ_ETOOSMALL; goto on_return; } else { if (parsed_len) { /* Apply padding too */ *parsed_len = data_len+NATNL_UDT_HEADER_SIZE; } } } /* Notify application */ if (sess->cb.on_rx_data) { (*sess->cb.on_rx_data)(sess, pkt, *parsed_len, sess->peer_addr, sess->peer_addr_len); } //DumpPacket(pkt, pkt_len, 0, 6); status = PJ_SUCCESS; } on_return: pj_lock_release(sess->lock); return status; }