summaryrefslogtreecommitdiffstats
path: root/tinySIP/src/transports
diff options
context:
space:
mode:
Diffstat (limited to 'tinySIP/src/transports')
-rw-r--r--tinySIP/src/transports/tsip_transport.c471
-rw-r--r--tinySIP/src/transports/tsip_transport_ipsec.c514
-rw-r--r--tinySIP/src/transports/tsip_transport_layer.c687
-rw-r--r--tinySIP/src/transports/tsip_transport_tls.c0
4 files changed, 1672 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..a31c6dc
--- /dev/null
+++ b/tinySIP/src/transports/tsip_transport.c
@@ -0,0 +1,471 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tsip_transport.c
+ * @brief SIP transport.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinysip/transports/tsip_transport.h"
+
+#include "tinysip/transports/tsip_transport_ipsec.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"
+
+/* 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)
+{
+ return tsk_object_new(tsip_transport_def_t, stack, host, port, type, description);
+}
+
+/* add Via header using the transport config */
+int tsip_transport_addvia(const tsip_transport_t* self, const char *branch, tsip_message_t *msg)
+{
+ tnet_ip_t ip;
+ tnet_port_t port;
+ int ret;
+
+ 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);
+ }
+
+ /* 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(&msg->firstVia->branch, _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;
+
+ /* already updtated (e.g. retrans)? */
+ if(!msg->update){
+ return 0;
+ }
+
+ /* retrieves the transport ip address and port */
+ if(!self->stack->network.aor.ip && !self->stack->network.aor.port){
+ 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 = tsk_strdup(ip);
+ ((tsip_stack_t*)self->stack)->network.aor.port = port;
+ }
+ }
+
+ /* === Host and port === */
+ if(msg->Contact && msg->Contact->uri){
+ tsk_strupdate(&(msg->Contact->uri->scheme), self->scheme);
+ tsk_strupdate(&(msg->Contact->uri->host), self->stack->network.aor.ip);
+ msg->Contact->uri->port = self->stack->network.aor.port;
+
+ 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);
+ }
+
+ 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(&quoted_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);
+ }
+ }
+
+
+ msg->update = tsk_false; /* To avoid to update retrans. */
+
+ return ret;
+}
+
+/* sets TLS certificates */
+int tsip_transport_set_tlscerts(tsip_transport_t *self, const char* ca, const char* pbk, const char* pvk)
+{
+ tnet_transport_t *transport = self->net_transport;
+
+ if(!self || !transport){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+
+ tsk_strupdate(&transport->tls.ca, ca);
+ tsk_strupdate(&transport->tls.pvk, pvk);
+ tsk_strupdate(&transport->tls.pbk, pbk);
+
+ return 0;
+}
+
+tsk_size_t tsip_transport_send_raw(const tsip_transport_t* self, const struct sockaddr* to, const void* data, tsk_size_t size)
+{
+ tsk_size_t ret = 0;
+ const struct sockaddr* dest = to? to : (const struct sockaddr *)&self->pcscf_addr;
+
+ if(TNET_SOCKET_TYPE_IS_DGRAM(self->type)){
+ if(!(ret = tnet_transport_sendto(self->net_transport, self->connectedFD, dest, data, size))){
+ TSK_DEBUG_WARN("Send() returns zero");
+ }
+ }
+ else{
+ ret = tnet_transport_send(self->net_transport, self->connectedFD, data, size);
+ }
+
+ 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;
+
+ /* 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 */
+ if(TSIP_MESSAGE_IS_REQUEST(msg) && (!TSIP_REQUEST_IS_ACK(msg) || (TSIP_REQUEST_IS_ACK(msg) && !msg->firstVia)) && !TSIP_REQUEST_IS_CANCEL(msg)){
+ tsip_transport_addvia(self, branch, msg); /* should be done before tsip_transport_msg_update() which could use the Via header */
+ tsip_transport_msg_update_aor((tsip_transport_t*)self, msg); /* AoR */
+ 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_IPSEC(self->type)){
+ tnet_fd_t fd = tsip_transport_ipsec_getFD(TSIP_TRANSPORT_IPSEC(self), TSIP_MESSAGE_IS_REQUEST(msg));
+ if(fd != TNET_INVALID_FD){
+ //struct sockaddr_storage to;
+ //tnet_sockaddr_init("2001:5c0:1502:1800::225", 4060, self->type, &to);
+
+ //tnet_sockfd_sendto(fd, (const struct sockaddr *)&to, buffer->data, buffer->size);
+ ret = tnet_sockfd_send(fd, buffer->data, buffer->size, 0);
+ }
+ }
+ else{
+ if(self->stack->network.mode_server && TSIP_MESSAGE_IS_RESPONSE(msg)){ // In server mode we will never send request. At least for now ;)
+ ret = tsip_transport_send_raw(self, (const struct sockaddr*)&msg->remote_addr, buffer->data, buffer->size);
+ }
+ else{
+ ret = tsip_transport_send_raw(self, tsk_null/* Use P-CSCF addr */, buffer->data, buffer->size);
+ }
+
+ /*if(destIP && destPort)
+ {
+ struct sockaddr_storage to;
+ if(tnet_sockaddr_init(destIP, destPort, tsip_transport_get_socket_type(self), &to))
+ {
+ goto bail;
+ }
+ if(tnet_transport_sendto(self->net_transport, self->connectedFD, &to, buffer->data, buffer->size))
+ {
+ ret = 0;
+ }
+ }
+ else*/
+ //{
+ // if((ret = tsip_transport_send_raw(self, buffer->data, buffer->size))){
+ // }
+ //}
+ }
+
+//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,
+ ipv6 ? "]" : "",
+ ((tsip_stack_t*)self->stack)->network.aor.port,
+ 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;
+}
+
+
+int tsip_transport_init(tsip_transport_t* self, tnet_socket_type_t type, const tsip_stack_handle_t *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 = TNET_SOCKET_TYPE_IS_TLS(type) ? "sips" : "sip";
+
+ if(TNET_SOCKET_TYPE_IS_STREAM(type)){
+ self->protocol = "tcp";
+ self->via_protocol = "TCP";
+ self->service = "SIP+D2T";
+
+ if(TNET_SOCKET_TYPE_IS_TLS(type)){
+ self->via_protocol = "TLS";
+ self->service = "SIPS+D2T";
+ }
+
+ /* Stream buffer */
+ self->buff_stream = tsk_buffer_create_null();
+ }
+ 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->buff_stream);
+
+ 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)){
+ // Print error?
+ }
+ }
+ 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;
+
diff --git a/tinySIP/src/transports/tsip_transport_ipsec.c b/tinySIP/src/transports/tsip_transport_ipsec.c
new file mode 100644
index 0000000..ab8d128
--- /dev/null
+++ b/tinySIP/src/transports/tsip_transport_ipsec.c
@@ -0,0 +1,514 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tsip_transport_ipsec.c
+ * @brief SIP/IPSec transport.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#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 "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(tsip_stack_t* 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){
+ 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 == 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_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((const char*)self->asso_temporary->ctx->addr_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){
+ 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_set_keys(self->asso_active->ctx, ik, ck))){
+ ret = tipsec_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){
+ 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;
+ }
+ }
+
+ 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){
+ return TNET_INVALID_FD;
+ }
+
+ /* If no active SAs ca be found then use default connection. */
+ if(!self->asso_active){
+ 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 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*);
+
+ /* 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 *);
+
+ tnet_ip_t ip_local;
+ tnet_ip_t ip_remote;
+ tnet_port_t port;
+
+ /* Set transport */
+ association->transport = transport;
+
+ /* Get local IP and port. */
+ tsip_transport_get_ip_n_port(transport, &ip_local, &port);
+
+ /* Create IPSec context */
+ association->ctx = tipsec_context_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));
+
+ /* Create Both client and Server legs */
+ association->socket_us = tnet_socket_create(ip_local, TNET_SOCKET_PORT_ANY, transport->type);
+ association->socket_uc = tnet_socket_create(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, &ip_remote)){ /* Get remote IP string */
+ tipsec_set_local(association->ctx, ip_local, ip_remote, association->socket_uc->port, association->socket_us->port);
+ }
+ else{
+ tipsec_set_local(association->ctx, ip_local, transport->stack->network.proxy_cscf, association->socket_uc->port, association->socket_us->port);
+ }
+ }
+ 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..2bcff8a
--- /dev/null
+++ b/tinySIP/src/transports/tsip_transport_layer.c
@@ -0,0 +1,687 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tsip_transport_layer.c
+ * @brief SIP transport layer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#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 "tsk_thread.h"
+#include "tsk_debug.h"
+
+/* 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
+
+
+tsip_transport_layer_t* tsip_transport_layer_create(tsip_stack_t *stack)
+{
+ return tsk_object_new(tsip_transport_layer_def_t, stack);
+}
+
+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;
+ const tsip_transport_t *transport = e->callback_data;
+
+ switch(e->type){
+ case event_data: {
+ //TSK_DEBUG_INFO("\n\n\nSIP Message:%s\n\n\n", e->data);
+ break;
+ }
+ case event_closed:
+ case event_connected:
+ default:{
+ return 0;
+ }
+ }
+
+
+ /* 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(transport->buff_stream) >= TSIP_MAX_STREAM_CHUNCK_SIZE){
+ TSK_DEBUG_ERROR("TCP Buffer is too big to be valid");
+ tsk_buffer_cleanup(transport->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, SigCompBuffer, data_size);
+ }
+ else{
+ // append result
+ tsk_buffer_append(transport->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, NULL, nack_data, next_size);
+ TSK_FREE(nack_data);
+ }
+ else{
+ // append result
+ tsk_buffer_append(transport->buff_stream, SigCompBuffer, (next_size - data_size));
+ data_size = next_size;
+ }
+ }
+ }
+ else{
+ /* Append new content. */
+ tsk_buffer_append(transport->buff_stream, e->data, e->size);
+ }
+
+ /* Check if we have all SIP headers. */
+parse_buffer:
+ if((endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(transport->buff_stream),TSK_BUFFER_SIZE(transport->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(transport->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(transport->buff_stream, 0, (endOfheaders + 4/*2CRLF*/)); /* Remove SIP headers and CRLF */
+ }
+ else{ /* There is a content */
+ if((endOfheaders + 4/*2CRLF*/ + clen) > TSK_BUFFER_SIZE(transport->buff_stream)){ /* There is content but not all the content. */
+ TSK_DEBUG_INFO("No all SIP content in the TCP buffer.");
+ goto bail;
+ }
+ else{
+ /* Add the content to the message. */
+ tsip_message_add_content(message, tsk_null, TSK_BUFFER_TO_U8(transport->buff_stream) + endOfheaders + 4/*2CRLF*/, clen);
+ /* Remove SIP headers, CRLF and the content. */
+ tsk_buffer_remove(transport->buff_stream, 0, (endOfheaders + 4/*2CRLF*/ + clen));
+ }
+ }
+ }
+
+ if(message && message->firstVia && message->Call_ID && message->CSeq && message->From && message->To){
+ /* Set fd */
+ message->local_fd = e->local_fd;
+ /* Alert transaction/dialog layer */
+ ret = tsip_transport_layer_handle_incoming_msg(transport, message);
+ /* Parse next chunck */
+ if(TSK_BUFFER_SIZE(transport->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);
+
+ 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: {
+ 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, data_ptr, data_size);
+ return 0;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("SigComp decompression has failed");
+ return -2;
+ }
+ }
+ else{
+ data_ptr = e->data;
+ data_size = e->size;
+ }
+
+ 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;
+
+ /* Alert transaction/dialog layer */
+ ret = tsip_transport_layer_handle_incoming_msg(transport, message);
+ }
+ TSK_OBJECT_SAFE_FREE(message);
+
+ return ret;
+}
+
+const tsip_transport_t* tsip_transport_layer_find(const tsip_transport_layer_t* self, const tsip_message_t *msg, const char** destIP, int32_t *destPort)
+{
+ tsip_transport_t* transport = 0;
+
+ if(!self || !destIP){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ *destIP = self->stack->network.proxy_cscf;
+ *destPort = self->stack->network.proxy_cscf_port;
+
+ /* =========== Sending Request =========
+ *
+ */
+ if(TSIP_MESSAGE_IS_REQUEST(msg)){
+ /* Request are always sent to the Proxy-CSCF
+ */
+ tsk_list_item_t *item;
+ tsip_transport_t *curr;
+ tsk_list_foreach(item, self->transports){
+ curr = item->data;
+ if(curr->type == self->stack->network.proxy_cscf_type){
+ transport = curr;
+ break;
+ }
+ }
+ }
+
+
+
+ /* =========== Sending Response =========
+ *
+ */
+ else if(msg->firstVia)
+ {
+ 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.
+ */
+ }
+ 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].
+ */
+ *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.
+ */
+ *destIP = msg->firstVia->received;
+ *destPort = msg->firstVia->port ? msg->firstVia->port : 5060;
+ }
+ }
+ else if(!msg->firstVia->received)
+ {
+ /* 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].
+ */
+ *destIP = msg->firstVia->host;
+ if(msg->firstVia->port >0)
+ {
+ *destPort = msg->firstVia->port;
+ }
+ }
+ }
+ }
+
+ { /* 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;
+ }
+ }
+ }
+ }
+
+ 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 */
+
+ if(transport && transport->net_transport && self->stack){
+ /* Set TLS certs */
+ if(TNET_SOCKET_TYPE_IS_TLS(type) || self->stack->security.enable_secagree_tls){
+ tsip_transport_set_tlscerts(transport, self->stack->security.tls.ca, self->stack->security.tls.pbk, self->stack->security.tls.pvk);
+ }
+ /* 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, const tsip_message_t *msg)
+{
+ if(msg && self && self->stack){
+ const char* destIP = tsk_null;
+ int32_t destPort = 5060;
+ const tsip_transport_t *transport = tsip_transport_layer_find(self, msg, &destIP, &destPort);
+ if(transport){
+ if(tsip_transport_send(transport, branch, TSIP_MESSAGE(msg), destIP, destPort)){
+ return 0;
+ }
+ else{
+ return -3;
+ }
+ }
+ else{
+ return -2;
+ }
+ }
+ 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_start(tsip_transport_layer_t* self)
+{
+ if(self){
+ if(!self->running){
+ int ret = 0;
+ tsk_list_item_t *item;
+ tsip_transport_t* transport;
+
+ /* 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){
+ tnet_fd_t fd;
+ transport = item->data;
+
+ tsip_transport_set_callback(transport, TNET_SOCKET_TYPE_IS_DGRAM(transport->type) ? TNET_TRANSPORT_CB_F(tsip_transport_layer_dgram_cb) : TNET_TRANSPORT_CB_F(tsip_transport_layer_stream_cb), transport);
+ if((ret = tnet_sockaddr_init(self->stack->network.proxy_cscf, self->stack->network.proxy_cscf_port, transport->type, &transport->pcscf_addr))){
+ TSK_DEBUG_ERROR("[%s:%u] is invalid address", self->stack->network.proxy_cscf, self->stack->network.proxy_cscf_port);
+ return ret;
+ }
+
+ if(TNET_SOCKET_TYPE_IS_STREAM(transport->type)){
+ if((fd = tsip_transport_connectto_2(transport, self->stack->network.proxy_cscf, self->stack->network.proxy_cscf_port)) == TNET_INVALID_FD){
+ TSK_DEBUG_ERROR("Failed to connect the SIP transport");
+ return ret;
+ }
+ if((ret = tnet_sockfd_waitUntilWritable(fd, TNET_CONNECT_TIMEOUT))){
+ TSK_DEBUG_ERROR("%d milliseconds elapsed and the socket is still not connected.", TNET_CONNECT_TIMEOUT);
+ tnet_transport_remove_socket(transport->net_transport, &fd);
+ return ret;
+ }
+ }
+ else{
+ 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
OpenPOWER on IntegriCloud