diff options
author | Mamadou DIOP <bossiel@yahoo.fr> | 2015-08-17 01:56:35 +0200 |
---|---|---|
committer | Mamadou DIOP <bossiel@yahoo.fr> | 2015-08-17 01:56:35 +0200 |
commit | 631fffee8a28b1bec5ed1f1d26a20e0135967f99 (patch) | |
tree | 74afe3bf3efe15aa82bcd0272b2b0f4d48c2d837 /tinySIP/src/transports | |
parent | 7908865936604036e6f200f1b5e069f8752f3a3a (diff) | |
download | doubango-631fffee8a28b1bec5ed1f1d26a20e0135967f99.zip doubango-631fffee8a28b1bec5ed1f1d26a20e0135967f99.tar.gz |
-
Diffstat (limited to 'tinySIP/src/transports')
-rw-r--r-- | tinySIP/src/transports/tsip_transport.c | 1098 | ||||
-rw-r--r-- | tinySIP/src/transports/tsip_transport_ipsec.c | 537 | ||||
-rw-r--r-- | tinySIP/src/transports/tsip_transport_layer.c | 1403 | ||||
-rw-r--r-- | tinySIP/src/transports/tsip_transport_tls.c | 0 |
4 files changed, 3038 insertions, 0 deletions
diff --git a/tinySIP/src/transports/tsip_transport.c b/tinySIP/src/transports/tsip_transport.c new file mode 100644 index 0000000..ac1c8d1 --- /dev/null +++ b/tinySIP/src/transports/tsip_transport.c @@ -0,0 +1,1098 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* Copyright (C) 2012 Doubango Telecom +* +* 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. +* +*/ + +/**@file tsip_transport.c + * @brief SIP transport. + * + */ +#include "tinysip/transports/tsip_transport.h" +#include "tinysip/transports/tsip_transport_ipsec.h" + +#include "tinysip/dialogs/tsip_dialog_layer.h" +#include "tinysip/transports/tsip_transport_layer.h" + +#include "tinysip/transactions/tsip_transac.h" /* TSIP_TRANSAC_MAGIC_COOKIE */ + +#include "tinysip/parsers/tsip_parser_uri.h" + +#include "tsk_string.h" +#include "tsk_buffer.h" +#include "tsk_debug.h" + +// Number of active peers before we start cleanup up (check for timeouts) +#if !defined(TSIP_TRANSPORT_STREAM_PEERS_COUNT_BEFORE_CHECKING_TIMEOUT) +# define TSIP_TRANSPORT_STREAM_PEERS_COUNT_BEFORE_CHECKING_TIMEOUT 100 +#endif +// Number of milliseconds of inactivity before we declare the peer as "timedout". +#if !defined(TSIP_TRANSPORT_STREAM_PEER_TIMEOUT) +# define TSIP_TRANSPORT_STREAM_PEER_TIMEOUT 600000 /* 10 minutes */ +#endif /* TSIP_TRANSPORT_STREAM_PEER_TIMEOUT */ +// Maximum number of milliseconds allowed to complete the WebSocket handshaking process. +#if !defined(TSIP_TRANSPORT_STREAM_PEER_WS_HANDSHAKING_TIMEOUT) +# define TSIP_TRANSPORT_STREAM_PEER_WS_HANDSHAKING_TIMEOUT 5000 /* 5 seconds */ +#endif /* TSIP_TRANSPORT_STREAM_PEER_TIMEOUT */ +// Maximum number of milliseconds allowed between the connection and the first valid SIP message. +#if !defined(TSIP_TRANSPORT_STREAM_PEER_FIRST_MSG_TIMEOUT) +# define TSIP_TRANSPORT_STREAM_PEER_FIRST_MSG_TIMEOUT 30000 /* 30 seconds */ // High because of WebRTC clients (Time between camera access request and end-of-ice process) +#endif /* TSIP_TRANSPORT_STREAM_PEER_FIRST_MSG_TIMEOUT */ + +static const char* __null_callid = tsk_null; + +static const tsip_transport_idx_xt _tsip_transport_idxs_xs[TSIP_TRANSPORT_IDX_MAX] = +{ + { TSIP_TRANSPORT_IDX_UDP, "UDP", TNET_SOCKET_TYPE_UDP }, + { TSIP_TRANSPORT_IDX_DTLS, "DTLS", TNET_SOCKET_TYPE_DTLS }, + { TSIP_TRANSPORT_IDX_TCP, "TCP", TNET_SOCKET_TYPE_TCP }, + { TSIP_TRANSPORT_IDX_TLS, "TLS", TNET_SOCKET_TYPE_TLS }, + { TSIP_TRANSPORT_IDX_WS, "WS", TNET_SOCKET_TYPE_WS }, + { TSIP_TRANSPORT_IDX_WSS, "WSS", TNET_SOCKET_TYPE_WSS }, +}; + +const tsip_transport_idx_xt* tsip_transport_get_by_name(const char* name) +{ + int i; + if(!name) { + return tsk_null; + } + for(i = 0; i < TSIP_TRANSPORT_IDX_MAX; ++i) { + if(tsk_striequals(_tsip_transport_idxs_xs[i].name, name)){ + return &_tsip_transport_idxs_xs[i]; + } + } + return tsk_null; +} + +// returns -1 if not exist +int tsip_transport_get_idx_by_name(const char* name) +{ + const tsip_transport_idx_xt* t_idx = tsip_transport_get_by_name(name); + return t_idx ? t_idx->idx : -1; +} + +enum tnet_socket_type_e tsip_transport_get_type_by_name(const char* name) +{ + const tsip_transport_idx_xt* t_idx = tsip_transport_get_by_name(name); + return t_idx ? t_idx->type : tnet_socket_type_invalid; +} + +/*== Predicate function to find a peer by local id */ +static int _pred_find_stream_peer_by_local_fd(const tsk_list_item_t *item, const void *local_fd) +{ + if(item && item->data){ + const tsip_transport_stream_peer_t *peer = (const tsip_transport_stream_peer_t*)item->data; + return (peer->local_fd - *((tnet_fd_t*)local_fd)); + } + return -1; +} + + +/* creates new SIP transport */ +tsip_transport_t* tsip_transport_create(tsip_stack_t* stack, const char* host, tnet_port_t port, tnet_socket_type_t type, const char* description) +{ + tsip_transport_t* transport; + if((transport = tsk_object_new(tsip_transport_def_t, stack, host, port, type, description))){ + int i; + for(i = 0; i < sizeof(_tsip_transport_idxs_xs)/sizeof(_tsip_transport_idxs_xs[0]); ++i){ + if(_tsip_transport_idxs_xs[i].type & type){ + transport->idx = _tsip_transport_idxs_xs[i].idx; + break; + } + } + } + return transport; +} + +/* add Via header using the transport config +must be called after update_aor() +*/ +int tsip_transport_addvia(const tsip_transport_t* self, const char *branch, tsip_message_t *msg) +{ + tnet_ip_t ip = { '\0' }; + tnet_port_t port; + int ret; + int32_t transport_idx; + + if((transport_idx = tsip_transport_get_idx_by_name(self->protocol)) == -1){ + transport_idx = self->stack->network.transport_idx_default; + } + + /* we always use same port to send() and recv() msg which means Via and Contact headers are identical */ + if(TNET_SOCKET_TYPE_IS_IPSEC(self->type) && ((tsip_transport_ipsec_t*)self)->asso_active){ + memcpy(ip, ((tsip_transport_ipsec_t*)self)->asso_active->socket_us->ip, sizeof(tnet_ip_t)); + port = ((tsip_transport_ipsec_t*)self)->asso_active->socket_us->port; + } + else if(self->stack->network.aor.ip[transport_idx] && self->stack->network.aor.port[transport_idx]){ + memcpy(ip, self->stack->network.aor.ip[transport_idx], TSK_MIN(tsk_strlen(self->stack->network.aor.ip[transport_idx]), sizeof(ip))); + port = self->stack->network.aor.port[transport_idx]; + } + else if((ret = tsip_transport_get_ip_n_port(self, &ip, &port))){ + return ret; + } + + /* is there a Via header? */ + if(!msg->firstVia){ + /* RFC 3261 - 18.1.1 Sending Requests + Before a request is sent, the client transport MUST insert a value of + the "sent-by" field into the Via header field. This field contains + an IP address or host name, and port. The usage of an FQDN is + RECOMMENDED. This field is used for sending responses under certain + conditions, described below. If the port is absent, the default + value depends on the transport. It is 5060 for UDP, TCP and SCTP, + 5061 for TLS. + */ + msg->firstVia = tsip_header_Via_create(TSIP_HEADER_VIA_PROTO_NAME_DEFAULT, TSIP_HEADER_VIA_PROTO_VERSION_DEFAULT, self->via_protocol, ip, port); + TSIP_HEADER_ADD_PARAM(TSIP_HEADER(msg->firstVia), "rport", tsk_null); + } + else if(msg->update && self->stack->network.mode == tsip_stack_mode_webrtc2sip){ + if(TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) || TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type)){ + const tsip_transport_t* ws_transport = tsip_transport_layer_find_by_type(self->stack->layer_transport, msg->src_net_type); + if(ws_transport){ + tsip_transport_stream_peer_t* peer = tsip_transport_find_stream_peer_by_local_fd(TSIP_TRANSPORT(ws_transport), msg->local_fd); + if(peer){ + // hack the first Via as many servers fail to parse "WS" or "WSS" as valid transpors + //if(tsk_striequals(msg->firstVia->transport, "WS") || tsk_striequals(msg->firstVia->transport, "WSS")){ + TSIP_HEADER_ADD_PARAM(TSIP_HEADER(msg->firstVia), "ws-hacked", TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type) ? "WSS" : "WS"); + tsk_strupdate(&msg->firstVia->transport, "TCP"); + tsk_strupdate(&msg->firstVia->host, peer->remote_ip); + msg->firstVia->port = peer->remote_port; + //} + TSK_OBJECT_SAFE_FREE(peer); + + // replace first Via with ours + tsip_message_add_header(msg, (const tsip_header_t *)msg->firstVia); + TSK_OBJECT_SAFE_FREE(msg->firstVia); + msg->firstVia = tsip_header_Via_create(TSIP_HEADER_VIA_PROTO_NAME_DEFAULT, TSIP_HEADER_VIA_PROTO_VERSION_DEFAULT, self->via_protocol, ip, port); + TSIP_HEADER_ADD_PARAM(TSIP_HEADER(msg->firstVia), "rport", tsk_null); + } + } + } + } + + /* updates the branch */ + if(branch){ + tsk_strupdate(&msg->firstVia->branch, branch); + } + else{ /* Probably ACK sent from Dialog Layer */ + TSK_FREE(msg->firstVia->branch); + if((msg->firstVia->branch = tsk_strdup(TSIP_TRANSAC_MAGIC_COOKIE))){ + tsk_istr_t _branch; + tsk_strrandom(&_branch); + tsk_strcat_2(&msg->firstVia->branch, "-%s", _branch); + } + } + + /* multicast case */ + if(tsk_false){ + /* RFC 3261 - 18.1.1 Sending Requests (FIXME) + A client that sends a request to a multicast address MUST add the + "maddr" parameter to its Via header field value containing the + destination multicast address, and for IPv4, SHOULD add the "ttl" + parameter with a value of 1. Usage of IPv6 multicast is not defined + in this specification, and will be a subject of future + standardization when the need arises. + */ + } + + /* + * comp=sigcomp; sigcomp-id= + */ + + return 0; +} + +int tsip_transport_msg_update_aor(tsip_transport_t* self, tsip_message_t *msg) +{ + int ret = 0; + int32_t transport_idx; + + /* already updtated (e.g. retrans)? */ + if(!msg->update){ + return 0; + } + + if((transport_idx = tsip_transport_get_idx_by_name(self->protocol)) == -1){ + transport_idx = self->stack->network.transport_idx_default; + } + + /* retrieves the transport ip address and port */ + if(!self->stack->network.aor.ip[transport_idx] && !self->stack->network.aor.port[transport_idx]){ + tnet_ip_t ip = {0}; + tnet_port_t port = 0; + + if((ret = tsip_transport_get_public_ip_n_port(self, &ip, &port))){ + TSK_DEBUG_ERROR("Failed to get public IP"); + return ret; + } + else{ + ((tsip_stack_t*)self->stack)->network.aor.ip[transport_idx] = tsk_strdup(ip); + ((tsip_stack_t*)self->stack)->network.aor.port[transport_idx] = port; + } + } + + /* === Host and port === */ + if(msg->Contact && msg->Contact->uri){ + tsk_strupdate(&(msg->Contact->uri->scheme), self->scheme); + msg->Contact->uri->host_type = TNET_SOCKET_TYPE_IS_IPV6(self->type) ? host_ipv6 : host_ipv4; /* for serializer ...who know? */ + tsk_params_add_param(&msg->Contact->uri->params, "transport", self->protocol); + + // IPSec + if(TNET_SOCKET_TYPE_IS_IPSEC(self->type) && ((tsip_transport_ipsec_t*)self)->asso_active){ + tsk_strupdate(&(msg->Contact->uri->host), ((tsip_transport_ipsec_t*)self)->asso_active->socket_us->ip); + msg->Contact->uri->port = ((tsip_transport_ipsec_t*)self)->asso_active->socket_us->port; + } + else { + tsk_strupdate(&(msg->Contact->uri->host), self->stack->network.aor.ip[transport_idx]); + msg->Contact->uri->port = self->stack->network.aor.port[transport_idx]; + } + + /* Add extra params for message received over WebSocket transport */ + if((TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) || TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type)) && msg->local_fd > 0){ + tnet_ip_t ws_src_ip; + tnet_port_t ws_src_port; + if(tnet_get_ip_n_port(msg->local_fd, tsk_false/*remote*/, &ws_src_ip, &ws_src_port) == 0){ + tsk_params_add_param(&msg->Contact->uri->params, "ws-src-ip", ws_src_ip); + tsk_params_add_param_3(&msg->Contact->uri->params, "ws-src-port", (int64_t)ws_src_port); + tsk_params_add_param(&msg->Contact->uri->params, "ws-src-proto", TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) ? "ws" : "wss"); + } + } + } + + return 0; +} + +/* update the entire message (IPSec headers, SigComp, ....) */ +int tsip_transport_msg_update(const tsip_transport_t* self, tsip_message_t *msg) +{ + int ret = 0; + + /* already updtated (e.g. retrans)? */ + if(!msg->update){ + return 0; + } + + /* === IPSec headers (Security-Client, Security-Verify, Sec-Agree ...) === */ + if(TNET_SOCKET_TYPE_IS_IPSEC(self->type)){ + ret = tsip_transport_ipsec_updateMSG(TSIP_TRANSPORT_IPSEC(self), msg); + } + + /* === SigComp === */ + if(msg->sigcomp_id){ + /* Via */ + if(msg->firstVia){ + char* quoted_id = tsk_null; + TSIP_HEADER_ADD_PARAM(msg->firstVia, "comp", "sigcomp"); + tsk_sprintf("ed_id, "\"%s\"", msg->sigcomp_id); + TSIP_HEADER_ADD_PARAM(msg->firstVia, "sigcomp-id", quoted_id); + TSK_FREE(quoted_id); + } + /* Contact */ + if(msg->Contact && msg->Contact->uri){ + tsk_params_add_param(&msg->Contact->uri->params, "sigcomp-id", msg->sigcomp_id); + } + } + /* === WebRTC2SIP === */ + if(TSIP_MESSAGE_IS_REQUEST(msg)) { + if(self->stack->network.mode == tsip_stack_mode_webrtc2sip) { + // Request Uri (Fix: https://code.google.com/p/webrtc2sip/issues/detail?id=56) + if(tsk_params_have_param(msg->line.request.uri->params, "transport")){ + tsk_params_add_param(&msg->line.request.uri->params, "transport", self->protocol); + } + } + } + + + msg->update = tsk_false; /* To avoid to update retrans. */ + + return ret; +} + +// "udp", "tcp" or "tls" +tsk_size_t tsip_transport_send_raw(const tsip_transport_t* self, const char* dst_host, tnet_port_t dst_port, const void* data, tsk_size_t size, const char* callid) +{ + tsk_size_t ret = 0; + + TSK_DEBUG_INFO("\n\nSEND: %.*s\n\n", size, (const char*)data); + + if(TNET_SOCKET_TYPE_IS_DGRAM(self->type)){// "udp" or "dtls" + const struct sockaddr_storage* to = &self->pcscf_addr; + struct sockaddr_storage dst_addr; // must be local scope + if(!tsk_strnullORempty(dst_host) && dst_port){ + if(tnet_sockaddr_init(dst_host, dst_port, self->type, &dst_addr) == 0){ + to = &dst_addr; + } + } + if(!(ret = tnet_transport_sendto(self->net_transport, self->connectedFD, (const struct sockaddr*)to, data, size))){ + TSK_DEBUG_ERROR("Send(%u) returns zero", size); + } + } + else{// "sctp", "tcp" or "tls" + tsip_transport_stream_peer_t* peer = tsk_null; + tnet_ip_t dst_ip; + + if(tsk_strnullORempty(dst_host) || !dst_port){ + if(tnet_get_sockip_n_port((const struct sockaddr *)&self->pcscf_addr, &dst_ip, &dst_port) != 0){ + TSK_DEBUG_ERROR("Failed to get Proxy-CSCF IP address and port"); + return 0; + } + } + else{ + // get IP address and port + // we use ip/port instead of fqdn because this what "tsip_transport_add_stream_peer()" requires it + if(tnet_resolve(dst_host, dst_port, self->type, &dst_ip, &dst_port) != 0){ + TSK_DEBUG_ERROR("Failed to resolve(%s/%d)", dst_host, dst_port); + return 0; + } + } + + if(!(peer = tsip_transport_find_stream_peer_by_remote_ip(TSIP_TRANSPORT(self), dst_ip, dst_port, self->type))){ + tnet_fd_t fd; + TSK_DEBUG_INFO("Cannot find peer with remote IP/Port=%s/%d, connecting to the destination...", dst_ip, dst_port); + // connect to the destination + // stream with the new "fd" will be added later, make sure that no other thread (e.g. network callback) will manipulate the peers + tsip_transport_stream_peers_lock(TSIP_TRANSPORT(self)); + if((fd = tnet_transport_connectto_2(TSIP_TRANSPORT(self)->net_transport, dst_ip, dst_port)) == TNET_INVALID_FD){ + TSK_DEBUG_ERROR("Failed to connect to %s/%d", dst_ip, dst_port); + tsip_transport_stream_peers_unlock(TSIP_TRANSPORT(self)); + return 0; + } + // only clients will have connected fd == EVAL. For servers, it will be equal to master's fd + // connected fd value will be set to EVAL when "disconnected" event is received + if (TSIP_TRANSPORT(self)->connectedFD == TNET_INVALID_FD) { + TSIP_TRANSPORT(self)->connectedFD = fd; + } + + if(tsip_transport_add_stream_peer_2(TSIP_TRANSPORT(self), fd, self->type, tsk_false, dst_ip, dst_port) != 0){ + TSK_DEBUG_ERROR("Failed to add stream peer local fd = %d, remote IP/Port=%s/%d", fd, dst_ip, dst_port); + tsip_transport_stream_peers_unlock(TSIP_TRANSPORT(self)); + return 0; + } + tsip_transport_stream_peers_unlock(TSIP_TRANSPORT(self)); + + // retrieve the peer + if(!(peer = tsip_transport_find_stream_peer_by_local_fd(TSIP_TRANSPORT(self), fd))){ + TSK_DEBUG_INFO("Cannot find peer with remote IP/Port=%s/%d. Cancel data sending", dst_ip, dst_port); + return 0; + } + } + // store call-id + if(callid != __null_callid && tsip_dialog_layer_have_dialog_with_callid(self->stack->layer_dialog, callid)){ + ret = tsip_transport_stream_peer_add_callid(peer, callid); + } + // send() data + if(peer->connected){ + ret = tnet_transport_send(self->net_transport, peer->local_fd, data, size); + } + else{ + TSK_DEBUG_INFO("Data send requested but peer not connected yet...saving data"); + tsk_buffer_append(peer->snd_buff_stream, data, size); + ret = 0; // nothing sent + } + TSK_OBJECT_SAFE_FREE(peer); + } + + return ret; +} + +// "ws" or "wss" +tsk_size_t tsip_transport_send_raw_ws(const tsip_transport_t* self, tnet_fd_t local_fd, const void* data, tsk_size_t size, const char* callid) +{ + /*static const uint8_t __ws_first_byte = 0x82;*/ + const uint8_t* pdata = (const uint8_t*)data; + uint64_t data_size = 1 + 1 + size; + uint64_t lsize = (uint64_t)size; + uint8_t* pws_snd_buffer; + tsip_transport_stream_peer_t* peer; + tsk_size_t ret; + + if(!(peer = tsip_transport_find_stream_peer_by_local_fd(TSIP_TRANSPORT(self), local_fd))){ + TSK_DEBUG_ERROR("Failed to find peer with local fd equal to %d", local_fd); + return 0; + } + + if(lsize > 0x7D && lsize <= 0xFFFF){ + data_size += 2; + } + else if(lsize > 0xFFFF){ + data_size += 8; + } + if(peer->ws.snd_buffer_size < data_size){ + if(!(peer->ws.snd_buffer = tsk_realloc(peer->ws.snd_buffer, (tsk_size_t)data_size))){ + TSK_DEBUG_ERROR("Failed to allocate buffer with size = %llu", data_size); + peer->ws.snd_buffer_size = 0; + TSK_OBJECT_SAFE_FREE(peer); + return 0; + } + peer->ws.snd_buffer_size = data_size; + } + pws_snd_buffer = (uint8_t*)peer->ws.snd_buffer; + + pws_snd_buffer[0] = 0x82; + if(lsize <= 0x7D){ + pws_snd_buffer[1] = (uint8_t)lsize; + pws_snd_buffer = &pws_snd_buffer[2]; + } + else if(lsize <= 0xFFFF){ + pws_snd_buffer[1] = 0x7E; + pws_snd_buffer[2] = (lsize >> 8) & 0xFF; + pws_snd_buffer[3] = (lsize & 0xFF); + pws_snd_buffer = &pws_snd_buffer[4]; + } + else{ + pws_snd_buffer[1] = 0x7F; + pws_snd_buffer[2] = (lsize >> 56) & 0xFF; + pws_snd_buffer[3] = (lsize >> 48) & 0xFF; + pws_snd_buffer[4] = (lsize >> 40) & 0xFF; + pws_snd_buffer[5] = (lsize >> 32) & 0xFF; + pws_snd_buffer[6] = (lsize >> 24) & 0xFF; + pws_snd_buffer[7] = (lsize >> 16) & 0xFF; + pws_snd_buffer[8] = (lsize >> 8) & 0xFF; + pws_snd_buffer[9] = (lsize & 0xFF); + pws_snd_buffer = &pws_snd_buffer[10]; + } + + memcpy(pws_snd_buffer, pdata, (size_t)lsize); + + // store call-id + if(callid != __null_callid && tsip_dialog_layer_have_dialog_with_callid(self->stack->layer_dialog, callid)){ + ret = tsip_transport_stream_peer_add_callid(peer, callid); + } + // send() data + ret = tnet_transport_send(self->net_transport, local_fd, peer->ws.snd_buffer, (tsk_size_t)data_size); + + TSK_OBJECT_SAFE_FREE(peer); + + return ret; +} + +/* sends a request +* all callers of this function should provide a sigcomp-id +*/ +tsk_size_t tsip_transport_send(const tsip_transport_t* self, const char *branch, tsip_message_t *msg, const char* destIP, int32_t destPort) +{ + tsk_size_t ret = 0; + if(self){ + tsk_buffer_t *buffer = tsk_null; + const char* callid = msg->Call_ID ? msg->Call_ID->value : __null_callid; + + /* Add Via and update AOR, IPSec headers, SigComp ... + * ACK sent from the transaction layer will contains a Via header and should not be updated + * CANCEL will have the same Via and Contact headers as the request it cancel + * Any request received from WS/WSS transport layer have to be updated regardless above rules + */ + if(TSIP_MESSAGE_IS_REQUEST(msg)){ + const tsk_bool_t update = ( (!TSIP_REQUEST_IS_ACK(msg) || (TSIP_REQUEST_IS_ACK(msg) && !msg->firstVia)) && !TSIP_REQUEST_IS_CANCEL(msg) ) + || ( TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) || TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type) ); + if(update){ + /* AoR: Contact header */ + tsip_transport_msg_update_aor((tsip_transport_t*)self, msg); + /* should be done before tsip_transport_msg_update() which could use the Via header + must be done after update_aor() + */ + tsip_transport_addvia(self, branch, msg); + tsip_transport_msg_update(self, msg); /* IPSec, SigComp, ... */ + } + } + else if(TSIP_MESSAGE_IS_RESPONSE(msg)){ + /* AoR for responses which have a contact header (e.g. 183/200 INVITE) */ + if(msg->Contact){ + tsip_transport_msg_update_aor((tsip_transport_t*)self, msg); + } + /* RFC 3581 - 4. Server Behavior + When a server compliant to this specification (which can be a proxy + or UAS) receives a request, it examines the topmost Via header field + value. If this Via header field value contains an "rport" parameter + with no value, it MUST set the value of the parameter to the source + port of the request. + */ + if(msg->firstVia->rport == 0){ + /* As the response message has been built from the request ...then it's first via is the same as + the request's first via. + */ + msg->firstVia->rport = msg->firstVia->port; + } + } + + if((buffer = tsk_buffer_create_null())){ + tsip_message_tostring(msg, buffer); + + if(buffer->size >1300){ + /* RFC 3261 - 18.1.1 Sending Requests (FIXME) + If a request is within 200 bytes of the path MTU, or if it is larger + than 1300 bytes and the path MTU is unknown, the request MUST be sent + using an RFC 2914 [43] congestion controlled transport protocol, such + as TCP. If this causes a change in the transport protocol from the + one indicated in the top Via, the value in the top Via MUST be + changed. This prevents fragmentation of messages over UDP and + provides congestion control for larger messages. However, + implementations MUST be able to handle messages up to the maximum + datagram packet size. For UDP, this size is 65,535 bytes, including + IP and UDP headers. + */ + } + + /* === SigComp === */ + if(msg->sigcomp_id){ + if(self->stack->sigcomp.handle){ + tsk_size_t out_size; + char SigCompBuffer[TSIP_SIGCOMP_MAX_BUFF_SIZE]; + + out_size = tsip_sigcomp_handler_compress(self->stack->sigcomp.handle, msg->sigcomp_id, TNET_SOCKET_TYPE_IS_STREAM(self->type), + buffer->data, buffer->size, SigCompBuffer, sizeof(SigCompBuffer)); + if(out_size){ + tsk_buffer_cleanup(buffer); + tsk_buffer_append(buffer, SigCompBuffer, out_size); + } + } + else{ + TSK_DEBUG_ERROR("The outgoing message should be compressed using SigComp but there is not compartment"); + } + } + + /* === Send the message === */ + if(TNET_SOCKET_TYPE_IS_WS(self->type) || TNET_SOCKET_TYPE_IS_WSS(self->type)){ + //if(!TNET_SOCKET_TYPE_IS_WS(msg->net_type) && !TNET_SOCKET_TYPE_IS_WSS(msg->net_type)){ + // message not received over WS/WS tranport but have to be sent over WS/WS + tsip_transport_stream_peer_t* peer = tsip_transport_find_stream_peer_by_remote_ip(TSIP_TRANSPORT(self), destIP, destPort, self->type); + if(peer){ + ret = tsip_transport_send_raw_ws(self, peer->local_fd, buffer->data, buffer->size, callid); + TSK_OBJECT_SAFE_FREE(peer); + } + else if(msg->local_fd > 0) + //} + //else{ + ret = tsip_transport_send_raw_ws(self, msg->local_fd, buffer->data, buffer->size, callid); + //} + } + else if(TNET_SOCKET_TYPE_IS_IPSEC(self->type)){ + tnet_fd_t fd = tsip_transport_ipsec_getFD(TSIP_TRANSPORT_IPSEC(self), TSIP_MESSAGE_IS_REQUEST(msg)); + // "fd == TNET_INVALID_FD" means IPSec SAs not up yet + ret = (fd != TNET_INVALID_FD) + ? tnet_sockfd_send(fd, buffer->data, buffer->size, 0) + : tsip_transport_send_raw(self, destIP, destPort, buffer->data, buffer->size, callid); + } + else{ + ret = tsip_transport_send_raw(self, destIP, destPort, buffer->data, buffer->size, callid); + } + +//bail: + TSK_OBJECT_SAFE_FREE(buffer); + } + } + + return ret; +} + + +tsip_uri_t* tsip_transport_get_uri(const tsip_transport_t *self, tsk_bool_t lr) +{ + if(self){ + //tnet_ip_t ip; + //tnet_port_t port; + tsip_uri_t* uri = tsk_null; + + //if(!tnet_get_ip_n_port(self->connectedFD, &ip, &port)){ + char* uristring = tsk_null; + int ipv6 = TNET_SOCKET_TYPE_IS_IPV6(self->type); + + tsk_sprintf(&uristring, "%s:%s%s%s:%d;%s;transport=%s", + self->scheme, + ipv6 ? "[" : "", + ((tsip_stack_t*)self->stack)->network.aor.ip[self->idx], + ipv6 ? "]" : "", + ((tsip_stack_t*)self->stack)->network.aor.port[self->idx], + lr ? "lr" : "", + self->protocol); + if(uristring){ + if((uri = tsip_uri_parse(uristring, tsk_strlen(uristring)))){ + uri->host_type = ipv6 ? host_ipv6 : host_ipv4; + } + TSK_FREE(uristring); + } + //} + return uri; + } + return tsk_null; +} + +// remote ip should not be FQDN +int tsip_transport_add_stream_peer_2(tsip_transport_t *self, tnet_fd_t local_fd, enum tnet_socket_type_e type, tsk_bool_t connected, const char* remote_host, tnet_port_t remote_port) +{ + tsip_transport_stream_peer_t* peer = tsk_null; + tnet_ip_t remote_ip; + int ret = 0; + + if(!self || local_fd < 0){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsip_transport_stream_peers_lock(self); + + if(tsip_transport_have_stream_peer_with_local_fd(self, local_fd)){ + // could happen if the closed socket haven't raise "close event" yet and new own added : Windows only + tsip_transport_remove_stream_peer_by_local_fd(self, local_fd); + } + + if(tsk_strnullORempty(remote_host) || !remote_port){ + if(tnet_get_ip_n_port(local_fd, tsk_false/*remote*/, &remote_ip, &remote_port) != 0){ + TSK_DEBUG_ERROR("Failed to get remote peer ip and address for local fd = %d", local_fd); + ret = -2; + goto bail; + } + remote_host = (const char*)remote_ip; + } + else if((ret = tnet_resolve(remote_host, remote_port, type, &remote_ip, &remote_port))){ + TSK_DEBUG_ERROR("Failed to resolve(%s/%d)", remote_host, remote_port); + ret = -3; + goto bail; + } + + if(!(peer = tsk_object_new(tsip_transport_stream_peer_def_t))){ + TSK_DEBUG_ERROR("Failed to create network stream peer"); + ret = -4; + goto bail; + } + + peer->local_fd = local_fd; + peer->type = type; + peer->connected = connected; + peer->remote_port = remote_port; + memcpy(peer->remote_ip, remote_ip, sizeof(remote_ip)); + + tsip_transport_stream_peers_lock(self); + peer->time_latest_activity = tsk_time_now(); + peer->time_added = peer->time_latest_activity; + tsk_list_push_back_data(self->stream_peers, (void**)&peer); + ++self->stream_peers_count; + TSK_DEBUG_INFO("#%d peers in the '%s' transport", self->stream_peers_count, tsip_transport_get_description(self)); + tsip_transport_stream_peers_unlock(self); + + // Cleanup streams + if (self->stream_peers_count > TSIP_TRANSPORT_STREAM_PEERS_COUNT_BEFORE_CHECKING_TIMEOUT && self->stack->network.mode == tsip_stack_mode_webrtc2sip) { + ret = tsip_transport_stream_peers_cleanup(self); + } + +bail: + TSK_OBJECT_SAFE_FREE(peer); + tsip_transport_stream_peers_unlock(self); + return ret; +} + +// up to the caller to release the returned object +tsip_transport_stream_peer_t* tsip_transport_find_stream_peer_by_local_fd(tsip_transport_t *self, tnet_fd_t local_fd) +{ + tsip_transport_stream_peer_t* peer = tsk_null; + tsk_list_item_t* item; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsip_transport_stream_peers_lock(self); + tsk_list_foreach(item, self->stream_peers){ + if(((tsip_transport_stream_peer_t*)item->data)->local_fd == local_fd){ + peer = tsk_object_ref(item->data); + break; + } + } + tsip_transport_stream_peers_unlock(self); + return peer; +} + +// up to the caller to release the returned object +// calling this function will remove the peer from the list +tsip_transport_stream_peer_t* tsip_transport_pop_stream_peer_by_local_fd(tsip_transport_t *self, tnet_fd_t local_fd) +{ + if(self){ + tsip_transport_stream_peer_t* peer = tsk_null; + tsk_list_item_t *item; + tsip_transport_stream_peers_lock(self); + if((item = tsk_list_pop_item_by_pred(self->stream_peers, _pred_find_stream_peer_by_local_fd, &local_fd))){ + peer = tsk_object_ref(item->data); + TSK_OBJECT_SAFE_FREE(item); + --self->stream_peers_count; + TSK_DEBUG_INFO("#%d peers in the '%s' transport", self->stream_peers_count, tsip_transport_get_description(self)); + } + tsip_transport_stream_peers_unlock(self); + return peer; + } + return tsk_null; +} + +// up to the caller to release the returned object +tsip_transport_stream_peer_t* tsip_transport_find_stream_peer_by_remote_ip(tsip_transport_t *self, const char* remote_ip, tnet_port_t remote_port, enum tnet_socket_type_e type) +{ + tsip_transport_stream_peer_t* peer = tsk_null; + tsk_list_item_t* item; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsip_transport_stream_peers_lock(self); + tsk_list_foreach(item, self->stream_peers){ + if(((tsip_transport_stream_peer_t*)item->data)->type == type && ((tsip_transport_stream_peer_t*)item->data)->remote_port == remote_port && tsk_striequals(((tsip_transport_stream_peer_t*)item->data)->remote_ip, remote_ip)){ + peer = tsk_object_ref(item->data); + break; + } + } + tsip_transport_stream_peers_unlock(self); + return peer; +} + +tsk_bool_t tsip_transport_have_stream_peer_with_remote_ip(tsip_transport_t *self, const char* remote_ip, tnet_port_t remote_port, enum tnet_socket_type_e type) +{ + if(self && !tsk_strnullORempty(remote_ip) && remote_port){ + tsip_transport_stream_peer_t* peer = tsip_transport_find_stream_peer_by_remote_ip(self, remote_ip, remote_port, type); + if(peer){ + TSK_OBJECT_SAFE_FREE(peer); + return tsk_true; + } + } + return tsk_false; +} + +tsk_bool_t tsip_transport_have_stream_peer_with_local_fd(tsip_transport_t *self, tnet_fd_t local_fd) +{ + tsip_transport_stream_peer_t* peer = tsip_transport_find_stream_peer_by_local_fd(self, local_fd); + tsk_bool_t ret = (peer != tsk_null); + TSK_OBJECT_SAFE_FREE(peer); + return ret; +} + +int tsip_transport_remove_stream_peer_by_local_fd(tsip_transport_t *self, tnet_fd_t local_fd) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsip_transport_stream_peers_lock(self); + if (tsk_list_remove_item_by_pred(self->stream_peers, _pred_find_stream_peer_by_local_fd, &local_fd)) { + --self->stream_peers_count; + TSK_DEBUG_INFO("#%d peers in the '%s' transport", self->stream_peers_count, tsip_transport_get_description(self)); + } + tsip_transport_stream_peers_unlock(self); + + return 0; +} + +int tsip_transport_remove_callid_from_stream_peers(tsip_transport_t *self, const char* callid, tsk_bool_t* removed) +{ + if(!self || !removed){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + *removed = tsk_false; + if(TNET_SOCKET_TYPE_IS_STREAM(self->type)){ + tsk_list_item_t *item; + tsip_transport_stream_peers_lock(self); + tsk_list_foreach(item, self->stream_peers){ + if(tsip_transport_stream_peer_remove_callid((tsip_transport_stream_peer_t*)item->data, callid, removed) == 0 && *removed){ + TSK_DEBUG_INFO("[Transport] Removed call-id = '%s' from transport with type = %d", callid, self->type); + break; + } + } + tsip_transport_stream_peers_unlock(self); + } + + return 0; +} + +tsk_bool_t tsip_transport_stream_peer_have_callid(const tsip_transport_stream_peer_t* self, const char* callid) +{ + tsk_bool_t have_cid = tsk_false; + if(self){ + const tsk_list_item_t* item; + + tsk_list_lock(self->dialogs_cids); + tsk_list_foreach(item, self->dialogs_cids){ + if(tsk_strequals(TSK_STRING_STR(item->data), callid)){ + have_cid = tsk_true; + break; + } + } + tsk_list_unlock(self->dialogs_cids); + } + return have_cid; +} + +int tsip_transport_stream_peer_add_callid(tsip_transport_stream_peer_t* self, const char* callid) +{ + if(self && !tsk_strnullORempty(callid)){ + tsk_list_lock(self->dialogs_cids); + if(!tsip_transport_stream_peer_have_callid(self, callid)){ + tsk_string_t* cid = tsk_string_create(callid); + if(cid){ + TSK_DEBUG_INFO("Add call-id = '%s' to peer with local fd = %d", callid, self->local_fd); + tsk_list_push_back_data(self->dialogs_cids, (void**)&cid); + TSK_OBJECT_SAFE_FREE(cid); + } + } + tsk_list_unlock(self->dialogs_cids); + return 0; + } + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; +} + +int tsip_transport_stream_peer_remove_callid(tsip_transport_stream_peer_t* self, const char* callid, tsk_bool_t *removed) +{ + if(self && removed){ + *removed = tsk_false; + tsk_list_lock(self->dialogs_cids); + if((*removed = tsk_list_remove_item_by_pred(self->dialogs_cids, tsk_string_pred_cmp, callid)) == tsk_true){ + TSK_DEBUG_INFO("[Stream] Removed call-id = '%s' from peer with local fd = %d", callid, self->local_fd); + } + tsk_list_unlock(self->dialogs_cids); + return 0; + } + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; +} + +int tsip_transport_stream_peers_cleanup(tsip_transport_t *self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (TNET_SOCKET_TYPE_IS_STREAM(self->type)) { + tsk_list_item_t *item; + tsip_transport_stream_peer_t *peer; + tnet_fd_t fd; + tsk_bool_t close; + uint64_t now = tsk_time_now(); + tsip_transport_stream_peers_lock(self); + tsk_list_foreach(item, self->stream_peers) { + if ((peer = (item->data))) { + close = ((now - TSIP_TRANSPORT_STREAM_PEER_TIMEOUT) > peer->time_latest_activity); + if (!close) { + close = !peer->got_valid_sip_msg && ((now - TSIP_TRANSPORT_STREAM_PEER_FIRST_MSG_TIMEOUT) > peer->time_added); + } + if (!close) { + if ((TNET_SOCKET_TYPE_IS_WS(peer->type) || TNET_SOCKET_TYPE_IS_WSS(peer->type)) && !peer->ws.handshaking_done) { + close = ((now - TSIP_TRANSPORT_STREAM_PEER_WS_HANDSHAKING_TIMEOUT) > peer->time_added); + } + } + if (close) { + fd = peer->local_fd; + TSK_DEBUG_INFO("Peer with fd=%d, type=%d, got_valid_sip_msg=%d, time_added=%llu, time_latest_activity=%llu, now=%llu in '%s' transport timedout", + fd, peer->type, peer->got_valid_sip_msg, peer->time_added, peer->time_latest_activity, now, tsip_transport_get_description(self)); + tsip_transport_remove_socket(self, (tnet_fd_t *)&fd); + } + } + } + tsip_transport_stream_peers_unlock(self); + } + + return 0; +} + +int tsip_transport_init(tsip_transport_t* self, tnet_socket_type_t type, const struct tsip_stack_s *stack, const char *host, tnet_port_t port, const char* description) +{ + if(!self || self->initialized){ + return -1; + } + + self->stack = stack; + self->type = type; + self->net_transport = tnet_transport_create(host, port, type, description); + + self->scheme = "sip"; + + if(TNET_SOCKET_TYPE_IS_STREAM(type)){ + if(TNET_SOCKET_TYPE_IS_TLS(type)){ + self->scheme = "sips"; + self->protocol = "tcp"; + self->via_protocol = "TLS"; + self->service = "SIPS+D2T"; + } + else if(TNET_SOCKET_TYPE_IS_WS(type)){ + self->protocol = "ws"; + self->via_protocol = "WS"; + self->service = "SIP+D2W"; + } + else if(TNET_SOCKET_TYPE_IS_WSS(type)){ + self->scheme = "sips"; + self->protocol = "wss"; + self->via_protocol = "WSS"; + self->service = "SIPS+D2W"; + } + else{ + self->protocol = "tcp"; + self->via_protocol = "TCP"; + self->service = "SIP+D2T"; + } + + /* Stream buffer */ + self->stream_peers = tsk_list_create(); + if (!self->stream_peers) { + return -1; + } + } + else{ + if(TNET_SOCKET_TYPE_IS_DTLS(type)){ + self->scheme = "sips"; + self->protocol = "dtls-udp"; + self->via_protocol = "DTLS-UDP"; + self->service = "SIPS+D2U"; + } + else{ + self->protocol = "udp"; + self->via_protocol = "UDP"; + self->service = "SIP+D2U"; + } + } + self->connectedFD = TNET_INVALID_FD; + self->initialized = 1; + + return 0; +} + +int tsip_transport_deinit(tsip_transport_t* self) +{ + if(!self || !self->initialized){ + return -1; + } + + TSK_OBJECT_SAFE_FREE(self->net_transport); + TSK_OBJECT_SAFE_FREE(self->stream_peers); + + self->initialized = 0; + return 0; +} + + + + +//======================================================== +// SIP transport object definition +// +static tsk_object_t* tsip_transport_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transport_t *transport = self; + if(transport){ + const tsip_stack_handle_t *stack = va_arg(*app, const tsip_stack_handle_t*); + const char *host = va_arg(*app, const char*); +#if defined(__GNUC__) + tnet_port_t port = (tnet_port_t)va_arg(*app, unsigned); +#else + tnet_port_t port = va_arg(*app, tnet_port_t); +#endif + tnet_socket_type_t type = va_arg(*app, tnet_socket_type_t); + const char *description = va_arg(*app, const char*); + + if(tsip_transport_init(transport, type, stack, host, port, description)){ + TSK_DEBUG_ERROR("Failed to initialize transport"); + return tsk_null; + } + } + return self; +} + +static tsk_object_t* tsip_transport_dtor(tsk_object_t * self) +{ + tsip_transport_t *transport = self; + if(transport){ + tsip_transport_deinit(transport); + } + return self; +} + +static int tsip_transport_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + const tsip_transport_t *transport1 = obj1; + const tsip_transport_t *transport2 = obj2; + if(transport1 && transport2){ + const char* desc1 = tsip_transport_get_description(transport1); + const char* desc2 = tsip_transport_get_description(transport2); + return tsk_stricmp(desc1, desc2); + } + return -1; +} + +static const tsk_object_def_t tsip_transport_def_s = +{ + sizeof(tsip_transport_t), + tsip_transport_ctor, + tsip_transport_dtor, + tsip_transport_cmp, +}; +const tsk_object_def_t *tsip_transport_def_t = &tsip_transport_def_s; + + + + +//======================================================== +// SIP transport stream peer object definition +// +static tsk_object_t* tsip_transport_stream_peer_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transport_stream_peer_t *peer = self; + if(peer){ + if (!(peer->rcv_buff_stream = tsk_buffer_create_null())) { + return tsk_null; + } + if (!(peer->snd_buff_stream = tsk_buffer_create_null())) { + return tsk_null; + } + if (!(peer->dialogs_cids = tsk_list_create())){ + return tsk_null; + } + } + return self; +} +static tsk_object_t* tsip_transport_stream_peer_dtor(tsk_object_t * self) +{ + tsip_transport_stream_peer_t *peer = self; + if(peer){ + TSK_DEBUG_INFO("*** Stream Peer destroyed ***"); + TSK_OBJECT_SAFE_FREE(peer->rcv_buff_stream); + TSK_OBJECT_SAFE_FREE(peer->snd_buff_stream); + + TSK_SAFE_FREE(peer->ws.rcv_buffer); + peer->ws.rcv_buffer_size = 0; + TSK_SAFE_FREE(peer->ws.snd_buffer); + peer->ws.snd_buffer_size = 0; + + TSK_OBJECT_SAFE_FREE(peer->dialogs_cids); + } + return self; +} +static int tsip_transport_stream_peer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + const tsip_transport_stream_peer_t *peer1 = obj1; + const tsip_transport_stream_peer_t *peer2 = obj2; + if(peer1 && peer2){ + return (peer1->local_fd - peer2->local_fd); + } + return -1; +} +static const tsk_object_def_t tsip_transport_stream_peer_def_s = +{ + sizeof(tsip_transport_stream_peer_t), + tsip_transport_stream_peer_ctor, + tsip_transport_stream_peer_dtor, + tsip_transport_stream_peer_cmp, +}; +const tsk_object_def_t *tsip_transport_stream_peer_def_t = &tsip_transport_stream_peer_def_s; diff --git a/tinySIP/src/transports/tsip_transport_ipsec.c b/tinySIP/src/transports/tsip_transport_ipsec.c new file mode 100644 index 0000000..390e999 --- /dev/null +++ b/tinySIP/src/transports/tsip_transport_ipsec.c @@ -0,0 +1,537 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/**@file tsip_transport_ipsec.c + * @brief SIP/IPSec transport. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transports/tsip_transport_ipsec.h" + +#include "tinysip/transports/tsip_transport.h" + +#include "tinysip/headers/tsip_header_Proxy_Require.h" +#include "tinysip/headers/tsip_header_Require.h" +#include "tinysip/headers/tsip_header_Security_Client.h" +#include "tinysip/headers/tsip_header_Security_Server.h" + +#include "tsip.h" + +#include "tnet_socket.h" + +#include "tsk_debug.h" + +TINYSIP_GEXTERN const tsk_object_def_t *tsip_ipsec_association_def_t; + +tsip_ipsec_association_t* tsip_ipsec_association_create(const tsip_transport_t* transport) +{ + return tsk_object_new(tsip_ipsec_association_def_t, transport); +} + +tsip_transport_ipsec_t* tsip_transport_ipsec_create(struct tsip_stack_s* stack, const char* host, tnet_port_t port, tnet_socket_type_t type, const char* description) +{ + return tsk_object_new(tsip_transport_ipsec_def_t, stack, host, port, type, description); +} + + +int tsip_transport_ipsec_createTempSAs(tsip_transport_ipsec_t* self) +{ + int ret = -1; + + /* Check */ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + /* Already have temporary SAs ? */ + if (self->asso_temporary) { + TSK_DEBUG_ERROR("IPSec transport layer already have temporary SAs"); + ret = -2; + goto bail; + } + + /* Create temporary association */ + if ((self->asso_temporary = tsip_ipsec_association_create(TSIP_TRANSPORT(self)))) { + if (self->asso_temporary->ctx && self->asso_temporary->ctx->state == tipsec_state_inbound) { + ret = 0; + } + else { + TSK_DEBUG_INFO("Failed to create new temporary SAs."); + ret = -3; + goto bail; + } + } + else { + TSK_DEBUG_INFO("Failed to create new temporary SAs."); + + ret = -4; + goto bail; + } + +bail: + + if (ret && ret != -1) { + TSK_OBJECT_SAFE_FREE(self->asso_temporary); + } + return ret; +} + +int tsip_transport_ipsec_ensureTempSAs(tsip_transport_ipsec_t* self, const tsip_response_t *r401_407, int64_t expires) +{ + int ret = -1; + struct sockaddr_storage to; + tsk_size_t index; + const tsip_header_Security_Server_t *ssHdr; + double maxQ = -2.0; /* The Q value in the SIP header will be equal to -1 by default. */ + int match = 0; + + + tipsec_spi_t spi_pc, spi_ps; + tipsec_port_t port_pc, port_ps; + tipsec_lifetime_t lifetime; + + if(!self || expires < 0){ + goto bail; + } + + lifetime = (tipsec_lifetime_t)expires; + + /* Already have temporary SAs ? */ + if(!self->asso_temporary){ + TSK_DEBUG_ERROR("Cannot ensure temporary SAs (No tempSAs)"); + ret = -2; + goto bail; + } + + /* Cleanup old Security-Verifies */ + TSK_OBJECT_SAFE_FREE(self->secVerifies); + + /* RFC 3329 - 2.3.1 Client Initiated + + When the client receives a response with a Security-Server header field, it MUST choose the security mechanism in the server's list + with the highest "q" value among all the mechanisms that are known to the client. + */ + for (index = 0; (ssHdr = (const tsip_header_Security_Server_t *)tsip_message_get_headerAt(r401_407, tsip_htype_Security_Server, index)); index++) { + tsip_header_Security_Verify_t* svHdr; + + if (maxQ > ssHdr->q || !tsk_striequals(ssHdr->mech, "ipsec-3gpp")){ + goto copy; + } + + if ((TIPSEC_ALG_FROM_STR(ssHdr->alg) == self->asso_temporary->ctx->alg) && + (TIPSEC_EALG_FROM_STR(ssHdr->ealg) == self->asso_temporary->ctx->ealg) && + (TIPSEC_PROTOCOL_FROM_STR(ssHdr->prot) == self->asso_temporary->ctx->protocol) && + (TIPSEC_MODE_FROM_STR(ssHdr->mod) == self->asso_temporary->ctx->mode)){ + + match = 1; + + maxQ = (ssHdr->q >= maxQ) ? ssHdr->q : maxQ; + spi_pc = ssHdr->spi_c; + spi_ps = ssHdr->spi_s; + port_pc = ssHdr->port_c; + port_ps = ssHdr->port_s; + } + +copy: + svHdr = tsip_header_Security_Verify_create_null(); + svHdr->mech = tsk_strdup(ssHdr->mech); + svHdr->alg = tsk_strdup(ssHdr->alg); + svHdr->prot = tsk_strdup(ssHdr->prot); + svHdr->mod = tsk_strdup(ssHdr->mod); + svHdr->ealg = tsk_strdup(ssHdr->ealg); + svHdr->port_c = ssHdr->port_c; + svHdr->port_s = ssHdr->port_s; + svHdr->spi_c = ssHdr->spi_c; + svHdr->spi_s = ssHdr->spi_s; + svHdr->q = ssHdr->q; + TSIP_HEADER_PARAMS(svHdr) = tsk_object_ref(TSIP_HEADER_PARAMS(ssHdr)); + if(!self->secVerifies){ + self->secVerifies = tsk_list_create(); + } + tsk_list_push_back_data(self->secVerifies, (void**)&svHdr); + } + + if(!match){ + TSK_DEBUG_ERROR("Failed to match security server<->security client."); + ret = -3; + goto bail; + } + + /* Set remote parameters received from 401/407 response. */ + if((ret = tipsec_ctx_set_remote(self->asso_temporary->ctx, spi_pc, spi_ps, port_pc, port_ps, lifetime))){ + TSK_DEBUG_ERROR("Failed to set remote IPSec parameters [%d]", ret); + goto bail; + } + + /* Connect Sockets: port_uc to port_ps*/ + if((ret = tnet_sockaddr_init(self->asso_temporary->ip_remote, self->asso_temporary->ctx->port_ps, TSIP_TRANSPORT(self)->type, &to))){ + TSK_DEBUG_ERROR("Invalid HOST/PORT [%s/%u].", (const char*)self->asso_temporary->ctx->addr_remote, self->asso_temporary->ctx->port_ps); + goto bail; + } + if((ret = tnet_sockfd_connectto(self->asso_temporary->socket_uc->fd, &to))){ + TSK_DEBUG_ERROR("Failed to connect port_uc to port_ps."); + goto bail; + } + +bail: + return ret; +} + +int tsip_transport_ipsec_startSAs(tsip_transport_ipsec_t* self, const tipsec_key_t* ik, const tipsec_key_t* ck) +{ + int ret = -1; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + if (!self->asso_temporary) { + TSK_DEBUG_ERROR("Failed to find temporary SAs"); + ret = -2; + goto bail; + } + + /* Promote tempSAs (temp => active) */ + TSK_OBJECT_SAFE_FREE(self->asso_active); /* delete old active SAs */ + self->asso_active = tsk_object_ref((void*)self->asso_temporary); /* promote */ + TSK_OBJECT_SAFE_FREE(self->asso_temporary); /* delete old temp SAs */ + + if ((ret = tipsec_ctx_set_keys(self->asso_active->ctx, ik, ck)) == 0){ + ret = tipsec_ctx_start(self->asso_active->ctx); + } + +bail: + return ret; +} + +int tsip_transport_ipsec_cleanupSAs(tsip_transport_ipsec_t* self) +{ + int ret = -1; + + if(!self){ + goto bail; + } + + TSK_OBJECT_SAFE_FREE(self->asso_temporary); + TSK_OBJECT_SAFE_FREE(self->asso_active); + +bail: + return ret; +} + +int tsip_transport_ipsec_updateMSG(tsip_transport_ipsec_t* self, tsip_message_t *msg) +{ + int ret = -1; + const tsip_ipsec_association_t* asso; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + asso = (self->asso_temporary && TSIP_REQUEST_IS_REGISTER(msg)) ? self->asso_temporary : self->asso_active; + if (!asso || !asso->ctx) { + TSK_DEBUG_ERROR("No IPSec association found."); + ret = -2; + goto bail; + } + + if (TSIP_MESSAGE_IS_RESPONSE(msg)) { + return 0; + } + + /* Security-Client, Require, Proxy-Require and Security Verify */ + switch(msg->line.request.request_type) { + case tsip_BYE: + case tsip_INVITE: + case tsip_OPTIONS: + case tsip_REGISTER: + case tsip_SUBSCRIBE: + case tsip_NOTIFY: + case tsip_REFER: + case tsip_INFO: + case tsip_UPDATE: + case tsip_MESSAGE: + case tsip_PUBLISH: + case tsip_PRACK: + { + const tsk_list_item_t *item; + TSIP_MESSAGE_ADD_HEADER(msg, TSIP_HEADER_SECURITY_CLIENT_VA_ARGS("ipsec-3gpp", + TIPSEC_ALG_TO_STR(asso->ctx->alg), + TIPSEC_PROTOCOL_TO_STR(asso->ctx->protocol), + TIPSEC_MODE_TO_STR(asso->ctx->mode), + TIPSEC_EALG_TO_STR(asso->ctx->ealg), + asso->ctx->port_uc, + asso->ctx->port_us, + asso->ctx->spi_uc, + asso->ctx->spi_us + )); + /* RFC 3329 - 2.3.1 Client Initiated + All the subsequent SIP requests sent by the client to that server + SHOULD make use of the security mechanism initiated in the previous + step. These requests MUST contain a Security-Verify header field + that mirrors the server's list received previously in the Security- + Server header field. These requests MUST also have both a Require + and Proxy-Require header fields with the value "sec-agree". + */ + tsk_list_foreach(item, self->secVerifies){ + tsip_message_add_header(msg, (const tsip_header_t*)item->data); + } + TSIP_MESSAGE_ADD_HEADER(msg, TSIP_HEADER_REQUIRE_VA_ARGS("sec-agree")); + TSIP_MESSAGE_ADD_HEADER(msg, TSIP_HEADER_PROXY_REQUIRE_VA_ARGS("sec-agree")); + break; + } + + default: break; + } + + ret = 0; + + /* Add Security-Server headers */ +bail: + return ret; +} + +tnet_fd_t tsip_transport_ipsec_getFD(tsip_transport_ipsec_t* self, int isRequest) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return TNET_INVALID_FD; + } + + /* If no active SAs ca be found then use default connection. */ + if (!self->asso_active) { + return TNET_INVALID_FD; + // return TSIP_TRANSPORT(self)->connectedFD; + } + + /* IPSec ports management + For more information: http://betelco.blogspot.com/2008/09/ipsec-using-security-agreement-in-3gpp.html + */ + + if (TNET_SOCKET_TYPE_IS_DGRAM(TSIP_TRANSPORT(self)->type)) { + /* + === UDP === + port_uc -> REGISTER -> port_ps + port_ps <- 200 OK <- port_pc + */ + return self->asso_active->socket_uc->fd; + } + else { + /* + === TCP === + port_uc -> REGISTER -> port_ps + port_uc <- 200 OK <- port_ps + + port_us <- NOTIFY <- port_pc + port_us -> 200 OK -> port_pc + */ + if (isRequest) { + return self->asso_active->socket_uc->fd; + } + else { + return self->asso_active->socket_us->fd; + } + } + + return TNET_INVALID_FD; +} + + + + + + + + + +//======================================================== +// SIP/IPSec transport object definition +// +static tsk_object_t* tsip_transport_ipsec_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transport_ipsec_t *transport = self; + if(transport){ + const struct tsip_stack_s *stack = va_arg(*app, const struct tsip_stack_s*); + const char *host = va_arg(*app, const char*); +#if defined(__GNUC__) + tnet_port_t port = (tnet_port_t)va_arg(*app, unsigned); +#else + tnet_port_t port = va_arg(*app, tnet_port_t); +#endif + tnet_socket_type_t type = va_arg(*app, tnet_socket_type_t); + const char *description = va_arg(*app, const char*); + + /* init base */ + tsip_transport_init(TSIP_TRANSPORT(transport), type, stack, host, port, description); + } + return self; +} + +static tsk_object_t* tsip_transport_ipsec_dtor(tsk_object_t * self) +{ + tsip_transport_ipsec_t *transport = self; + if(transport){ + /* deinit base */ + tsip_transport_deinit(TSIP_TRANSPORT(transport)); + + /* deinit self */ + tsip_transport_ipsec_cleanupSAs(transport); + + TSK_OBJECT_SAFE_FREE(transport->secVerifies); + } + return self; +} + +static int tsip_transport_ipsec_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + const tsip_transport_ipsec_t *transport1 = obj1; + const tsip_transport_ipsec_t *transport2 = obj2; + if(transport1 && transport2){ + const char* desc1 = tsip_transport_get_description(TSIP_TRANSPORT(transport1)); + const char* desc2 = tsip_transport_get_description(TSIP_TRANSPORT(transport2)); + return tsk_stricmp(desc1, desc2); + } + return -1; +} + +static const tsk_object_def_t tsip_transport_ipsec_def_s = +{ + sizeof(tsip_transport_ipsec_t), + tsip_transport_ipsec_ctor, + tsip_transport_ipsec_dtor, + tsip_transport_ipsec_cmp, +}; +const tsk_object_def_t *tsip_transport_ipsec_def_t = &tsip_transport_ipsec_def_s; + + + + + + + + + + + +//================================================================================================= +// IPSec association object definition +// +static tsk_object_t* tsip_ipsec_association_ctor(tsk_object_t * self, va_list * app) +{ + tsip_ipsec_association_t *association = self; + if(association){ + + const tsip_transport_t* transport = va_arg(*app, const tsip_transport_t *); + + /* Set transport */ + association->transport = transport; + + /* Get local IP and port. */ + tsip_transport_get_ip_n_port(transport, &association->ip_local, &association->port_local); + + /* Create IPSec context */ + if (tipsec_ctx_create( + TIPSEC_IPPROTO_FROM_STR(transport->protocol), + TNET_SOCKET_TYPE_IS_IPV6(transport->type), + TIPSEC_MODE_FROM_STR(transport->stack->security.ipsec.mode), + TIPSEC_EALG_FROM_STR(transport->stack->security.ipsec.ealg), + TIPSEC_ALG_FROM_STR(transport->stack->security.ipsec.alg), + TIPSEC_PROTOCOL_FROM_STR(transport->stack->security.ipsec.protocol), &association->ctx)) + { + TSK_DEBUG_ERROR("Failed to create IPSec context"); + return tsk_null; + } + + /* Create Both client and Server legs */ + association->socket_us = tnet_socket_create(association->ip_local, TNET_SOCKET_PORT_ANY, transport->type); + association->socket_uc = tnet_socket_create(association->ip_local, TNET_SOCKET_PORT_ANY, transport->type); + + /* Add Both sockets to the network transport */ + tsip_transport_add_socket(transport, association->socket_us->fd, transport->type, 0, 0); + tsip_transport_add_socket(transport, association->socket_uc->fd, transport->type, 0, 1); + + /* Set local */ + if (tnet_get_peerip(transport->connectedFD, &association->ip_remote) == 0) { /* Get remote IP string */ + if (tipsec_ctx_set_local(association->ctx, association->ip_local, association->ip_remote, association->socket_uc->port, association->socket_us->port)) { + TSK_DEBUG_ERROR("Failed to set IPSec local info:%s,%s,%u,%u", association->ip_local, association->ip_remote, association->socket_uc->port, association->socket_us->port); + return tsk_null; + } + } + else { + // Resolve the HostName because "tipsec_ctx_set_local()" requires IP address instead of FQDN. + if (tnet_resolve(transport->stack->network.proxy_cscf[transport->stack->network.transport_idx_default], + transport->stack->network.proxy_cscf_port[transport->stack->network.transport_idx_default], + transport->stack->network.proxy_cscf_type[transport->stack->network.transport_idx_default], + &association->ip_remote, tsk_null)) + { + return tsk_null; + } + if (tipsec_ctx_set_local(association->ctx, + association->ip_local, + association->ip_remote, + association->socket_uc->port, + association->socket_us->port)) + { + return tsk_null; + } + } + } + return self; +} + +static tsk_object_t* tsip_ipsec_association_dtor(tsk_object_t * self) +{ + tsip_ipsec_association_t *association = self; + if(association){ + TSK_OBJECT_SAFE_FREE(association->ctx); + + /* Remove Both sockets from the network transport and delete them. */ + if(association->socket_uc){ + tsip_transport_remove_socket(association->transport, &association->socket_uc->fd); + TSK_OBJECT_SAFE_FREE(association->socket_uc); + } + if(association->socket_us){ + tsip_transport_remove_socket(association->transport, &association->socket_us->fd); + TSK_OBJECT_SAFE_FREE(association->socket_us); + } + } + return self; +} + +static int tsip_ipsec_association_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return -1; +} + +static const tsk_object_def_t tsip_ipsec_association_def_s = +{ + sizeof(tsip_ipsec_association_t), + tsip_ipsec_association_ctor, + tsip_ipsec_association_dtor, + tsip_ipsec_association_cmp, +}; +const tsk_object_def_t *tsip_ipsec_association_def_t = &tsip_ipsec_association_def_s; diff --git a/tinySIP/src/transports/tsip_transport_layer.c b/tinySIP/src/transports/tsip_transport_layer.c new file mode 100644 index 0000000..93668b9 --- /dev/null +++ b/tinySIP/src/transports/tsip_transport_layer.c @@ -0,0 +1,1403 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/**@file tsip_transport_layer.c + * @brief SIP transport layer. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transports/tsip_transport_layer.h" + +#include "tinysip/transports/tsip_transport_ipsec.h" + +#include "tinysip/transactions/tsip_transac_layer.h" +#include "tinysip/dialogs/tsip_dialog_layer.h" + +#include "tinysip/parsers/tsip_parser_message.h" + +#include "tinysip/headers/tsip_header_Route.h" + +#include "tinyhttp.h" + +#include "tsk_thread.h" +#include "tsk_debug.h" + +static const char* __null_callid = tsk_null; + +/* max size of a chunck to form a valid SIP message */ +#define TSIP_MAX_STREAM_CHUNCK_SIZE 0xFFFF +/* min size of a chunck to form a valid SIP message +* (Request/Response)-Line, Via, From, To, Call-Id, CSeq and Content-Length +* Tests have been done with both compact and full headers */ +#define TSIP_MIN_STREAM_CHUNCK_SIZE 0xA0 + +#if !defined(TSIP_CONNECT_TIMEOUT) +# define TSIP_CONNECT_TIMEOUT 1000 +#endif + +extern tsip_event_t* tsip_event_create(tsip_ssession_t* ss, short code, const char* phrase, const tsip_message_t* sipmessage, tsip_event_type_t type); + +tsip_transport_layer_t* tsip_transport_layer_create(tsip_stack_t *stack) +{ + return tsk_object_new(tsip_transport_layer_def_t, stack); +} + +const tsip_transport_t* tsip_transport_layer_find_by_type(const tsip_transport_layer_t* self, tnet_socket_type_t type) +{ + const tsk_list_item_t *item; + const tsip_transport_t* transport = tsk_null; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsk_list_lock(self->transports); + + tsk_list_foreach(item, self->transports){ + if(((const tsip_transport_t*)item->data)->type == type){ + transport = ((const tsip_transport_t*)item->data); + break; + } + } + + tsk_list_unlock(self->transports); + + return transport; +} + +const tsip_transport_t* tsip_transport_layer_find_by_idx(const tsip_transport_layer_t* self, int32_t idx) +{ + const tsk_list_item_t *item; + const tsip_transport_t* transport = tsk_null; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsk_list_lock(self->transports); + + tsk_list_foreach(item, self->transports){ + if(((const tsip_transport_t*)item->data)->idx == idx){ + transport = ((const tsip_transport_t*)item->data); + break; + } + } + + tsk_list_unlock(self->transports); + + return transport; +} + +int tsip_transport_layer_handle_incoming_msg(const tsip_transport_t *transport, tsip_message_t *message) +{ + int ret = -1; + + if(message){ + const tsip_transac_layer_t *layer_transac = transport->stack->layer_transac; + const tsip_dialog_layer_t *layer_dialog = transport->stack->layer_dialog; + + if((ret = tsip_transac_layer_handle_incoming_msg(layer_transac, message))){ + /* NO MATCHING TRANSACTION FOUND ==> LOOK INTO DIALOG LAYER */ + ret = tsip_dialog_layer_handle_incoming_msg(layer_dialog, message); + } + } + return ret; +} + +/*== Non-blocking callback function (STREAM: TCP, TLS and SCTP) */ +static int tsip_transport_layer_stream_cb(const tnet_transport_event_t* e) +{ + int ret = -1; + tsk_ragel_state_t state; + tsip_message_t *message = tsk_null; + int endOfheaders = -1; + tsip_transport_t *transport = (tsip_transport_t *)e->callback_data; + tsip_transport_stream_peer_t* peer; + + switch(e->type){ + case event_data: { + TSK_DEBUG_INFO("\n\nRECV:%.*s\n\n", e->size, (const char*)e->data); + break; + } + case event_closed: + case event_error: + case event_removed: + { + tsip_transport_stream_peer_t* peer; + TSK_DEBUG_INFO("Stream Peer closed - %d", e->local_fd); + // signal "peer disconnected" before "stack disconnected" + if((peer = tsip_transport_pop_stream_peer_by_local_fd(transport, e->local_fd))){ + tsip_dialog_layer_signal_peer_disconnected(TSIP_STACK(transport->stack)->layer_dialog, peer); + TSK_OBJECT_SAFE_FREE(peer); + } + else { + TSK_DEBUG_INFO("Closed peer with fd=%d not registered yet", e->local_fd); + } + // connectedFD== master's fd for servers + if(transport->connectedFD == e->local_fd || transport->connectedFD == TNET_INVALID_FD){ + TSK_DEBUG_INFO("SIP 'connectedFD' (%d) closed", transport->connectedFD); + if(transport->stack){ + tsip_event_t* e; + // signal to all dialogs that transport error raised + tsip_dialog_layer_signal_stack_disconnected(TSIP_STACK(transport->stack)->layer_dialog); + // signal to the end-user that the stack is disconnected + if((e = tsip_event_create(tsk_null, tsip_event_code_stack_disconnected, "Stack disconnected", tsk_null, tsip_event_stack))){ + TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(transport->stack), e); + } + } + transport->connectedFD = TNET_INVALID_FD; + } + return 0; + } + case event_connected: + case event_accepted: + { + tsip_transport_stream_peer_t* peer; + TSK_DEBUG_INFO("Stream Peer accepted/connected - %d", e->local_fd); + // find peer + if((peer = tsip_transport_find_stream_peer_by_local_fd(transport, e->local_fd))){ + peer->connected = tsk_true; + if(peer->snd_buff_stream->data && peer->snd_buff_stream->size > 0){ // is there pending outgoing data postponed until socket get connected? + tnet_transport_send(transport->net_transport, peer->local_fd, peer->snd_buff_stream->data, peer->snd_buff_stream->size); + tsk_buffer_cleanup(peer->snd_buff_stream); + } + TSK_OBJECT_SAFE_FREE(peer); + } + else{ + // on iOS (cfsocket implementation) opening the master stream raise "connected" callback which is not correct. + // Ignoring the socket is not a problem as we'll get a callback event ("accepted") when a client connects to the master. + // The master cannot raise "connected" even as it's already in "listening" state +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000) + if(tnet_transport_get_master_fd(transport->net_transport) == e->local_fd){ + return 0; + } +#endif + return tsip_transport_add_stream_peer(transport, e->local_fd, transport->type, tsk_true); + } + } + default:{ + return 0; + } + } + + if(!(peer = tsip_transport_find_stream_peer_by_local_fd(transport, e->local_fd))){ + TSK_DEBUG_ERROR("Failed to find peer with local fd equal to %d", e->local_fd); + return -1; + } + + /* Update latest time activity */ + peer->time_latest_activity = tsk_time_now(); + + /* RFC 3261 - 7.5 Framing SIP Messages + + Unlike HTTP, SIP implementations can use UDP or other unreliable + datagram protocols. Each such datagram carries one request or + response. See Section 18 on constraints on usage of unreliable + transports. + + Implementations processing SIP messages over stream-oriented + transports MUST ignore any CRLF appearing before the start-line + [H4.1]. + + The Content-Length header field value is used to locate the end of + each SIP message in a stream. It will always be present when SIP + messages are sent over stream-oriented transports. + */ + + /* Check if buffer is too big to be valid (have we missed some chuncks?) */ + if(TSK_BUFFER_SIZE(peer->rcv_buff_stream) >= TSIP_MAX_STREAM_CHUNCK_SIZE){ + TSK_DEBUG_ERROR("TCP Buffer is too big to be valid"); + tsk_buffer_cleanup(peer->rcv_buff_stream); + } + + /* === SigComp === */ + if(TSIP_IS_SIGCOMP_DATA(e->data)){ + char SigCompBuffer[TSIP_SIGCOMP_MAX_BUFF_SIZE]; + //====== FIXME: This implmentation will always use the first SigComp-Id for decompression ===== + tsk_bool_t is_nack; + const char* comp_id; + void* nack_data = tsk_null; + tsk_size_t data_size, next_size; + + // First Pass + comp_id = tsip_sigcomp_handler_fixme_getcompid(transport->stack->sigcomp.handle); + data_size = tsip_sigcomp_handler_uncompressTCP(transport->stack->sigcomp.handle, comp_id, e->data, e->size, SigCompBuffer, sizeof(SigCompBuffer), &is_nack); + + if(data_size){ + if(is_nack){ + tsip_transport_send_raw(transport, tsk_null, 0, SigCompBuffer, data_size, __null_callid); + } + else{ + // append result + tsk_buffer_append(peer->rcv_buff_stream, SigCompBuffer, data_size); + } + } + else{ /* Partial message? */ + TSK_DEBUG_ERROR("SigComp decompression has failed"); + return 0; + } + // Query for all other chuncks + while((next_size = tsip_sigcomp_handler_uncompress_next(transport->stack->sigcomp.handle, comp_id, &nack_data, &is_nack)) || nack_data){ + if(is_nack){ + tsip_transport_send_raw(transport, tsk_null, 0, nack_data, next_size, __null_callid); + TSK_FREE(nack_data); + } + else{ + // append result + tsk_buffer_append(peer->rcv_buff_stream, SigCompBuffer, (next_size - data_size)); + data_size = next_size; + } + } + } + else{ + /* Append new content. */ + tsk_buffer_append(peer->rcv_buff_stream, e->data, e->size); + } + + /* Check if we have all SIP/WS headers. */ +parse_buffer: + if((endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(peer->rcv_buff_stream),TSK_BUFFER_SIZE(peer->rcv_buff_stream), "\r\n\r\n"/*2CRLF*/)) < 0){ + TSK_DEBUG_INFO("No all SIP headers in the TCP buffer."); + goto bail; + } + + /* If we are there this mean that we have all SIP headers. + * ==> Parse the SIP message without the content. + */ + tsk_ragel_state_init(&state, TSK_BUFFER_DATA(peer->rcv_buff_stream), endOfheaders + 4/*2CRLF*/); + if(tsip_message_parse(&state, &message, tsk_false/* do not extract the content */) == tsk_true){ + tsk_size_t clen = TSIP_MESSAGE_CONTENT_LENGTH(message); /* MUST have content-length header (see RFC 3261 - 7.5). If no CL header then the macro return zero. */ + if(clen == 0){ /* No content */ + tsk_buffer_remove(peer->rcv_buff_stream, 0, (endOfheaders + 4/*2CRLF*/)); /* Remove SIP headers and CRLF */ + } + else{ /* There is a content */ + if((endOfheaders + 4/*2CRLF*/ + clen) > TSK_BUFFER_SIZE(peer->rcv_buff_stream)){ /* There is content but not all the content. */ + TSK_DEBUG_INFO("No all SIP content in the TCP buffer (clen=%u and %u > %u).", clen, (endOfheaders + 4/*2CRLF*/ + clen), TSK_BUFFER_SIZE(peer->rcv_buff_stream)); + goto bail; + } + else{ + /* Add the content to the message. */ + tsip_message_add_content(message, tsk_null, TSK_BUFFER_TO_U8(peer->rcv_buff_stream) + endOfheaders + 4/*2CRLF*/, clen); + /* Remove SIP headers, CRLF and the content. */ + tsk_buffer_remove(peer->rcv_buff_stream, 0, (endOfheaders + 4/*2CRLF*/ + clen)); + } + } + } + else { + TSK_DEBUG_ERROR("Failed to parse pending stream....reset buffer"); + tsk_buffer_cleanup(peer->rcv_buff_stream); + } + + if(message && message->firstVia && message->Call_ID && message->CSeq && message->From && message->To){ + /* Signal we got at least one valid SIP message */ + peer->got_valid_sip_msg = tsk_true; + /* Set fd */ + message->local_fd = e->local_fd; + message->src_net_type = transport->type; + /* Alert transaction/dialog layer */ + ret = tsip_transport_layer_handle_incoming_msg(transport, message); + /* Parse next chunck */ + if(TSK_BUFFER_SIZE(peer->rcv_buff_stream) >= TSIP_MIN_STREAM_CHUNCK_SIZE){ + /* message already passed to the dialog/transac layers */ + TSK_OBJECT_SAFE_FREE(message); + goto parse_buffer; + } + } + else{ + TSK_DEBUG_ERROR("Failed to parse SIP message"); + ret = -15; + } + +bail: + TSK_OBJECT_SAFE_FREE(message); + TSK_OBJECT_SAFE_FREE(peer); + + return ret; +} + +/*== Non-blocking callback function (STREAM: WS, WSS) */ +static int tsip_transport_layer_ws_cb(const tnet_transport_event_t* e) +{ + int ret = -1; + tsk_ragel_state_t state; + tsip_message_t *message = tsk_null; + int endOfheaders = -1; + tsip_transport_t *transport = (tsip_transport_t *)e->callback_data; + tsk_bool_t check_end_of_hdrs = tsk_true; + tsk_bool_t go_message = tsk_false; + uint64_t data_len = 0; + uint64_t pay_len = 0; + tsip_transport_stream_peer_t* peer; + + switch(e->type){ + case event_data: { + break; + } + case event_closed: + case event_error: + case event_removed: + { + tsip_transport_stream_peer_t* peer; + TSK_DEBUG_INFO("WebSocket Peer closed with fd = %d", e->local_fd); + if((peer = tsip_transport_pop_stream_peer_by_local_fd(transport, e->local_fd))){ + tsip_dialog_layer_signal_peer_disconnected(TSIP_STACK(transport->stack)->layer_dialog, peer); + TSK_OBJECT_SAFE_FREE(peer); + } + return 0; + } + case event_accepted: + case event_connected: + { + TSK_DEBUG_INFO("WebSocket Peer accepted/connected with fd = %d", e->local_fd); + return tsip_transport_add_stream_peer(transport, e->local_fd, transport->type, tsk_true); + } + default:{ + return 0; + } + } + + if(!(peer = tsip_transport_find_stream_peer_by_local_fd(transport, e->local_fd))){ + TSK_DEBUG_ERROR("Failed to find peer with local fd equal to %d", e->local_fd); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + return -1; + } + + /* Update latest time activity */ + peer->time_latest_activity = tsk_time_now(); + + /* Check if buffer is too big to be valid (have we missed some chuncks?) */ + if((TSK_BUFFER_SIZE(peer->rcv_buff_stream) + e->size) >= TSIP_MAX_STREAM_CHUNCK_SIZE){ + TSK_DEBUG_ERROR("TCP Buffer is too big to be valid"); + tsk_buffer_cleanup(peer->rcv_buff_stream); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + + // Append new content + tsk_buffer_append(peer->rcv_buff_stream, e->data, e->size); + + /* Check if WebSocket data */ + if(peer->rcv_buff_stream->size > 4){ + const uint8_t* pdata = (const uint8_t*)peer->rcv_buff_stream->data; + tsk_bool_t is_GET = (pdata[0] == 'G' && pdata[1] == 'E' && pdata[2] == 'T'); + if (!peer->ws.handshaking_done && !is_GET) { + TSK_DEBUG_ERROR("WS handshaking not done yet"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + check_end_of_hdrs = is_GET; + } + + /* Check if we have all HTTP/SIP/WS headers. */ +parse_buffer: + if(check_end_of_hdrs && (endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(peer->rcv_buff_stream),TSK_BUFFER_SIZE(peer->rcv_buff_stream), "\r\n\r\n"/*2CRLF*/)) < 0){ + TSK_DEBUG_INFO("No all headers in the WS buffer"); + goto bail; + } + + /* WebSocket handling*/ + if(peer->rcv_buff_stream->size > 4){ + const uint8_t* pdata = (const uint8_t*)peer->rcv_buff_stream->data; + + /* WebSocket Handshake */ + if(pdata[0] == 'G' && pdata[1] == 'E' && pdata[2] == 'T'){ + thttp_message_t *http_req = thttp_message_create(); + thttp_response_t *http_resp = tsk_null; + tsk_buffer_t *http_buff = tsk_null; + const thttp_header_Sec_WebSocket_Protocol_t* http_hdr_proto; + const thttp_header_Sec_WebSocket_Key_t* http_hdr_key; + const char* msg_start = (const char*)peer->rcv_buff_stream->data; + const char* msg_end = (msg_start + peer->rcv_buff_stream->size); + int32_t idx; + + if((idx = tsk_strindexOf(msg_start, (msg_end - msg_start), "\r\n")) > 2){ + TSK_DEBUG_INFO("WebSocket handshake message: %.*s", (msg_end - msg_start), msg_start); + msg_start += (idx + 2); // skip request header + while(msg_start < msg_end){ + if((idx = tsk_strindexOf(msg_start, (msg_end - msg_start), "\r\n")) <= 2){ + break; + } + idx+= 2; + tsk_ragel_state_init(&state, msg_start, idx); + if((ret = thttp_header_parse(&state, http_req))){ + TSK_DEBUG_ERROR("Failed to parse header: %s", msg_start); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + msg_start += idx; + } + } + + // get key header + if(!(http_hdr_key = (const thttp_header_Sec_WebSocket_Key_t*)thttp_message_get_header(http_req, thttp_htype_Sec_WebSocket_Key))){ + TSK_DEBUG_ERROR("No 'Sec-WebSocket-Key' header"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + + + if(http_hdr_key && (http_hdr_proto = (const thttp_header_Sec_WebSocket_Protocol_t*)thttp_message_get_header(http_req, thttp_htype_Sec_WebSocket_Protocol))){ + if(tsk_list_find_object_by_pred((const tsk_list_t*)http_hdr_proto->values, tsk_string_pred_icmp, "sip")){ + // send response + if((http_resp = thttp_response_new((short)101, "Switching Protocols", http_req))){ + // compute response key + thttp_auth_ws_keystring_t key_resp = {0}; + thttp_auth_ws_response(http_hdr_key->value, &key_resp); + + thttp_message_add_headers_2(http_resp, + THTTP_HEADER_DUMMY_VA_ARGS("Upgrade", "websocket"), + THTTP_HEADER_DUMMY_VA_ARGS("Connection", "Upgrade"), + THTTP_HEADER_SEC_WEBSOCKET_ACCEPT_VA_ARGS(key_resp), + THTTP_HEADER_SEC_WEBSOCKET_PROTOCOL_VA_ARGS("sip"), + THTTP_HEADER_SEC_WEBSOCKET_VERSION_VA_ARGS("13"), + tsk_null); + + // serialize response + if((http_buff = tsk_buffer_create_null())){ + thttp_message_serialize(http_resp, http_buff); + // TSK_DEBUG_INFO("WS response=%s", http_buff->data); + // send response + if((tnet_transport_send(transport->net_transport, e->local_fd, http_buff->data, http_buff->size)) != http_buff->size){ + TSK_DEBUG_ERROR("Failed to send reponse"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + } + else { + TSK_DEBUG_ERROR("Failed to create buffer"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + peer->ws.handshaking_done = tsk_true; // WS handshaking done + } + } + else{ + TSK_DEBUG_ERROR("Not SIP protocol"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + } + else{ + TSK_DEBUG_ERROR("No 'Sec-WebSocket-Protocol' header"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + goto bail; + } + + tsk_buffer_remove(peer->rcv_buff_stream, 0, (endOfheaders + 4/*2CRLF*/)); /* Remove HTTP headers and CRLF */ + TSK_OBJECT_SAFE_FREE(http_req); + TSK_OBJECT_SAFE_FREE(http_resp); + TSK_OBJECT_SAFE_FREE(http_buff); + } /* end-of WebSocket handshake */ + + /* WebSocket data */ + else{ + const uint8_t opcode = pdata[0] & 0x0F; + if((pdata[0] & 0x01)/* FIN */){ + const uint8_t mask_flag = (pdata[1] >> 7); // Must be "1" for "client -> server" + uint8_t mask_key[4] = { 0x00 }; + uint64_t pay_idx; + uint8_t* pws_rcv_buffer; + + if(pdata[0] & 0x40 || pdata[0] & 0x20 || pdata[0] & 0x10){ + TSK_DEBUG_ERROR("Unknown extension: %d", (pdata[0] >> 4) & 0x07); + tsk_buffer_cleanup(peer->rcv_buff_stream); + goto bail; + } + + pay_len = pdata[1] & 0x7F; + data_len = 2; + + if(pay_len == 126){ + if(peer->rcv_buff_stream->size < 4) { TSK_DEBUG_WARN("Too short"); goto bail; } + pay_len = (pdata[2] << 8 | pdata[3]); + pdata = &pdata[4]; + data_len += 2; + } + else if(pay_len == 127){ + if((peer->rcv_buff_stream->size - data_len) < 8) { TSK_DEBUG_WARN("Too short"); goto bail; } + pay_len = (((uint64_t)pdata[2]) << 56 | ((uint64_t)pdata[3]) << 48 | ((uint64_t)pdata[4]) << 40 | ((uint64_t)pdata[5]) << 32 | ((uint64_t)pdata[6]) << 24 | ((uint64_t)pdata[7]) << 16 | ((uint64_t)pdata[8]) << 8 || ((uint64_t)pdata[9])); + pdata = &pdata[10]; + data_len += 8; + } + else{ + pdata = &pdata[2]; + } + + if(mask_flag){ // must be "true" + if((peer->rcv_buff_stream->size - data_len) < 4) { TSK_DEBUG_WARN("Too short"); goto bail; } + mask_key[0] = pdata[0]; + mask_key[1] = pdata[1]; + mask_key[2] = pdata[2]; + mask_key[3] = pdata[3]; + pdata = &pdata[4]; + data_len += 4; + } + + if((peer->rcv_buff_stream->size - data_len) < pay_len){ + TSK_DEBUG_INFO("No all data in the WS buffer"); + goto bail; + } + + // create ws buffer tohold unmasked data + if(peer->ws.rcv_buffer_size < pay_len){ + if(!(peer->ws.rcv_buffer = tsk_realloc(peer->ws.rcv_buffer, (tsk_size_t)pay_len))){ + TSK_DEBUG_ERROR("Failed to allocate buffer of size %lld", pay_len); + peer->ws.rcv_buffer_size = 0; + goto bail; + } + peer->ws.rcv_buffer_size = (tsk_size_t)pay_len; + } + + pws_rcv_buffer = (uint8_t*)peer->ws.rcv_buffer; + data_len += pay_len; + + // unmasking the payload + for(pay_idx = 0; pay_idx < pay_len; ++pay_idx){ + pws_rcv_buffer[pay_idx] = (pdata[pay_idx] ^ mask_key[(pay_idx & 3)]); + } + + go_message = tsk_true; + } + else if(opcode == 0x08){ // RFC6455 - 5.5.1. Close + TSK_DEBUG_INFO("WebSocket opcode 0x8 (Close)"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + } + } + }/* end-of WebSocket handling */ + + // skip SIP message parsing if websocket transport + + if(!go_message){ + goto bail; + } + + // If we are there this mean that we have all SIP headers. + // ==> Parse the SIP message without the content. + TSK_DEBUG_INFO("Receiving SIP o/ WebSocket message: %.*s", pay_len, (const char*)peer->ws.rcv_buffer); + tsk_ragel_state_init(&state, peer->ws.rcv_buffer, (tsk_size_t)pay_len); + if (tsip_message_parse(&state, &message, tsk_false/* do not extract the content */) == tsk_true) { + const uint8_t* body_start = (const uint8_t*)state.eoh; + int64_t clen = (pay_len - (int64_t)(body_start - ((const uint8_t*)peer->ws.rcv_buffer))); + if (clen > 0) { + // Add the content to the message. */ + tsip_message_add_content(message, tsk_null, body_start, (tsk_size_t)clen); + } + tsk_buffer_remove(peer->rcv_buff_stream, 0, (tsk_size_t)data_len); + } + + if(message && message->firstVia && message->Call_ID && message->CSeq && message->From && message->To){ + /* Signal we got at least one valid SIP message */ + peer->got_valid_sip_msg = tsk_true; + /* Set fd */ + message->local_fd = e->local_fd; + message->src_net_type = transport->type; + /* Alert transaction/dialog layer */ + ret = tsip_transport_layer_handle_incoming_msg(transport, message); + /* Parse next chunck */ + if(TSK_BUFFER_SIZE(peer->rcv_buff_stream) >= TSIP_MIN_STREAM_CHUNCK_SIZE){ + /* message already passed to the dialog/transac layers */ + TSK_OBJECT_SAFE_FREE(message); + goto parse_buffer; + } + } + else{ + TSK_DEBUG_ERROR("Failed to parse SIP message"); + tsip_transport_remove_socket(transport, (tnet_fd_t *)&e->local_fd); + ret = -15; + goto bail; + } + +bail: + TSK_OBJECT_SAFE_FREE(message); + TSK_OBJECT_SAFE_FREE(peer); + + return ret; +} + +/*== Non-blocking callback function (DGRAM: UDP) */ +static int tsip_transport_layer_dgram_cb(const tnet_transport_event_t* e) +{ + int ret = -1; + tsk_ragel_state_t state; + tsip_message_t *message = tsk_null; + const tsip_transport_t *transport = e->callback_data; + const char* data_ptr; + tsk_size_t data_size; + char SigCompBuffer[TSIP_SIGCOMP_MAX_BUFF_SIZE]; + + switch(e->type){ + case event_data: { + TSK_DEBUG_INFO("\n\nRECV:%.*s\n\n", e->size, (const char*)e->data); + break; + } + case event_closed: + case event_connected: + default:{ + return 0; + } + } + + /* === SigComp === */ + if(TSIP_IS_SIGCOMP_DATA(e->data)){ + //====== + // FIXME: This implmentation will always use the first SigComp-Id for decompression + // The destination addr will always be the pcscf which will not work for server mode + //===== + tsk_bool_t is_nack; + const char* comp_id; + + comp_id = tsip_sigcomp_handler_fixme_getcompid(transport->stack->sigcomp.handle); + data_size = tsip_sigcomp_handler_uncompressUDP(transport->stack->sigcomp.handle, comp_id, e->data, e->size, SigCompBuffer, sizeof(SigCompBuffer), &is_nack); + data_ptr = SigCompBuffer; + if(data_size){ + if(is_nack){ + tsip_transport_send_raw(transport, tsk_null, 0, data_ptr, data_size, __null_callid); + return 0; + } + } + else{ + TSK_DEBUG_ERROR("SigComp decompression has failed"); + return -2; + } + } + else{ + data_ptr = e->data; + data_size = e->size; + } + + if (data_size == 4 && ((data_ptr[0] == '\r' && data_ptr[1] == '\n'&& data_ptr[2] == '\r' && data_ptr[3] == '\n') || (data_ptr[0] == 0x00 && data_ptr[1] == 0x00 && data_ptr[2] == 0x00 && data_ptr[3] == 0x00))) { + TSK_DEBUG_INFO("2CRLF"); + tsip_transport_send_raw(transport, tsk_null, 0, data_ptr, data_size, __null_callid); + return 0; + } + + tsk_ragel_state_init(&state, data_ptr, data_size); + if(tsip_message_parse(&state, &message, tsk_true) == tsk_true + && message->firstVia && message->Call_ID && message->CSeq && message->From && message->To) + { + /* Set local fd used to receive the message and the address of the remote peer */ + message->local_fd = e->local_fd; + message->remote_addr = e->remote_addr; + message->src_net_type = transport->type; + + /* RFC 3581 - 4. Server Behavior + When a server compliant to this specification (which can be a proxy + or UAS) receives a request, it examines the topmost Via header field + value. If this Via header field value contains an "rport" parameter + with no value, it MUST set the value of the parameter to the source + port of the request. This is analogous to the way in which a server + will insert the "received" parameter into the topmost Via header + field value. In fact, the server MUST insert a "received" parameter + containing the source IP address that the request came from, even if + it is identical to the value of the "sent-by" component. Note that + this processing takes place independent of the transport protocol. + */ + if(TSIP_MESSAGE_IS_REQUEST(message) && TSIP_STACK_MODE_IS_SERVER(transport->stack)){ + if(message->firstVia->rport == 0){ // 0: exist with no value; -1: doesn't exist; other contains the rport value + tnet_ip_t ip; + tnet_port_t port; + if((ret = tnet_get_sockip_n_port((const struct sockaddr*)&e->remote_addr, &ip, &port)) == 0){ + message->firstVia->rport = (int32_t)port; + tsk_strupdate(&message->firstVia->received, (const char*)ip); + } + } + } + + + /* Alert transaction/dialog layer */ + ret = tsip_transport_layer_handle_incoming_msg(transport, message); + } + TSK_OBJECT_SAFE_FREE(message); + + return ret; +} + +static const tsip_transport_t* tsip_transport_layer_find(const tsip_transport_layer_t* self, tsip_message_t *msg, char** destIP, int32_t *destPort) +{ + const tsip_transport_t* transport = tsk_null; + + if(!self || !destIP){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + // check whether the message already contains destination address (most likely retransmitted) + if(!tsk_strnullORempty(msg->dst_address) && msg->dst_port && TNET_SOCKET_TYPE_IS_VALID(msg->dst_net_type)){ + const tsk_list_item_t *item; + tsk_strupdate(destIP, msg->dst_address); + *destPort = msg->dst_port; + tsk_list_foreach(item, self->transports){ + if(((const tsip_transport_t*)item->data)->type == msg->dst_net_type){ + transport = ((const tsip_transport_t*)item->data); + goto bail; + } + } + } + + // use default values + tsk_strupdate(destIP, self->stack->network.proxy_cscf[self->stack->network.transport_idx_default]); + *destPort = self->stack->network.proxy_cscf_port[self->stack->network.transport_idx_default]; + + /* =========== Sending Request ========= + * + */ + if(TSIP_MESSAGE_IS_REQUEST(msg)){ + tsip_dialog_t* dialog; + tsk_list_item_t *item; + tsip_transport_t *curr; + tnet_socket_type_t destNetType = self->stack->network.transport_types[self->stack->network.transport_idx_default]; + /* RFC 3261 - 18.1.1 Sending Requests + If the port is absent, the default value depends on the transport. It is 5060 for UDP, TCP and SCTP, 5061 for TLS. */ + // int32_t destDefaultPort = TNET_SOCKET_TYPE_IS_TLS(destNetType) ? 5061 : 5060; + + /* If message received over WebSocket transport and stack is running in w2s mode then forward to the first route if available */ + if((self->stack->network.mode == tsip_stack_mode_webrtc2sip)){ + const tsip_header_Route_t *route_first; + if((route_first = (const tsip_header_Route_t*)tsip_message_get_header(msg, tsip_htype_Route)) && route_first->uri && !tsk_strnullORempty(route_first->uri->host)){ + const char* transport_str = tsk_params_get_param_value(route_first->uri->params, "transport"); + const tsip_header_Route_t *route; + tnet_port_t local_port; + const char *local_ip; + int t_idx = -1, route_i = 0; + if(!tsk_strnullORempty(transport_str)){ + t_idx = tsip_transport_get_idx_by_name(transport_str); + if(t_idx != -1){ + destNetType = self->stack->network.transport_types[t_idx]; + } + } + tsk_strupdate(destIP, route_first->uri->host); + *destPort = (route_first->uri->port ? route_first->uri->port : 5060); + + local_ip = self->stack->network.local_ip[t_idx == -1 ? self->stack->network.transport_idx_default : t_idx]; + local_port = self->stack->network.local_port[t_idx == -1 ? self->stack->network.transport_idx_default : t_idx]; +clean_routes: + route_i = 0; + while((route = (const tsip_header_Route_t *)tsip_message_get_headerAt(msg, tsip_htype_Route, route_i++))){ + if(route && route->uri){ + if(tsk_params_have_param(route->uri->params, "sipml5-outbound") || (tsk_strequals(local_ip, route->uri->host) && local_port == route->uri->port)){ + tsk_list_remove_item_by_data(msg->headers, route); + goto clean_routes; + } + } + } + } + else if(!TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) && !TNET_SOCKET_TYPE_IS_WS(msg->src_net_type)){ + const char* ws_src_ip = tsk_params_get_param_value(msg->line.request.uri->params, "ws-src-ip"); + if(ws_src_ip){ + const char* ws_src_port = tsk_params_get_param_value(msg->line.request.uri->params, "ws-src-port"); + const char* ws_src_proto = tsk_params_get_param_value(msg->line.request.uri->params, "ws-src-proto"); + tsk_strupdate(destIP, ws_src_ip); + *destPort = atoi(ws_src_port); + destNetType = self->stack->network.transport_types[tsip_transport_get_idx_by_name(ws_src_proto)]; + } + } + } + else{ + /* Sends request to the first route or remote target */ + dialog = tsip_dialog_layer_find_by_callid(self->stack->layer_dialog, msg->Call_ID->value); + if(dialog){ + const tsip_header_Record_Route_t* route; + tsk_bool_t b_using_route = tsk_false; + tsk_list_foreach(item, dialog->record_routes){ + if(!(route = item->data)){ + continue; + } + if(route->uri && route->uri->host){ + tsk_strupdate(destIP, route->uri->host); + *destPort = route->uri->port > 0 ? route->uri->port : (TNET_SOCKET_TYPE_IS_TLS(destNetType) ? 5061 : 5060); + b_using_route = tsk_true; + break; + } + } + if(!b_using_route){ + // Client mode requires the port to be defined (dialog connected) while server mode doesn't. + if(dialog->uri_remote_target && dialog->uri_remote_target->host && (dialog->uri_remote_target->port || TSIP_STACK_MODE_IS_SERVER(self->stack))){ + const char* transport_name = tsk_params_get_param_value(dialog->uri_remote_target->params, "transport"); + tsk_strupdate(destIP, dialog->uri_remote_target->host); + *destPort = dialog->uri_remote_target->port ? dialog->uri_remote_target->port : (tsk_striequals(transport_name, "TLS") ? 5061 : 5060); + if(!tsk_strnullORempty(transport_name)) { + enum tnet_socket_type_e _destNetType = tsip_transport_get_type_by_name(transport_name); + if(TNET_SOCKET_TYPE_IS_VALID(_destNetType)) { + // _destNetType is UDP, TCP, WSSS...and not UDP-IPv4, TCP-IPv6, WSS-IPv4-IPsec...This is why closest match is used. + destNetType = _destNetType; + } + } + } + } + TSK_OBJECT_SAFE_FREE(dialog); + } + } + + /* Find the right transport using exact/closest match */ + { + const tsip_transport_t* transport_closest1 = tsk_null; + const tsip_transport_t* transport_closest2 = tsk_null; + tsk_list_foreach(item, self->transports){ + curr = item->data; + if(curr->type == destNetType){ + transport = curr; + break; + } + if((curr->type & destNetType) == destNetType){ + transport_closest1 = curr; + } + if(self->stack->network.transport_idx_default>= 0 && curr->type == self->stack->network.transport_types[self->stack->network.transport_idx_default]) { + transport_closest2 = curr; + } + } + if(!transport && (transport_closest1 || transport_closest2)) { + const tsip_transport_t* transport_closest = transport_closest1 ? transport_closest1 : transport_closest2; + // For example, UDP will match with UDP-IPv4-IPSec or UDP-IPv6 + TSK_DEBUG_INFO("Setting transport with closest match(%d->%d)", destNetType, transport_closest->type); + transport = transport_closest; + } + } + + + /* DNS NAPTR + SRV if the Proxy-CSCF is not defined and route set is empty */ + if(transport && !(*destIP) && !self->stack->network.proxy_cscf[self->stack->network.transport_idx_default]){ + tnet_port_t dstPort; + if(tnet_dns_query_naptr_srv(self->stack->dns_ctx, msg->To->uri->host, transport->service, destIP, &dstPort) == 0){ + *destPort = dstPort; + } + else{ + tsk_strupdate(destIP, msg->To->uri->host); + *destPort = 5060; + } + } + } + + + + /* =========== Sending Response ========= + * + */ + else if(msg->firstVia) + { + { /* Find the transport. */ + tsk_list_item_t *item; + tsip_transport_t *curr; + tsk_list_foreach(item, self->transports) + { + curr = item->data; + if(tsip_transport_have_socket(curr, msg->local_fd)) + { + transport = curr; + break; + } + } + } + + /* webrtc2sip mode */ + if(self->stack->network.mode == tsip_stack_mode_webrtc2sip){ + if(TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type) || TNET_SOCKET_TYPE_IS_WS(msg->src_net_type)){ // response over WS or WSS + transport = tsip_transport_layer_find_by_idx(self, tsip_transport_get_idx_by_name(msg->firstVia->transport)); + if(transport){ + tsk_strupdate(destIP, msg->firstVia->host); + *destPort = msg->firstVia->port; + msg->dst_net_type = transport->type; + } + return transport; + } + else{ // response over UDP, TCP or TLS + const tsip_header_Via_t* via_2nd = (const tsip_header_Via_t*)tsip_message_get_headerAt(msg, tsip_htype_Via, 1); + tsk_bool_t via_ws_transport = via_2nd && (tsk_striequals(via_2nd->transport, "WS") || tsk_striequals(via_2nd->transport, "WSS")); + tsk_bool_t via_ws_hacked = via_2nd && TSIP_HEADER_HAVE_PARAM(via_2nd, "ws-hacked"); + if(via_2nd && (via_ws_transport || via_ws_hacked)){ + int t_idx = tsip_transport_get_idx_by_name(via_ws_transport ? via_2nd->transport : TSIP_HEADER_GET_PARAM_VALUE(via_2nd, "ws-hacked")); + const tsip_transport_t* ws_transport = tsip_transport_layer_find_by_idx(self, t_idx); + if(ws_transport){ + tsip_transport_stream_peer_t* peer = tsip_transport_find_stream_peer_by_remote_ip(TSIP_TRANSPORT(ws_transport), via_2nd->host, via_2nd->port, ws_transport->type); + if(peer){ + tsk_strupdate(destIP, peer->remote_ip); + *destPort = peer->remote_port; + msg->dst_net_type = ws_transport->type; + TSK_OBJECT_SAFE_FREE(peer); + return ws_transport; + } + } + + TSK_DEBUG_ERROR("Failed to match response expected to be forwarded via WebSocket transport"); + return tsk_null; + } + } + } + + if(TSIP_HEADER_VIA_RELIABLE_TRANS(msg->firstVia)) /*== RELIABLE ===*/ + { + /* RFC 3261 - 18.2.2 Sending Responses + If the "sent-protocol" is a reliable transport protocol such as + TCP or SCTP, or TLS over those, the response MUST be sent using + the existing connection to the source of the original request + that created the transaction, if that connection is still open. + This requires the server transport to maintain an association + between server transactions and transport connections. If that + connection is no longer open, the server SHOULD open a + connection to the IP address in the "received" parameter, if + present, using the port in the "sent-by" value, or the default + port for that transport, if no port is specified. If that + connection attempt fails, the server SHOULD use the procedures + in [4] for servers in order to determine the IP address and + port to open the connection and send the response to. + */ + if(tsk_strnullORempty(*destIP)){ + tnet_ip_t peer_ip; + tnet_port_t peer_port; + if(transport && tnet_get_peerip_n_port(msg->local_fd, &peer_ip, &peer_port) == 0){ // connection is still open ? + tsk_strupdate(destIP, peer_ip); + *destPort = peer_port; + } + else{ + if(msg->firstVia->received){ + tsk_strupdate(destIP, msg->firstVia->received); + *destPort = msg->firstVia->rport > 0 ? msg->firstVia->rport : msg->firstVia->port; + } + else{ + tsk_strupdate(destIP, msg->firstVia->host); + *destPort = msg->firstVia->port; + } + } + } + } + else + { + if(msg->firstVia->maddr) /*== UNRELIABLE MULTICAST ===*/ + { + /* RFC 3261 - 18.2.2 Sending Responses + Otherwise, if the Via header field value contains a "maddr" parameter, the + response MUST be forwarded to the address listed there, using + the port indicated in "sent-by", or port 5060 if none is present. + If the address is a multicast address, the response SHOULD be + sent using the TTL indicated in the "ttl" parameter, or with a + TTL of 1 if that parameter is not present. + */ + } + else /*=== UNRELIABLE UNICAST ===*/ + { + if(msg->firstVia->received) + { + if(msg->firstVia->rport > 0) + { + /* RFC 3581 - 4. Server Behavior + When a server attempts to send a response, it examines the topmost + Via header field value of that response. If the "sent-protocol" + component indicates an unreliable unicast transport protocol, such as + UDP, and there is no "maddr" parameter, but there is both a + "received" parameter and an "rport" parameter, the response MUST be + sent to the IP address listed in the "received" parameter, and the + port in the "rport" parameter. The response MUST be sent from the + same address and port that the corresponding request was received on. + This effectively adds a new processing step between bullets two and + three in Section 18.2.2 of SIP [1]. + */ + tsk_strupdate(destIP, msg->firstVia->received); + *destPort = msg->firstVia->rport; + } + else + { + /* RFC 3261 - 18.2.2 Sending Responses + Otherwise (for unreliable unicast transports), if the top Via + has a "received" parameter, the response MUST be sent to the + address in the "received" parameter, using the port indicated + in the "sent-by" value, or using port 5060 if none is specified + explicitly. If this fails, for example, elicits an ICMP "port + unreachable" response, the procedures of Section 5 of [4] + SHOULD be used to determine where to send the response. + */ + tsk_strupdate(destIP, msg->firstVia->received); + *destPort = msg->firstVia->port ? msg->firstVia->port : 5060; + } + } + else + { + /* RFC 3261 - 18.2.2 Sending Responses + Otherwise, if it is not receiver-tagged, the response MUST be + sent to the address indicated by the "sent-by" value, using the + procedures in Section 5 of [4]. + */ + tsk_strupdate(destIP, msg->firstVia->host); + if(msg->firstVia->port > 0) + { + *destPort = msg->firstVia->port; + } + } + } + } + } + + // update message to avoid destination address to avoid running the same algo for retransmissions + tsk_strupdate(&msg->dst_address, *destIP); + msg->dst_port = *destPort; + if(!TNET_SOCKET_TYPE_IS_VALID(msg->dst_net_type) && transport){ + msg->dst_net_type = transport->type; + } + +bail: + return transport; +} + +int tsip_transport_layer_add(tsip_transport_layer_t* self, const char* local_host, tnet_port_t local_port, tnet_socket_type_t type, const char* description) +{ + // FIXME: CHECK IF already exist + if(self && description) + { + tsip_transport_t *transport = + (TNET_SOCKET_TYPE_IS_IPSEC(type) || self->stack->security.enable_secagree_ipsec) ? + (tsip_transport_t *)tsip_transport_ipsec_create((tsip_stack_t*)self->stack, local_host, local_port, type, description) /* IPSec is a special case. All other are ok. */ + : tsip_transport_create((tsip_stack_t*)self->stack, local_host, local_port, type, description); /* UDP, SCTP, TCP, TLS, WS, WSS */ + + if(transport && transport->net_transport && self->stack){ + /* Set TLS certs */ + if(TNET_SOCKET_TYPE_IS_TLS(type) || TNET_SOCKET_TYPE_IS_WSS(type) || TNET_SOCKET_TYPE_IS_DTLS(type) || self->stack->security.enable_secagree_tls){ + tsip_transport_tls_set_certs(transport, self->stack->security.tls.ca, self->stack->security.tls.pbk, self->stack->security.tls.pvk, self->stack->security.tls.verify); + } + /* Nat Traversal context */ + if(self->stack->natt.ctx){ + tnet_transport_set_natt_ctx(transport->net_transport, self->stack->natt.ctx); + } + tsk_list_push_back_data(self->transports, (void**)&transport); + return 0; + } + else { + return -2; + } + } + return -1; +} + +int tsip_transport_layer_send(const tsip_transport_layer_t* self, const char *branch, tsip_message_t *msg) +{ + if(msg && self && self->stack){ + char* destIP = tsk_null; + int32_t destPort = 5060; + const tsip_transport_t *transport = tsip_transport_layer_find(self, msg, &destIP, &destPort); + int ret; + if(transport){ + if(tsip_transport_send(transport, branch, TSIP_MESSAGE(msg), destIP, destPort) > 0/* returns number of send bytes */){ + ret = 0; + } + else{ + ret = -3; + } + } + else{ + TSK_DEBUG_ERROR("Failed to find valid transport"); + ret = -2; + } + TSK_FREE(destIP); + return ret; + } + else{ + TSK_DEBUG_ERROR("Invalid Parameter"); + return -1; + } +} + + +int tsip_transport_createTempSAs(const tsip_transport_layer_t *self) +{ + int ret = -1; + + tsk_list_item_t *item; + tsip_transport_t* transport; + + if(!self){ + goto bail; + } + + tsk_list_foreach(item, self->transports){ + transport = item->data; + if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){ + ret = tsip_transport_ipsec_createTempSAs(TSIP_TRANSPORT_IPSEC(transport)); + break; + } + } + +bail: + return ret; +} + +int tsip_transport_ensureTempSAs(const tsip_transport_layer_t *self, const tsip_response_t *r401_407, int64_t expires) +{ + int ret = -1; + + tsk_list_item_t *item; + tsip_transport_t* transport; + + if(!self){ + goto bail; + } + + tsk_list_foreach(item, self->transports){ + transport = item->data; + if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){ + ret = tsip_transport_ipsec_ensureTempSAs(TSIP_TRANSPORT_IPSEC(transport), r401_407, expires); + break; + } + } + +bail: + return ret; +} + +int tsip_transport_startSAs(const tsip_transport_layer_t* self, const void* ik, const void* ck) +{ + int ret = -1; + + tsk_list_item_t *item; + tsip_transport_t* transport; + + if(!self){ + goto bail; + } + + tsk_list_foreach(item, self->transports){ + transport = item->data; + if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){ + ret = tsip_transport_ipsec_startSAs(TSIP_TRANSPORT_IPSEC(transport), (const tipsec_key_t*)ik, (const tipsec_key_t*)ck); + break; + } + } + +bail: + return ret; +} + +int tsip_transport_cleanupSAs(const tsip_transport_layer_t *self) +{ + int ret = -1; + + tsk_list_item_t *item; + tsip_transport_t* transport; + + if(!self){ + goto bail; + } + + tsk_list_foreach(item, self->transports){ + transport = item->data; + if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){ + ret = tsip_transport_ipsec_cleanupSAs(TSIP_TRANSPORT_IPSEC(transport)); + break; + } + } + +bail: + return ret; +} + +int tsip_transport_layer_remove_callid_from_stream_peers(tsip_transport_layer_t *self, const char* callid) +{ + if(self && callid){ + int ret = 0; + tsk_bool_t removed = tsk_false; + tsip_transport_t* transport; + tsk_list_item_t* item; + tsk_list_lock(self->transports); + tsk_list_foreach(item, self->transports){ + if(!(transport = TSIP_TRANSPORT(item->data)) || !TNET_SOCKET_TYPE_IS_STREAM(transport->type)){ + continue; + } + if((ret = tsip_transport_remove_callid_from_stream_peers(transport, callid, &removed)) == 0 && removed){ + TSK_DEBUG_INFO("[Transport Layer] Removed call-id = '%s' from transport layer", callid); + break; + } + } + tsk_list_unlock(self->transports); + return ret; + } + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; +} + +tsk_bool_t tsip_transport_layer_have_stream_peer_with_remote_ip(const tsip_transport_layer_t *self, const char* remote_ip, tnet_port_t remote_port) +{ + if(self && remote_ip){ + const tsk_list_item_t* item; + tsk_bool_t found = tsk_false; + tsip_transport_t* transport; + tsk_list_lock(self->transports); + tsk_list_foreach(item, self->transports){ + if(!(transport = TSIP_TRANSPORT(item->data)) || !TNET_SOCKET_TYPE_IS_STREAM(transport->type)){ + continue; + } + if(tsip_transport_have_stream_peer_with_remote_ip(transport, remote_ip, remote_port, transport->type)){ + found = tsk_true; + break; + } + } + tsk_list_unlock(self->transports); + return found; + } + return tsk_false; +} + + + + +int tsip_transport_layer_start(tsip_transport_layer_t* self) +{ + if(self){ + if(!self->running){ + int ret = 0; + tsk_list_item_t *item; + tsip_transport_t* transport; + int32_t transport_idx = self->stack->network.transport_idx_default; + + /* start() */ + tsk_list_foreach(item, self->transports){ + transport = item->data; + if((ret = tsip_transport_start(transport))){ + return ret; + } + } + + /* connect() */ + tsk_list_foreach(item, self->transports){ + transport = item->data; + + // set callback + if(TNET_SOCKET_TYPE_IS_DGRAM(transport->type)){ + tsip_transport_set_callback(transport, TNET_TRANSPORT_CB_F(tsip_transport_layer_dgram_cb), transport); + } + else if(TNET_SOCKET_TYPE_IS_WS(transport->type) || TNET_SOCKET_TYPE_IS_WSS(transport->type)){ + tsip_transport_set_callback(transport, TNET_TRANSPORT_CB_F(tsip_transport_layer_ws_cb), transport); + } + else{ + tsip_transport_set_callback(transport, TNET_TRANSPORT_CB_F(tsip_transport_layer_stream_cb), transport); + } + + if((ret = tnet_sockaddr_init(self->stack->network.proxy_cscf[transport_idx], self->stack->network.proxy_cscf_port[transport_idx], transport->type, &transport->pcscf_addr))){ + TSK_DEBUG_ERROR("[%s:%u] is invalid address", self->stack->network.proxy_cscf[transport_idx], self->stack->network.proxy_cscf_port[transport_idx]); + return ret; + } + + if(TNET_SOCKET_TYPE_IS_STREAM(transport->type)){ + if(!TSIP_STACK_MODE_IS_SERVER(transport->stack)){ + // Between "tsip_transport_connectto_2()" and "tsip_transport_add_stream_peer_2()" the net callback could be called and + // off cource peer will not be found in the list. This is why the list is locked. + tsip_transport_stream_peers_lock(transport); + if((transport->connectedFD = tsip_transport_connectto_2(transport, self->stack->network.proxy_cscf[transport_idx], self->stack->network.proxy_cscf_port[transport_idx])) == TNET_INVALID_FD){ + TSK_DEBUG_ERROR("Failed to connect the SIP transport"); + tsip_transport_stream_peers_unlock(transport); + return -3; + } + TSK_DEBUG_INFO("SIP transport fd=%d", transport->connectedFD); + // store peer + tsip_transport_add_stream_peer_2(transport, transport->connectedFD, transport->type, tsk_false, self->stack->network.proxy_cscf[transport_idx], self->stack->network.proxy_cscf_port[transport_idx]); + tsip_transport_stream_peers_unlock(transport); + // give the socket chance to connect + if((ret = tnet_sockfd_waitUntilWritable(transport->connectedFD, TSIP_CONNECT_TIMEOUT)) || (ret = tnet_sockfd_waitUntilReadable(transport->connectedFD, TSIP_CONNECT_TIMEOUT))){ + TSK_DEBUG_INFO("%d milliseconds elapsed and the socket is still not connected.", TSIP_CONNECT_TIMEOUT); + // dot not exit, store the outgoing data until connection succeed + } + } + } + + // set connectedFD=master for servers + if(transport->connectedFD == TNET_INVALID_FD){ + transport->connectedFD = tnet_transport_get_master_fd(transport->net_transport); + } + } + + self->running = tsk_true; + + return 0; + } + else return -2; + } + return -1; +} + + +int tsip_transport_layer_shutdown(tsip_transport_layer_t* self) +{ + if(self){ + if(!TSK_LIST_IS_EMPTY(self->transports)){ + //if(self->running){ + /*int ret = 0;*/ + tsk_list_item_t *item; + while((item = tsk_list_pop_first_item(self->transports))){ + TSK_OBJECT_SAFE_FREE(item); // Network transports are not reusable ==> (shutdow+remove) + } + self->running = tsk_false; + return 0; + } + else{ + return 0; /* not running */ + } + } + else{ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } +} + + + + + + +//======================================================== +// SIP transport layer object definition +// +static tsk_object_t* tsip_transport_layer_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transport_layer_t *layer = self; + if(layer){ + layer->stack = va_arg(*app, const tsip_stack_t *); + + layer->transports = tsk_list_create(); + } + return self; +} + +static tsk_object_t* tsip_transport_layer_dtor(tsk_object_t * self) +{ + tsip_transport_layer_t *layer = self; + if(layer){ + tsip_transport_layer_shutdown(self); + + TSK_OBJECT_SAFE_FREE(layer->transports); + + TSK_DEBUG_INFO("*** Transport Layer destroyed ***"); + } + return self; +} + +static int tsip_transport_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return -1; +} + +static const tsk_object_def_t tsip_transport_layer_def_s = +{ + sizeof(tsip_transport_layer_t), + tsip_transport_layer_ctor, + tsip_transport_layer_dtor, + tsip_transport_layer_cmp, +}; +const tsk_object_def_t *tsip_transport_layer_def_t = &tsip_transport_layer_def_s; diff --git a/tinySIP/src/transports/tsip_transport_tls.c b/tinySIP/src/transports/tsip_transport_tls.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tinySIP/src/transports/tsip_transport_tls.c |