/* Quick check to determine if there is enough packet to process in the * incoming buffer. Return the packet length, or zero if there's no packet. */ static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t bufsize) { pj_bool_t is_stun; if (turn_sock->conn_type == PJ_TURN_TP_UDP) return (unsigned)bufsize; /* Quickly check if this is STUN message, by checking the first two bits and * size field which must be multiple of 4 bytes */ is_stun = ((((pj_uint8_t*)buf)[0] & 0xC0) == 0) && ((GETVAL16H((const pj_uint8_t*)buf, 2) & 0x03)==0); if (is_stun) { pj_size_t msg_len = GETVAL16H((const pj_uint8_t*)buf, 2); return (unsigned)((msg_len+20 <= bufsize) ? msg_len+20 : 0); } else { /* This must be ChannelData. */ pj_turn_channel_data cd; if (bufsize < 4) return 0; /* Decode ChannelData packet */ pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data)); cd.length = pj_ntohs(cd.length); if (bufsize >= cd.length+sizeof(cd)) return (cd.length+sizeof(cd)+3) & (~3); else return 0; } }
/** * 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; }