#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://doubango.org> * * This file is part of Open Source Doubango Framework. * * DOUBANGO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DOUBANGO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DOUBANGO. * */ #include "stun/tnet_stun_utils.h" #include "stun/tnet_stun_pkt.h" #include "stun/tnet_stun_attr.h" #include "tnet_utils.h" #include "tsk_memory.h" #include "tsk_debug.h" int tnet_stun_utils_inet_pton(tsk_bool_t b_v6, const char* p_src, tnet_stun_addr_t* p_dst) { int ret; if (!p_src || !p_dst) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if ((ret = tnet_inet_pton(b_v6 ? AF_INET6 : AF_INET, p_src, *p_dst) != 1)) { // success == 1 TSK_DEBUG_ERROR("tnet_inet_pton() with error code = %d", ret); return -3; } return 0; } int tnet_stun_utils_inet_ntop(tsk_bool_t b_v6, const tnet_stun_addr_t* pc_src, tnet_ip_t* p_dst) { if (!pc_src || !p_dst) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (tnet_inet_ntop(b_v6 ? AF_INET6 : AF_INET, *pc_src, *p_dst, sizeof(*p_dst)) == tsk_null) { TSK_DEBUG_ERROR("tnet_inet_ntop() failed"); return -2; } return 0; } int tnet_stun_utils_transac_id_rand(tnet_stun_transac_id_t* p_transac_id) { tsk_size_t u; static tsk_size_t __u_size = sizeof(tnet_stun_transac_id_t); static long __l_chan_num = 0; tsk_atomic_inc(&__l_chan_num); if (!p_transac_id) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } for (u = 0; (u < __u_size) && (u < sizeof(long)); ++u) { *(((uint8_t*)p_transac_id) + u) = (__l_chan_num >> (u << 3)) & 0xFF; } for (u = sizeof(long); u < __u_size; ++u) { *(((uint8_t*)p_transac_id) + u) = rand() % 0xFF; } return 0; } int tnet_stun_utils_buff_cmp(const uint8_t* pc_buf1_ptr, tsk_size_t n_buff1_size, const uint8_t* pc_buf2_ptr, tsk_size_t n_buff2_size) { int ret; tsk_size_t u; if (!pc_buf1_ptr || !pc_buf2_ptr || (n_buff1_size != n_buff2_size)) { return -1; } for (u = 0; u < n_buff1_size; ++u) { if ((ret = (pc_buf1_ptr[u] - pc_buf2_ptr[u]))) { return ret; } } return 0; } int tnet_stun_utils_send_unreliably(tnet_fd_t localFD, uint16_t RTO, uint16_t Rc, const struct tnet_stun_pkt_s* pc_stun_req, struct sockaddr* p_addr_server, struct tnet_stun_pkt_s** pp_stun_resp) { int ret = -1; uint16_t i, rto = RTO; struct timeval tv; fd_set set; void* p_buff_ptr = tsk_null; tsk_size_t u_buff_size; if (!pc_stun_req || !p_addr_server || !pp_stun_resp) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } /* RFC 5389 - 7.2.1. Sending over UDP STUN indications are not retransmitted; thus, indication transactions over UDP are not reliable. */ *pp_stun_resp = tsk_null; tv.tv_sec = 0; tv.tv_usec = 0; if ((ret = tnet_stun_pkt_get_size_in_octetunits_with_padding(pc_stun_req, &u_buff_size))) { goto bail; } u_buff_size += kStunBuffMinPad; #if HAVE_CRT //Debug memory if (!(p_buff_ptr = malloc(u_buff_size))) { #else if (!(p_buff_ptr = tsk_malloc(u_buff_size))) { #endif //HAVE_CRT goto bail; } if ((ret = tnet_stun_pkt_write_with_padding(pc_stun_req, p_buff_ptr, u_buff_size, &u_buff_size))) { goto bail; } /* RFC 5389 - 7.2.1. Sending over UDP A client SHOULD retransmit a STUN request message starting with an interval of RTO ("Retransmission TimeOut"), doubling after each retransmission. e.g. 0 ms, 500 ms, 1500 ms, 3500 ms, 7500ms, 15500 ms, and 31500 ms */ for (i = 0; i < Rc; i++) { tv.tv_sec += rto/1000; tv.tv_usec += (rto% 1000) * 1000; if (tv.tv_usec >= 1000000) { tv.tv_usec -= 1000000; tv.tv_sec++; } FD_ZERO(&set); FD_SET(localFD, &set); if ((ret = tnet_sockfd_sendto(localFD, p_addr_server, p_buff_ptr, u_buff_size))) { // do nothing... not an error } if ((ret = select(localFD+1, &set, NULL, NULL, &tv)) < 0) { goto bail; } else if (ret == 0) { /* timeout */ TSK_DEBUG_INFO("STUN request timedout at %d", i); rto *= 2; continue; } else if (FD_ISSET(localFD, &set)) { /* there is data to read */ unsigned int len = 0; void* data = 0; TSK_DEBUG_INFO("STUN request got response"); /* Check how how many bytes are pending */ if ((ret = tnet_ioctlt(localFD, FIONREAD, &len)) < 0) { goto bail; } if(len == 0) { TSK_DEBUG_INFO("tnet_ioctlt() returent zero bytes"); continue; } /* Receive pending data */ #if HAVE_CRT //Debug memory data = malloc(len); #else data = tsk_malloc(len); #endif //HAVE_CRT if ((ret = tnet_sockfd_recvfrom(localFD, data, len, 0, p_addr_server)) < 0) { TSK_FREE(data); TSK_DEBUG_ERROR("Recv STUN dgrams failed with error code:%d", tnet_geterrno()); goto bail; } /* Parse the incoming response. */ ret = tnet_stun_pkt_read(data, (tsk_size_t)ret, pp_stun_resp); TSK_FREE(data); if (*pp_stun_resp) { if (tnet_stun_utils_transac_id_cmp((*pp_stun_resp)->transac_id, pc_stun_req->transac_id) != 0) { /* Not same transaction id */ TSK_OBJECT_SAFE_FREE(*pp_stun_resp); continue; } } goto bail; } else { continue; } } bail: TSK_FREE(p_buff_ptr); return (*pp_stun_resp) ? 0 : -4; }