--- channels/chan_sip.c.orig 2006-12-06 23:42:41.000000000 +0800 +++ channels/chan_sip.c 2007-02-01 08:20:53.000000000 +0800 @@ -95,6 +95,17 @@ #define IPTOS_MINCOST 0x02 #endif +#define SIP_TCP_SUPPORT /* this will enable SIP over TCP/TLS */ + +#ifdef SIP_TCP_SUPPORT +#define OPENSSL_NO_KRB5 /* to prevent compile error */ +#include +#include +#include +#include +#include +#endif + /* #define VOCAL_DATA_HACK */ #define SIPDUMPER @@ -127,7 +138,6 @@ #define CALLERID_UNKNOWN "Unknown" - #define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ #define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ #define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ @@ -328,6 +338,10 @@ #define DEFAULT_SIP_PORT 5060 /*!< From RFC 3261 (former 2543) */ #define SIP_MAX_PACKET 4096 /*!< Also from RFC 3261 (2543), should sub headers tho */ +#ifdef SIP_TCP_SUPPORT +#define DEFAULT_TRANSPORT "UDP" +#endif + static char default_useragent[AST_MAX_EXTENSION] = DEFAULT_USERAGENT; #define DEFAULT_CONTEXT "default" @@ -386,6 +400,27 @@ #define DEFAULT_MWITIME 10 static int global_mwitime = DEFAULT_MWITIME; /*!< Time between MWI checks for peers */ +#ifdef SIP_TCP_SUPPORT +#define MAX_PATH_LEN 100 +#define DEFAULT_SIP_TLS_PORT 5061 /* From RFC 3261 */ +#define DEFAULT_PASSWORD "asterisk" +#define DEFAULT_ENTROPY "/dev/urandom" +#define DEFAULT_TRUSTCERTS "/var/lib/asterisk/keys/trustcerts.pem" +#define DEFAULT_TRUSTCERTSDIR "/var/lib/asterisk/keys/trustdir" +#define DEFAULT_SERVERCERT "/var/lib/asterisk/keys/servercert.pem" +#define DEFAULT_SERVEREKEY "/var/lib/asterisk/keys/serverkey.pem" +#define DEFAULT_DH512 "/var/lib/asterisk/keys/dh512.pem" +#define DEFAULT_DH1024 "/var/lib/asterisk/keys/dh1024.pem" +#define CIPHER_LIST "ALL" + +static char trustcerts_file[MAX_PATH_LEN] = DEFAULT_TRUSTCERTS; +static char servercert_file[MAX_PATH_LEN] = DEFAULT_SERVERCERT; +static char serverkey_file[MAX_PATH_LEN] = DEFAULT_SERVEREKEY; +static char serverkey_password[MAX_PATH_LEN] = DEFAULT_PASSWORD; +static char dh512param_file[MAX_PATH_LEN] = DEFAULT_DH512; +static char dh1024param_file[MAX_PATH_LEN] = DEFAULT_DH1024; +#endif + static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); @@ -440,6 +475,11 @@ static struct io_context *io; static int *sipsock_read_id; +#ifdef SIP_TCP_SUPPORT +static int *siptcpsock_read_id; +static int *siptlssock_read_id; +#endif + #define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ #define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */ @@ -622,6 +662,13 @@ int redircodecs; /*!< Redirect codecs */ struct sockaddr_in recv; /*!< Received as */ struct in_addr ourip; /*!< Our IP */ + +#ifdef SIP_TCP_SUPPORT + SSL *ssl; /* SSL object for TLS connection */ + int sockfd; /* socket fd used by this SIP channel*/ + char transport[4]; /* transport protocol: UDP, TCP or TLS */ +#endif + struct ast_channel *owner; /*!< Who owns us */ char exten[AST_MAX_EXTENSION]; /*!< Extension where to start */ char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */ @@ -790,6 +837,12 @@ struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ struct sockaddr_in addr; /*!< IP address of peer */ +#ifdef SIP_TCP_SUPPORT + SSL *ssl; /* SSL object for TLS connection */ + int sockfd; /* Connection socket is saved to here */ + char transport[4]; /* transport protocol: UDP, TCP or TLS */ +#endif + /* Qualification */ struct sip_pvt *call; /*!< Call pointer */ int pokeexpire; /*!< When to expire poke (qualify= checking) */ @@ -871,6 +924,12 @@ static int sipsock = -1; +#ifdef SIP_TCP_SUPPORT +static int siptcpsock = -1; /* TCP listening socket */ +static int siptlssock = -1; /* TLS listening socket */ +static SSL_CTX *tlsctx = NULL; /* SSL Context for TLS */ +static struct sockaddr_in tlsbindaddr; +#endif static struct sockaddr_in bindaddr = { 0, }; static struct sockaddr_in externip; @@ -909,6 +968,11 @@ static int expire_register(void *data); static int callevents = 0; +#ifdef SIP_TCP_SUPPORT +static int sipsock_read(int *id, int fd, short events, void *ignore); +static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime); +#endif + static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); static int sip_devicestate(void *data); static int sip_sendtext(struct ast_channel *ast, const char *text); @@ -1074,11 +1138,52 @@ int res; char iabuf[INET_ADDRSTRLEN]; +#ifdef SIP_TCP_SUPPORT + if ((p->sockfd > 0) && (p->sockfd != sipsock)) { /* This is TCP */ + if (p->ssl) { /* TLS write */ + if( sipdebug ) + ast_verbose("TLS write: fd %d\n", p->sockfd); + + struct sip_peer *peer = NULL; + res = 0; + while (len > res) { + res += SSL_write(p->ssl, data + res, len - res); + switch (SSL_get_error(p->ssl, res)) { + case SSL_ERROR_NONE: + break; + default: + ast_log(LOG_ERROR, "SSL write error\n"); + SSL_clear(p->ssl); + SSL_free(p->ssl); + p->ssl = NULL; + p->sockfd = -1; + peer = find_peer(p->peername, NULL, 1); + if (peer && peer->ssl) { + peer->ssl = NULL; + peer->sockfd = -1; + } + break; + } + } + } else { /* TCP write */ + if( sipdebug ) + ast_verbose("TCP write: fd %d\n", p->sockfd); + res = write(p->sockfd, data, len); + } + } else { + if( sipdebug ) + ast_verbose("UDP write: fd %d\n", p->sockfd); +#endif + if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in)); else res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in)); +#ifdef SIP_TCP_SUPPORT + } +#endif + if (res != len) { ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), res, strerror(errno)); } @@ -1094,9 +1199,17 @@ /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ if (ast_test_flag(p, SIP_NAT) & SIP_NAT_RFC3581) +#ifdef SIP_TCP_SUPPORT + snprintf(buf, len, "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x;rport", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else snprintf(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif else /* Work around buggy UNIDEN UIP200 firmware */ +#ifdef SIP_TCP_SUPPORT + snprintf(buf, len, "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else snprintf(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif } /*! \brief ast_sip_ouraddrfor: NAT fix - decide which IP address to use for ASterisk server? ---*/ @@ -1481,42 +1594,130 @@ parse_request(dst); } -/*! \brief send_response: Transmit response on SIP request---*/ -static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +#ifdef SIP_TCP_SUPPORT +static int cleanup_tcp_connection(int fd, SSL *ssl) { - int res; + int found = 0; + struct sip_pvt *p = NULL; + + if (fd == -1) { + ast_log(LOG_ERROR, "Tried to free up sockfd -1\n"); + return -1; + } + +// ASTOBJ_CONTAINER_RDLOCK(&peerl); + ASTOBJ_CONTAINER_TRAVERSE(&peerl, !found, { + if (iterator->sockfd == fd) { + found = 1; + if (iterator->ssl) { + SSL_clear(iterator->ssl); + SSL_free(iterator->ssl); + } + else + close(iterator->sockfd); + iterator->ssl = NULL; + iterator->sockfd = -1; +/* ast_copy_string(iterator->transport, "UDP", sizeof(iterator->transport)); */ + } + } ); +// ASTOBJ_CONTAINER_UNLOCK(&peerl); + + /* close tcp fd and free ssl in sip_pvt */ + ast_mutex_lock(&iflock); + p = iflist; + while (p) { + if (p->sockfd == fd) { + /* Found the call */ + ast_mutex_lock(&p->lock); + if (!found && p->ssl) { + SSL_clear(p->ssl); + SSL_free(p->ssl); + } else if (!found) + close(p->sockfd); + p->ssl = NULL; + p->sockfd = -1; +/* ast_copy_string(p->transport, "UDP", sizeof(p->transport)); */ + ast_mutex_unlock(&p->lock); + break; + } + p = p->next; + } + ast_mutex_unlock(&iflock); + + ast_log(LOG_DEBUG, "Cleaned up tcp connection for fd %d, ssl %p\n", fd, ssl); + return 0; +} + +static int tcptls_connect(struct sip_pvt *p) +{ + int fd = -1; + struct sockaddr_in myaddr; char iabuf[INET_ADDRSTRLEN]; - struct sip_request tmp; - char tmpmsg[80]; + BIO *bio = NULL; + SSL *ssl = NULL; + + if (!strncasecmp(p->transport, "UDP", 3)) { + return sipsock; + } - if (sip_debug_test_pvt(p)) { - if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) - ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data); - else - ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data); + if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ast_log(LOG_WARNING, "TCP can't create socket : %s\n", strerror(errno)); + return -1; } - if (reliable) { - if (recordhistory) { - parse_copy(&tmp, req); - snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxRespRel", tmpmsg); - } - res = __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable > 1), req->method); - } else { - if (recordhistory) { - parse_copy(&tmp, req); - snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxResp", tmpmsg); + + /* bind local protocol address to socket */ + bzero(&myaddr, sizeof(struct sockaddr_in)); + myaddr.sin_family = AF_INET; + memcpy(&myaddr.sin_addr, &p->ourip, sizeof(p->ourip)); + myaddr.sin_port = htons(0); /* any port is OK? */ + + if (bind(fd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_in)) < 0) { + ast_log(LOG_WARNING, "TCP failed to bind : %s\n", strerror(errno)); + return -1; + } + + /* start 3-way hand shake with the peer */ + if (connect(fd, (struct sockaddr *) &p->sa, sizeof(struct sockaddr_in)) < 0) { + ast_log(LOG_WARNING, "TCP can't connect to %s:%d, error %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), strerror(errno)); + return -1; + } + + if (sip_debug_test_pvt(p)) + ast_verbose(VERBOSE_PREFIX_2 "Successfuly TCP connected fd %d to %s:%d\n", fd, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port)); + + p->sockfd = fd; + + /* If TLS, do TLS handshake */ + if ((fd > -1) && !strncasecmp(p->transport, "TLS", sizeof("TLS"))) { + /* Initiate TLS handshake */ + bio = BIO_new_socket(fd, BIO_CLOSE); + ssl = SSL_new(tlsctx); + SSL_set_bio(ssl, bio, bio); + + if(SSL_connect(ssl) <= 0) { + ast_log(LOG_ERROR, "SSL_connect error"); + SSL_clear(ssl); + SSL_free(ssl); + close(fd); + p->ssl = NULL; + p->sockfd = -1; + return -1; } - res = __sip_xmit(p, req->data, req->len); + ast_log(LOG_DEBUG, "New TLS connection is opened\n"); + p->ssl = ssl; } - if (res > 0) - return 0; - return res; + + return fd; } +#endif -/*! \brief send_request: Send SIP Request to the other part of the dialogue ---*/ -static int send_request(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +enum send_type { + SEND_TYPE_RESPONSE = 0, + SEND_TYPE_REQUEST +}; + +/*! \brief send_response_request: Transmit response on SIP request or Send SIP Request to the other part of the dialogue---*/ +static int send_response_request(enum send_type type, struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) { int res; char iabuf[INET_ADDRSTRLEN]; @@ -1525,28 +1726,73 @@ if (sip_debug_test_pvt(p)) { if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) +#ifdef SIP_TCP_SUPPORT + ast_verbose("%sTransmitting (NAT) to %s:%d:%s\n with fd %d\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), p->transport, p->sockfd, req->data); +#else ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data); +#endif else +#ifdef SIP_TCP_SUPPORT + ast_verbose("%sTransmitting (no NAT) to %s:%d:%s\n with fd %d\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), p->transport, p->sockfd, req->data); +#else ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data); +#endif } + +#ifdef SIP_TCP_SUPPORT + /* if transport is TCP and not opened connection, connect now */ + if (!strncasecmp(p->transport, "TCP", 3) || !strncasecmp(p->transport, "TLS", 3) ) { + /* make TCP connection only when not connected and no NAT/firewall */ + if ((p->sockfd < 0) && !(ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)) { + p->sockfd = tcptls_connect(p); + if (p->sockfd < 0) { + ast_log(LOG_WARNING, "Failed to make TCP connection to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port)); + return -1; + } + ast_io_add(io, p->sockfd, sipsock_read, AST_IO_IN, p->ssl); + } else if (p->sockfd < 0) { + if (sip_debug_test_pvt(p)) + ast_log(LOG_WARNING, "peer is NATed, but TCP socket is closed\n"); + return -1; + } + } + + if (reliable && !strncasecmp(p->transport, "UDP", 3) /*(p->sockfd == sipsock)*/ ) { /* only UDP needs reliable transmit */ +#else if (reliable) { +#endif if (recordhistory) { parse_copy(&tmp, req); snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxReqRel", tmpmsg); + append_history(p, type == SEND_TYPE_RESPONSE ? "TxRespRel" : "TxReqRel", tmpmsg); } - res = __sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable > 1), req->method); + res = __sip_reliable_xmit(p, seqno, type == SEND_TYPE_RESPONSE ? 1 : 0, req->data, req->len, (reliable > 1), req->method); } else { if (recordhistory) { parse_copy(&tmp, req); snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxReq", tmpmsg); + append_history(p, type == SEND_TYPE_RESPONSE ? "TxResp" : "TxReq", tmpmsg); } res = __sip_xmit(p, req->data, req->len); } + + if ( type == SEND_TYPE_RESPONSE && res > 0) + return 0; return res; } +/*! \brief send_response: Transmit response on SIP request---*/ +static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +{ + return send_response_request( SEND_TYPE_RESPONSE, p, req, reliable, seqno ); +} + +/*! \brief send_request: Send SIP Request to the other part of the dialogue ---*/ +static int send_request(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +{ + return send_response_request( SEND_TYPE_REQUEST, p, req, reliable, seqno ); +} + /*! \brief get_in_brackets: Pick out text in brackets from character string ---*/ /* returns pointer to terminated stripped string. modifies input string. */ static char *get_in_brackets(char *tmp) @@ -1899,6 +2145,11 @@ ast_copy_string(r->peermd5secret, peer->md5secret, sizeof(r->peermd5secret)); ast_copy_string(r->tohost, peer->tohost, sizeof(r->tohost)); ast_copy_string(r->fullcontact, peer->fullcontact, sizeof(r->fullcontact)); +#ifdef SIP_TCP_SUPPORT + r->sockfd = peer->sockfd; + r->ssl = peer->ssl; + ast_copy_string(r->transport, peer->transport, sizeof(r->transport)); +#endif if (!r->initreq.headers && !ast_strlen_zero(peer->fromdomain)) { if ((callhost = strchr(r->callid, '@'))) { strncpy(callhost + 1, peer->fromdomain, sizeof(r->callid) - (callhost - r->callid) - 2); @@ -1948,6 +2199,10 @@ char host[MAXHOSTNAMELEN], *hostn; char peer[256]; +#ifdef SIP_TCP_SUPPORT + char transport[256]; +#endif /* SIP_TCP_SUPPORT */ + ast_copy_string(peer, opeer, sizeof(peer)); port = strchr(peer, ':'); if (port) { @@ -1972,16 +2227,60 @@ portno = atoi(port); else portno = DEFAULT_SIP_PORT; + +#ifdef SIP_TCP_SUPPORT + ast_copy_string(transport, DEFAULT_TRANSPORT, sizeof(DEFAULT_TRANSPORT)); +#endif /* SIP_TCP_SUPPORT */ + if (srvlookup) { char service[MAXHOSTNAMELEN]; int tportno; int ret; + char srvTransport[256]; snprintf(service, sizeof(service), "_sip._udp.%s", peer); ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); +#ifdef SIP_TCP_SUPPORT + ast_copy_string(srvTransport, "UDP", 3); + if( ret <= 0 ){ + snprintf(service, sizeof(service), "_sip._tcp.%s", peer); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + ast_copy_string(srvTransport, "TCP", 3); + } + if( ret <= 0 ){ + snprintf(service, sizeof(service), "_sip._tls.%s", peer); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + ast_copy_string(srvTransport, "TLS", 3); + } + if( ret <= 0 ){ + snprintf(service, sizeof(service), "_sipinternal._udp.%s", peer); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + ast_copy_string(srvTransport, "UDP", 3); + } + if( ret <= 0 ){ + snprintf(service, sizeof(service), "_sipinternal._tcp.%s", peer); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + ast_copy_string(srvTransport, "TCP", 3); + } + if( ret <= 0 ){ + snprintf(service, sizeof(service), "_sipinternal._tls.%s", peer); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + ast_copy_string(srvTransport, "TLS", 3); + } +#endif /* SIP_TCP_SUPPORT */ if (ret > 0) { hostn = host; portno = tportno; +#ifdef SIP_TCP_SUPPORT + ast_copy_string(transport, srvTransport, sizeof(transport)); + + if( sipdebug ) + ast_verbose( "Found information about services _sip._xxx.%s (%s:%d; transport=%s)\n", peer, hostn, portno, transport ); + +#endif /* SIP_TCP_SUPPORT */ + }else{ + ast_log( LOG_WARNING, "Can not get information about services _sip._xxx.%s\n", peer ); } + } hp = ast_gethostbyname(hostn, &ahp); if (hp) { @@ -1989,6 +2288,9 @@ memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); dialog->sa.sin_port = htons(portno); memcpy(&dialog->recv, &dialog->sa, sizeof(dialog->recv)); +#ifdef SIP_TCP_SUPPORT + ast_copy_string(dialog->transport, transport, sizeof(dialog->transport)); +#endif /* SIP_TCP_SUPPORT */ return 0; } else { ast_log(LOG_WARNING, "No such host: %s\n", peer); @@ -2158,6 +2460,13 @@ if (p->rpid_from) free(p->rpid_from); +/*#ifdef SIP_TCP_SUPPORT + if ((p->sockfd > 0) && (p->sockfd != sipsock)) + close(p->sockfd); + if (p->ssl) + SSL_free(p->ssl); +#endif*/ + /* Unlink us from the owner if we have one */ if (p->owner) { if (lockowner) @@ -3120,6 +3429,12 @@ /* Start with 101 instead of 1 */ p->ocseq = 101; +#ifdef SIP_TCP_SUPPORT + p->ssl = NULL; + p->sockfd = -1; + ast_copy_string(p->transport, DEFAULT_TRANSPORT, sizeof(p->transport)); /* default transport protocol */ +#endif + if (sip_methods[intended_method].need_rtp) { p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (videosupport) @@ -4036,12 +4351,26 @@ struct hostent *hp; struct ast_hostent ahp; int debug=sip_debug_test_pvt(p); +#ifdef SIP_TCP_SUPPORT + char *t, transport[256]; + int tn; +#endif /* SIP_TCP_SUPPORT */ + +#ifdef SIP_TCP_SUPPORT /* Parse uri to h (host) and port - uri is already just the part inside the <> */ /* general form we are expecting is sip[s]:username[:password]@host[:port][;...] */ +#else + /* Parse uri to h (host), port and transport - uri is already just the part inside the <> */ + /* general form we are expecting is sip[s]:username[:password]@host[:port][;...][;transport=UDP|TCP|TLS][;...] */ +#endif /* SIP_TCP_SUPPORT */ if (debug) +#ifdef SIP_TCP_SUPPORT + ast_verbose("set_destination: Parsing <%s> for address/port/transport to send to\n", uri); +#else ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri); +#endif /* SIP_TCP_SUPPORT */ /* Find and parse hostname */ h = strchr(uri, '@'); @@ -4083,11 +4412,44 @@ ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname); return; } + +#ifdef SIP_TCP_SUPPORT + /* Find and parse transport "transport=" */ + t = strstr(uri, "transport="); + if( t ) { + t += 10; + tn = strcspn(t, ";>") + 1; + if (tn > sizeof(transport)) + tn = sizeof(transport); + ast_copy_string(transport, t, tn); + } + else + ast_copy_string(transport, DEFAULT_TRANSPORT, sizeof(DEFAULT_TRANSPORT)); + p->sa.sin_family = AF_INET; memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); p->sa.sin_port = htons(port); + + if(!strncasecmp(transport, "UDP", 3)) { + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); + }else if(!strncasecmp(transport, "TCP", 3)) { + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + }else if(!strncasecmp(transport, "TLS", 3)) { + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + }else { + ast_log(LOG_WARNING, "Unsupported transport '%s' should be 'UDP', 'TCP', or 'TLS'\n", transport); + return; + } +#endif /* SIP_TCP_SUPPORT */ + if (debug) +#ifdef SIP_TCP_SUPPORT + ast_verbose("set_destination: set destination to %s, port %d, transport %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), port, p->transport); +#else ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), port); +#endif /* SIP_TCP_SUPPORT */ + + return; } /*! \brief init_resp: Initialize SIP response, based on SIP request ---*/ @@ -4817,9 +5179,17 @@ /* Construct Contact: header */ if (ourport != 5060) /* Needs to be 5060, according to the RFC */ +#ifdef SIP_TCP_SUPPORT + snprintf(p->our_contact, sizeof(p->our_contact), "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->transport); +#else snprintf(p->our_contact, sizeof(p->our_contact), "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport); +#endif /* SIP_TCP_SUPPORT */ else +#ifdef SIP_TCP_SUPPORT + snprintf(p->our_contact, sizeof(p->our_contact), "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), p->transport); +#else snprintf(p->our_contact, sizeof(p->our_contact), "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip)); +#endif /* SIP_TCP_SUPPORT */ } /*! \brief build_rpid: Build the Remote Party-ID & From using callingpres options ---*/ @@ -4983,7 +5353,11 @@ snprintf(from, sizeof(from), "\"%s\" ;tag=%s", n, l, ast_strlen_zero(p->fromdomain) ? ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip) : p->fromdomain, p->tag); /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ +#ifdef SIP_TCP_SUPPORT + if (!ast_strlen_zero(p->fullcontact) && strchr(p->fullcontact, '@') != NULL) { +#else if (!ast_strlen_zero(p->fullcontact)) { +#endif /* If we have full contact, trust it */ ast_build_string(&invite, &invite_max, "%s", p->fullcontact); } else { @@ -5216,20 +5590,22 @@ ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from)); c = get_in_brackets(from); - if (strncmp(c, "sip:", 4)) { + if (strncmp(c, "sip:", 4) && strncmp(c, "sips:", 5)) { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); return -1; } + if ((a = strchr(c, ';'))) *a = '\0'; mfrom = c; ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); c = get_in_brackets(to); - if (strncmp(c, "sip:", 4)) { + if (strncmp(c, "sip:", 4) && strncmp(c, "sips:", 5)) { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); return -1; } + if ((a = strchr(c, ';'))) *a = '\0'; mto = c; @@ -5716,10 +6092,14 @@ ast_copy_string(from, of, sizeof(from)); of = get_in_brackets(from); ast_copy_string(p->from,of,sizeof(p->from)); - if (strncmp(of, "sip:", 4)) { - ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); - } else + if (strncmp(of, "sip:", 4) == 0) { of += 4; + } else if (strncmp(of, "sips:", 5) == 0) { + of += 5; + } else { + ast_log(LOG_NOTICE, "From address missing 'sip:' or 'sips:', using it anyway\n"); + } + /* Get just the username part */ if ((c = strchr(dest, '@'))) { c = NULL; @@ -5938,10 +6318,13 @@ ast_copy_string(pvt->okcontacturi, c, sizeof(pvt->okcontacturi)); /* Make sure it's a SIP URL */ - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c); - } else + if ( strncasecmp(c, "sip:", 4) == 0 ) { c += 4; + } else if ( strncasecmp(c, "sips:", 5) == 0 ) { + c += 5; + }else { + ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing 'sip:' or 'sips:') trying to use anyway\n", c); + } /* Ditch arguments */ n = strchr(c, ';'); @@ -6008,6 +6391,10 @@ struct ast_hostent ahp; struct sockaddr_in oldsin; +#ifdef SIP_TCP_SUPPORT + char *t, *q; +#endif + if (ast_strlen_zero(expires)) { /* No expires header */ expires = strcasestr(get_header(req, "Contact"), ";expires="); if (expires) { @@ -6064,11 +6451,47 @@ /* For the 200 OK, we should use the received contact */ snprintf(pvt->our_contact, sizeof(pvt->our_contact) - 1, "<%s>", c); /* Make sure it's a SIP URL */ - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c); - } else + if ( strncasecmp(c, "sip:", 4) == 0 ) { c += 4; - /* Ditch q */ + } else if ( strncasecmp(c, "sips:", 5) == 0 ) { + c += 5; + } else { + ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c); + } + +#ifdef SIP_TCP_SUPPORT + /* transport parameter */ + t = NULL; + q = NULL; + t = strstr(c, "transport="); + if( t ) { + t += 10; + } + q = strstr(c, "q="); + if( q ) { + q += 2; + } + /* Ditch ';' after transport */ + if( t ) { + n = strchr(t, ';'); + if (n) { + *n = '\0'; + } + } + /* Ditch ';' after q */ + if( q ) { + n = strchr(q, ';'); + if (n) { + *n = '\0'; + } + } + if (option_verbose > 2 ) + { + ast_verbose(VERBOSE_PREFIX_3 "Contact header: transport %s\n", t ? t : "" ); + ast_verbose(VERBOSE_PREFIX_3 "Contact header: q %s\n", q ? q : "" ); + } +#endif + /* Ditch first ';' */ n = strchr(c, ';'); if (n) { *n = '\0'; @@ -6089,9 +6512,12 @@ port = atoi(pt); } else port = DEFAULT_SIP_PORT; + memcpy(&oldsin, &p->addr, sizeof(oldsin)); + if (!(ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)) { /* XXX This could block for a long time XXX */ + /* We should only do this if it's a name, not an IP */ hp = ast_gethostbyname(n, &ahp); if (!hp) { ast_log(LOG_WARNING, "Invalid host '%s'\n", n); @@ -6106,10 +6532,53 @@ memcpy(&p->addr, &pvt->recv, sizeof(p->addr)); } +#ifdef SIP_TCP_SUPPORT + /* Check a peer has old stalled TCP or TLS connection */ +/* if (p->ssl) { + SSL_clear(p->ssl); + SSL_free(p->ssl); + p->ssl = NULL; + } + if (p->sockfd > 0) { + close(p->sockfd); + p->sockfd = -1; + }*/ + p->sockfd = pvt->sockfd; + p->ssl = pvt->ssl; + if (t) { + if(!strncasecmp(t, "TCP", 3)) { + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } else if (!strncasecmp(t, "TLS", 3)) { + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + if (!pvt->ssl) + ast_log(LOG_ERROR, "transport tls comes without SSL from peer %s, fd %d\n", p->name, pvt->sockfd); + } else if (!strncasecmp(t, "UDP", 3)) { + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); + } else { + ast_log(LOG_ERROR, "Unsupported transport %s comes from peer %s, fd %d\n", t, p->name, pvt->sockfd); + return PARSE_REGISTER_FAILED; + } + } + else { + if( pvt->sockfd == sipsock ) { + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); + } else { + if (pvt->ssl) + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + else if (pvt->sockfd != sipsock) + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } + } +#endif + if (c) /* Overwrite the default username from config at registration */ ast_copy_string(p->username, c, sizeof(p->username)); else +#ifdef SIP_TCP_SUPPORT + ast_copy_string(p->username, p->name, sizeof(p->username)); +#else p->username[0] = '\0'; +#endif if (p->expire > -1) { ast_sched_del(sched, p->expire); @@ -6579,7 +7048,9 @@ if (!strncmp(c, "sip:", 4)) { name = c + 4; - } else { + } else if (!strncmp(c, "sips:", 5)) { + name = c + 5; + }else { name = c; ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); } @@ -6719,11 +7190,15 @@ if (ast_strlen_zero(tmp)) return 0; c = get_in_brackets(tmp); - if (strncmp(c, "sip:", 4)) { + if ( strncmp(c, "sip:", 4) == 0 ) { + c += 4; + }else if ( strncmp(c, "sips:", 5) == 0 ) { + c += 5; + }else { ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", c); return -1; } - c += 4; + if ((a = strchr(c, '@')) || (a = strchr(c, ';'))) { *a = '\0'; } @@ -6753,17 +7228,24 @@ from = get_in_brackets(tmpf); - if (strncmp(uri, "sip:", 4)) { + if ( strncmp(uri, "sip:", 4) == 0 ) { + uri += 4; + }else if ( strncmp(uri, "sips:", 5) == 0 ) { + uri += 5; + }else { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", uri); return -1; } - uri += 4; + if (!ast_strlen_zero(from)) { - if (strncmp(from, "sip:", 4)) { + if ( strncmp(from, "sip:", 4) == 0 ) { + from += 4; + }else if ( strncmp(from, "sips:", 5) == 0 ) { + from += 5; + }else { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from); return -1; } - from += 4; } else from = NULL; @@ -6901,22 +7383,24 @@ } h_contact = get_header(req, "Contact"); - if (strncmp(refer_to, "sip:", 4)) { - ast_log(LOG_WARNING, "Refer-to: Huh? Not a SIP header (%s)?\n", refer_to); + if ( strncmp(refer_to, "sip:", 4) == 0) { + refer_to += 4; + }else if ( strncmp(refer_to, "sips:", 5) == 0) { + refer_to += 5; + }else { + ast_log(LOG_ERROR, "Refer-to: Huh? Not a SIP header (%s)?\n", refer_to); return -1; } - if (strncmp(referred_by, "sip:", 4)) { + if ( strncmp(referred_by, "sip:", 4) == 0 ) { + referred_by += 4; + } else if ( strncmp(referred_by, "sips:", 5) == 0 ) { + referred_by += 5; + } else { ast_log(LOG_WARNING, "Referred-by: Huh? Not a SIP header (%s) Ignoring?\n", referred_by); referred_by = NULL; } - if (refer_to) - refer_to += 4; - - if (referred_by) - referred_by += 4; - if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */ *ptr = '\0'; @@ -7021,12 +7505,15 @@ c = get_in_brackets(tmp); - - if (strncmp(c, "sip:", 4)) { + if ( strncmp(c, "sip:", 4) == 0 ) { + c += 4; + } else if ( strncmp(c, "sips:", 5) == 0 ) { + c += 5; + } else { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); return -1; } - c += 4; + if ((a = strchr(c, '@'))) *a = '\0'; if ((a = strchr(c, ';'))) @@ -7059,6 +7546,10 @@ struct hostent *hp; struct ast_hostent ahp; +#ifdef SIP_TCP_SUPPORT + char transport[4]; +#endif /* SIP_TCP_SUPPORT */ + ast_copy_string(via, get_header(req, "Via"), sizeof(via)); /* Check for rport */ @@ -7074,7 +7565,20 @@ if (c) { *c = '\0'; c = ast_skip_blanks(c+1); +#ifdef SIP_TCP_SUPPORT + if (!strcasecmp(via, "SIP/2.0/UDP")) + { + ast_copy_string( transport, "UDP", sizeof(transport) ); + }else if (!strcasecmp(via, "SIP/2.0/TCP")) + { + ast_copy_string( transport, "TCP", sizeof(transport) ); + }else if (!strcasecmp(via, "SIP/2.0/TLS")) + { + ast_copy_string( transport, "TLS", sizeof(transport) ); + }else { +#else if (strcasecmp(via, "SIP/2.0/UDP")) { +#endif /* SIP_TCP_SUPPORT */ ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via); return -1; } @@ -7090,10 +7594,17 @@ p->sa.sin_family = AF_INET; memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); p->sa.sin_port = htons(pt ? atoi(pt) : DEFAULT_SIP_PORT); +#ifdef SIP_TCP_SUPPORT + ast_copy_string( p->transport, transport, sizeof(p->transport) ); +#endif /* SIP_TCP_SUPPORT */ if (sip_debug_test_pvt(p)) { c = (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) ? "NAT" : "non-NAT"; - ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), c); +#ifdef SIP_TCP_SUPPORT + ast_verbose("Sending to %s: %d; transport %s (%s)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), p->transport, c); +#else + ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), c); +#endif /* SIP_TCP_SUPPORT */ } } return 0; @@ -7213,8 +7724,10 @@ of = get_in_brackets(from); if (ast_strlen_zero(p->exten)) { t = uri2; - if (!strncmp(t, "sip:", 4)) - t+= 4; + if (strncmp(t, "sip:", 4) == 0) + t += 4; + else if (strncmp(t, "sips:", 5) == 0) + t += 5; ast_copy_string(p->exten, t, sizeof(p->exten)); t = strchr(p->exten, '@'); if (t) @@ -7224,10 +7737,14 @@ } /* save the URI part of the From header */ ast_copy_string(p->from, of, sizeof(p->from)); - if (strncmp(of, "sip:", 4)) { - ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); - } else + if ( strncmp(of, "sip:", 4) == 0) { of += 4; + } else if ( strncmp(of, "sips:", 5) == 0) { + of += 5; + } else { + ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); + } + /* Get just the username part */ if ((c = strchr(of, '@'))) { *c = '\0'; @@ -8185,6 +8702,10 @@ ast_cli(fd, " ToHost : %s\n", peer->tohost); ast_cli(fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port)); ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); +#ifdef SIP_TCP_SUPPORT + ast_cli(fd, " Sock fd : %d\n", peer->sockfd); + ast_cli(fd, " Transport : %s\n", peer->transport); +#endif ast_cli(fd, " Def. Username: %s\n", peer->username); ast_cli(fd, " SIP Options : "); if (peer->sipoptions) { @@ -9637,8 +10158,11 @@ if (e) *e = '\0'; if (ast_test_flag(p, SIP_PROMISCREDIR)) { - if (!strncasecmp(s, "sip:", 4)) + if ( strncasecmp(s, "sip:", 4) == 0 ) { s += 4; + } else if ( strncasecmp(s, "sips:", 5) == 0 ) { + s += 5; + } e = strchr(s, '/'); if (e) *e = '\0'; @@ -9652,8 +10176,11 @@ e = strchr(tmp, '/'); if (e) *e = '\0'; - if (!strncasecmp(s, "sip:", 4)) + if ( strncasecmp(s, "sip:", 4) == 0) { s += 4; + } else if ( strncasecmp(s, "sips:", 5) == 0) { + s += 5; + } ast_log(LOG_DEBUG, "Found 302 Redirect to extension '%s'\n", s); if (p->owner) ast_copy_string(p->owner->call_forward, s, sizeof(p->owner->call_forward)); @@ -10561,6 +11088,7 @@ /* This is a call to ourself. Send ourselves an error code and stop processing immediately, as SIP really has no good mechanism for being able to call yourself */ + if (debug) ast_verbose( "482 Loop Detected\n" ); transmit_response_reliable(p, "482 Loop Detected", req, 1); if (!p->lastinvite) ast_set_flag(p, SIP_NEEDDESTROY); @@ -11380,6 +11908,60 @@ return res; } +#ifdef SIP_TCP_SUPPORT +static int check_content_length(char *data, int readbytes, int max) +{ + int f = 0, blen = 0, msglen = 0; + char buf[SIP_MAX_PACKET]; + char *c = NULL, *header = NULL, *p; + + memcpy(buf, data, readbytes); + buf[readbytes] = 0; + c = buf; + + /* First header starts immediately */ + header = c; + while (*c) { + if (*c == '\n') { + /* We've got a new header */ + *c = 0; + + if (!strncmp(header, "Content-Length", strlen("Content-Length"))) { + p = strchr(header, ':'); + if (p) { + p ++; + blen = atoi(p); + } else { + ast_log(LOG_ERROR, "No colol in Content-Length header\n"); + return -1; + } + } + if (ast_strlen_zero(header)) { + /* Line by itself means we're now in content */ + c++; + break; + } + if (f >= SIP_MAX_HEADERS - 1) { + ast_log(LOG_WARNING, "Too many SIP headers...\n"); + } else + f++; + header = c + 1; + } else if (*c == '\r') { + /* Ignore but eliminate \r's */ + *c = 0; + } + c++; + } + msglen = (int)(c - buf) + blen; + + /* ast_verbose("blen %d, msglen %d, readbytes %d\n", blen, msglen, readbytes); */ + if ((readbytes >= msglen) || (readbytes >= max)) + return msglen; + else + return 0; +} +#endif + /*! \brief sipsock_read: Read data from SIP socket ---*/ /* Successful messages is connected to SIP call and forwarded to handle_request() */ static int sipsock_read(int *id, int fd, short events, void *ignore) @@ -11392,10 +11974,69 @@ int nounlock; int recount = 0; char iabuf[INET_ADDRSTRLEN]; +#ifdef SIP_TCP_SUPPORT + SSL *ssl = (SSL *)ignore; + int msglen = 0, n = 0; +#endif unsigned int lockretry = 100; len = sizeof(sin); memset(&req, 0, sizeof(req)); +#ifdef SIP_TCP_SUPPORT + if (fd != sipsock) { /* It is TCP socket */ + if (getpeername(fd, (struct sockaddr *)&sin, &len) < 0) + ast_log(LOG_WARNING, "TCP getpeername error: %s\n", strerror(errno)); + + /* ToDo: TLS read needs check the whole SIP message is read or not + by looking at double CRLF and Contect-Length header */ + + if (ssl) { /* This must be TLS connection socket */ + do { + res = SSL_read(ssl, req.data+n, sizeof(req.data) - 1); + switch(SSL_get_error(ssl, res)) { + case SSL_ERROR_NONE: + n += res; + break; + case SSL_ERROR_ZERO_RETURN: /* The peer closed the connection */ + ast_log(LOG_NOTICE, "The peer closed TLS Connection\n"); + res = -1; + break; + case SSL_ERROR_SYSCALL: + ast_log(LOG_ERROR, "TLS SSL_ERROR_SYSCALL\n"); + res = -1; + break; + default: + ast_log(LOG_ERROR, "TLS read error %d, %s\n", SSL_get_error(ssl, res), strerror(errno)); + res = -1; + break; + } + if (res >= 0) + msglen = check_content_length(req.data, n, (sizeof(req.data) - 1)); + } while (!msglen && (res >= 0)); + } else { /* This must be TCP connection socket */ + do { + res = read(fd, req.data+n, sizeof(req.data) - 1); + if (res > 0) { + n += res; + } else if (res == 0) { /* The client closed the TCP connection? */ + ast_log(LOG_NOTICE, "Remote closed TCP connection : fd %d\n", fd); + res = -1; + } + else if (res < 0) { + ast_log(LOG_ERROR, "TCP read error: %s\n", strerror(errno)); + } + if (res >= 0) + msglen = check_content_length(req.data, n, (sizeof(req.data) - 1)); + } while (!msglen && (res >= 0)); + } + /* clean up sockfd and ssl if a peer closed the TCP connection */ + if (res < 0) { + cleanup_tcp_connection(fd, ssl); + return 0; /* Remove it */ + } + res = msglen; + } else +#endif res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); if (res < 0) { #if !defined(__FreeBSD__) @@ -11418,7 +12059,11 @@ if (pedanticsipchecking) req.len = lws2sws(req.data, req.len); /* Fix multiline headers */ if (ast_test_flag(&req, SIP_PKT_DEBUG)) { +#ifdef SIP_TCP_SUPPORT + ast_verbose("\n<-- Sip read from %s:%d:%s \n%s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), fd == sipsock ? "UDP" : (ssl ? "TLS" : "TCP"), req.data); +#else ast_verbose("\n<-- SIP read from %s:%d: \n%s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), req.data); +#endif } parse_request(&req); req.method = find_sip_method(req.rlPart1); @@ -11460,6 +12105,18 @@ snprintf(tmp, sizeof(tmp), "%s / %s /%s", req.data, get_header(&req, "CSeq"), req.rlPart2); append_history(p, "Rx", tmp); } +#ifdef SIP_TCP_SUPPORT + p->sockfd = fd; /* Save socket fd to send a response */ + if (fd != sipsock) { + if (ssl) { + p->ssl = ssl; /* Only TLS will have non-null ssl */ + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + } else + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } + else + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); +#endif nounlock = 0; if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) { /* Request failed */ @@ -11517,6 +12174,73 @@ return 0; } +#ifdef SIP_TCP_SUPPORT +static int siptcp_accept(int *id, int fd, short events, void *ignore) +{ + int tcpconnfd = -1; + struct sockaddr_in cliaddr; + socklen_t sa_len; + char iabuf[INET_ADDRSTRLEN]; + + sa_len = sizeof(cliaddr); + if ((tcpconnfd = accept(siptcpsock, (struct sockaddr *)&cliaddr, &sa_len)) < 0) { + ast_log(LOG_WARNING, "Failed to accept SIP TCP connection from TCP listening sock %d : %s\n", + siptcpsock, strerror(errno)); + return 1; + } + + if( sip_debug_test_addr(&cliaddr) ) + ast_verbose(VERBOSE_PREFIX_2 "Accepted TCP connection fd %d from %s:%d\n", + tcpconnfd, ast_inet_ntoa(iabuf, sizeof(iabuf), cliaddr.sin_addr), ntohs(cliaddr.sin_port)); + ast_io_add(io, tcpconnfd, sipsock_read, AST_IO_IN, NULL); + return 1; +} + +static int siptls_accept(int *id, int fd, short events, void *ignore) +{ + int ret, tlsconnfd = -1; + struct sockaddr_in cliaddr; + socklen_t sa_len; + char iabuf[INET_ADDRSTRLEN]; + BIO *bio; +// BIO *bio_err=BIO_new_fp(stderr, BIO_NOCLOSE); // debug only + SSL *ssl; + + sa_len = sizeof(cliaddr); + if ((tlsconnfd = accept(siptlssock, (struct sockaddr *)&cliaddr, &sa_len)) < 0) { + ast_log(LOG_WARNING, "Failed to accept SIP TLS connection from TLS listening sock %d : %s\n", + siptlssock, strerror(errno)); + return 1; + } + + /* Initiate TLS handshake */ + bio = BIO_new_socket(tlsconnfd, BIO_CLOSE); + if (!(ssl = SSL_new(tlsctx))) { + ast_log(LOG_ERROR, "SSL_new error : %s\n", ERR_reason_error_string(ERR_get_error())); +// ERR_print_errors(bio_err); + return 2; + } + SSL_set_bio(ssl, bio, bio); + + if((ret = SSL_accept(ssl) <= 0)) { + ast_log(LOG_ERROR, "SSL accept error : %s\n", ERR_reason_error_string(ERR_get_error())); +// ERR_print_errors(bio_err); + return 3; + } + + /* ToDo : Client authentication code */ + + if( sip_debug_test_addr(&cliaddr) ) + ast_verbose(VERBOSE_PREFIX_2 "Accepted TLS connection fd %d from %s:%d\n", + tlsconnfd, ast_inet_ntoa(iabuf, sizeof(iabuf), cliaddr.sin_addr), ntohs(cliaddr.sin_port)); + + if (!ast_io_add(io, tlsconnfd, sipsock_read, AST_IO_IN, ssl)) + ast_log(LOG_ERROR, "ist_io_add failed\n"); + + return 4; +} +#endif + /*! \brief do_monitor: The SIP monitoring thread ---*/ static void *do_monitor(void *data) { @@ -11532,6 +12256,13 @@ /* Add an I/O event to our UDP socket */ if (sipsock > -1) sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL); + +#ifdef SIP_TCP_SUPPORT + if (siptcpsock > -1) + siptcpsock_read_id = ast_io_add(io, siptcpsock, siptcp_accept, AST_IO_IN, NULL); + if (siptlssock > -1) + siptlssock_read_id = ast_io_add(io, siptlssock, siptls_accept, AST_IO_IN, NULL); +#endif /* This thread monitors all the frame relay interfaces which are not yet in use (and thus do not have a separate thread) indefinitely */ @@ -11550,7 +12281,16 @@ /* Change the I/O fd of our UDP socket */ if (sipsock > -1) sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL); + +#ifdef SIP_TCP_SUPPORT + if (siptcpsock > -1) + siptcpsock_read_id = ast_io_change(io, siptcpsock_read_id, siptcpsock, NULL, 0, NULL); + if (siptlssock > -1) + siptlssock_read_id = ast_io_change(io, siptlssock_read_id, siptlssock, NULL, 0, NULL); +#endif + } + /* Check for interfaces needing to be killed */ ast_mutex_lock(&iflock); restartsearch: @@ -11754,6 +12494,14 @@ else ast_inet_ntoa(p->tohost, sizeof(p->tohost), peer->addr.sin_addr); +#ifdef SIP_TCP_SUPPORT + p->sockfd = peer->sockfd; + p->ssl = peer->ssl; + ast_copy_string(p->transport, peer->transport, sizeof(p->transport)); + if (sipdebug) + ast_log(LOG_NOTICE, "Poking %s: transport %s, sockfd %d \n", peer->username, p->transport, p->sockfd); +#endif + /* Recalculate our side, and recalculate Call ID */ if (ast_sip_ouraddrfor(&p->sa.sin_addr,&p->ourip)) memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); @@ -12412,7 +13160,12 @@ peer->prefs = prefs; oldha = peer->ha; peer->ha = NULL; - peer->addr.sin_family = AF_INET; +/* peer->addr.sin_family = AF_INET; */ +#ifdef SIP_TCP_SUPPORT + peer->sockfd = -1; + peer->ssl = NULL; + ast_copy_string(peer->transport, DEFAULT_TRANSPORT, sizeof(peer->transport)); +#endif ast_copy_flags(peer, &global_flags, SIP_FLAGS_TO_COPY); peer->capability = global_capability; peer->rtptimeout = global_rtptimeout; @@ -12476,7 +13229,17 @@ peer->expire = -1; ast_clear_flag(&peer->flags_page2, SIP_PAGE2_DYNAMIC); if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) { +#ifdef SIP_TCP_SUPPORT + if ( ast_get_ip_or_srv(&peer->addr, v->value, "_sip._udp") < 0 && + ast_get_ip_or_srv(&peer->addr, v->value, "_sip._tcp") < 0 && + ast_get_ip_or_srv(&peer->addr, v->value, "_sip._tls") < 0 && + ast_get_ip_or_srv(&peer->addr, v->value, "_sipinternal._udp") < 0 && + ast_get_ip_or_srv(&peer->addr, v->value, "_sipinternal._tcp") < 0 && + ast_get_ip_or_srv(&peer->addr, v->value, "_sipinternal._tls") < 0 ) { +#else if (ast_get_ip_or_srv(&peer->addr, v->value, srvlookup ? "_sip._udp" : NULL)) { +#endif /* SIP_TCP_SUPPORT */ + ast_log(LOG_WARNING, "Unable to locate outboundproxy for peer'%s'\n", v->value); ASTOBJ_UNREF(peer, sip_destroy_peer); return NULL; } @@ -12573,6 +13336,18 @@ ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno); peer->maxms = 0; } +#ifdef SIP_TCP_SUPPORT + } else if (!strcasecmp(v->name, "transport")) { + if(!strncasecmp(v->value, "udp", 3)) { + ast_copy_string(peer->transport, "UDP", sizeof(peer->transport)); + } else if(!strncasecmp(v->value, "tcp", 3)) { + ast_copy_string(peer->transport, "TCP", sizeof(peer->transport)); + }else if(!strncasecmp(v->value, "tls", 3)) { + ast_copy_string(peer->transport, "TLS", sizeof(peer->transport)); + }else { + ast_log(LOG_WARNING, "Unsupported transport '%s' should be 'UDP', 'TCP', or 'TLS' at line %d of sip.conf\n", v->value, v->lineno); + } +#endif /* SIP_TCP_SUPPORT */ } /* else if (strcasecmp(v->name,"type")) * ast_log(LOG_WARNING, "Ignoring %s\n", v->name); @@ -12619,6 +13394,9 @@ struct ast_flags dummy; int auto_sip_domains = 0; struct sockaddr_in old_bindaddr = bindaddr; +#ifdef SIP_TCP_SUPPORT + struct sockaddr_in old_tlsbindaddr = tlsbindaddr; +#endif cfg = ast_config_load(config); @@ -12764,8 +13542,18 @@ } else if (!strcasecmp(v->name, "fromdomain")) { ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain)); } else if (!strcasecmp(v->name, "outboundproxy")) { +#ifdef SIP_TCP_SUPPORT + if ( ast_get_ip_or_srv(&outboundproxyip, v->value, "_sip._udp") < 0 && + ast_get_ip_or_srv(&outboundproxyip, v->value, "_sip._tcp") < 0 && + ast_get_ip_or_srv(&outboundproxyip, v->value, "_sip._tls") < 0 && + ast_get_ip_or_srv(&outboundproxyip, v->value, "_sipinternal._udp") < 0 && + ast_get_ip_or_srv(&outboundproxyip, v->value, "_sipinternal._tcp") < 0 && + ast_get_ip_or_srv(&outboundproxyip, v->value, "_sipinternal._tls") < 0 ) +#else if (ast_get_ip_or_srv(&outboundproxyip, v->value, srvlookup ? "_sip._udp" : NULL) < 0) ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value); +#endif /* SIP_TCP_SUPPORT */ + ast_log(LOG_WARNING, "Unable to locate outboundproxy host '%s'\n", v->value); } else if (!strcasecmp(v->name, "outboundproxyport")) { /* Port needs to be after IP */ sscanf(v->value, "%d", &format); @@ -12873,6 +13661,27 @@ } else if (!strcasecmp(v->name, "callevents")) { callevents = ast_true(v->value); } +#ifdef SIP_TCP_SUPPORT + else if (!strcasecmp(v->name, "tlsport")) { + if (sscanf(v->value, "%d", &ourport) == 1) { + tlsbindaddr.sin_port = htons(ourport); + } else { + ast_log(LOG_WARNING, "Invalid tlsport number '%s' at line %d of %s\n", v->value, v->lineno, config); + } + } else if (!strcasecmp(v->name, "trustcerts")) { + ast_copy_string(trustcerts_file, v->value, sizeof(trustcerts_file)); + } else if (!strcasecmp(v->name, "servercert")) { + ast_copy_string(servercert_file, v->value, sizeof(servercert_file)); + } else if (!strcasecmp(v->name, "serverkey")) { + ast_copy_string(serverkey_file, v->value, sizeof(serverkey_file)); + } else if (!strcasecmp(v->name, "serverkeypassword")) { + ast_copy_string(serverkey_password, v->value, sizeof(serverkey_password)); + } else if (!strcasecmp(v->name, "dh512param")) { + ast_copy_string(dh512param_file, v->value, sizeof(dh512param_file)); + } else if (!strcasecmp(v->name, "dh1024param")) { + ast_copy_string(dh1024param_file, v->value, sizeof(dh1024param_file)); + } +#endif /* else if (strcasecmp(v->name,"type")) * ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ @@ -12964,6 +13773,85 @@ } } } +#ifdef SIP_TCP_SUPPORT + if ((siptcpsock > -1) && (memcmp(&old_tlsbindaddr, &bindaddr, sizeof(struct sockaddr_in)))) { + close(siptcpsock); + siptcpsock = -1; + } + /* Open a TCP listening socket */ + if (siptcpsock < 0) { + siptcpsock = socket(AF_INET, SOCK_STREAM, 0); + if (siptcpsock < 0) { + ast_log(LOG_WARNING, "Unable to create SIP TCP socket: %s\n", strerror(errno)); + } else { + /* Allow SIP clients on the same host to access us: */ + const int reuseFlag = 1; + setsockopt(siptcpsock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&reuseFlag, + sizeof reuseFlag); + + if (bind(siptcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + ast_log(LOG_WARNING, "Failed to bind SIP TCP socket to %s:%d: %s\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port), + strerror(errno)); + close(siptcpsock); + siptcpsock = -1; + } else { + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_2 "SIP TCP Listening on %s:%d\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); + } + if (setsockopt(siptcpsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) + ast_log(LOG_WARNING, "Unable to set TOS to %d in SIP TCP\n", tos); + if (listen(siptcpsock, 30) < 0) { + ast_log(LOG_WARNING, "Failed to listen on SIP TCP\n"); + } + } + } + } + + /* Open a TLS listening socket */ + memcpy(&tlsbindaddr.sin_addr, &bindaddr.sin_addr, sizeof(tlsbindaddr.sin_addr)); + if (!ntohs(tlsbindaddr.sin_port)) + tlsbindaddr.sin_port = ntohs(DEFAULT_SIP_TLS_PORT); + tlsbindaddr.sin_family = AF_INET; + if ((siptlssock > -1) && (memcmp(&old_tlsbindaddr, &tlsbindaddr, sizeof(struct sockaddr_in)))) { + close(siptlssock); + siptlssock = -1; + } + if (siptlssock < 0) { + siptlssock = socket(AF_INET, SOCK_STREAM, 0); + if (siptlssock < 0) { + ast_log(LOG_WARNING, "Unable to create SIP TLS socket: %s\n", strerror(errno)); + } else { + /* Allow SIP clients on the same host to access us: */ + const int reuseFlag = 1; + setsockopt(siptlssock, SOL_SOCKET, SO_REUSEADDR, ( + const char *)&reuseFlag, + sizeof reuseFlag); + + if (bind(siptlssock, (struct sockaddr *)&tlsbindaddr, sizeof(tlsbindaddr)) < 0) { + ast_log(LOG_WARNING, "Failed to bind SIP TLS socket to %s:%d: %s\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), tlsbindaddr.sin_addr), ntohs(tlsbindaddr.sin_port), + strerror(errno)); + close(siptlssock); + siptlssock = -1; + } else { + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_2 "SIP TLS Listening on %s:%d\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), tlsbindaddr.sin_addr), ntohs(tlsbindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); + } + if (setsockopt(siptlssock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) + ast_log(LOG_WARNING, "Unable to set TOS to %d in SIP TLS\n", tos); + if (listen(siptlssock, 30) < 0) { + ast_log(LOG_WARNING, "Failed to listen on SIP TLS\n"); + } + } + } + } +#endif ast_mutex_unlock(&netlock); /* Add default domains - host name, IP address and IP:port */ @@ -13464,6 +14352,108 @@ { { "sip", "reload", NULL }, sip_reload, "Reload SIP configuration", sip_reload_usage }, }; +#ifdef SIP_TCP_SUPPORT +DH *read_dhparams(char *dhfile) +{ + BIO *bio = NULL; + DH *dh = NULL; + + bio = BIO_new_file(dhfile, "r"); + if (!bio) + ast_log(LOG_ERROR, "Error opening file %s", dhfile); + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + if (!dh) + ast_log(LOG_ERROR, "Error reading DH parameters from %s", dhfile); + BIO_free(bio); + return dh; +} + +DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength) +{ + DH *ret; + + switch (keylength) + { + case 512: + ret = read_dhparams(dh512param_file); + break; + case 1024: + default: + ret = read_dhparams(dh1024param_file); + break; + } + return ret; +} + +int password_callback(char *buf, int num, int rwflag, void *userdata) +{ + if (num < (strlen(serverkey_password)+1)) { + ast_log(LOG_ERROR, "password buf len %d is too small\n", num); + return 0; + } + strcpy(buf, serverkey_password); + return (strlen(serverkey_password)); +} + +int verify_callback(int ok, X509_STORE_CTX *store) +{ + char data[256]; + + if (!ok) + { + X509 *cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int err = X509_STORE_CTX_get_error(store); + + ast_log(LOG_ERROR, "-Error with certificate at depth: %i\n", depth); + X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); + ast_log(LOG_ERROR, " issuer = %s\n", data); + X509_NAME_oneline(X509_get_subject_name(cert), data, 256); + ast_log(LOG_ERROR, " subject = %s\n", data); + ast_log(LOG_ERROR, " err %i:%s\n", err, X509_verify_cert_error_string(err)); + } + + return ok; +} + +/* initailize OpenSSL library */ +SSL_CTX *init_OpenSSL(void) +{ + SSL_CTX *ctx = NULL; + + if (!SSL_library_init()) { + ast_log(LOG_ERROR, "SSL_library_init failed\n"); + return NULL; + } + SSL_load_error_strings(); + RAND_load_file(DEFAULT_ENTROPY, 1024); + + ctx = SSL_CTX_new(TLSv1_method()); + + if (SSL_CTX_load_verify_locations(ctx, trustcerts_file, NULL) != 1) + ast_log(LOG_ERROR, "Error loading a trust certs\n"); + + SSL_CTX_set_default_passwd_cb(ctx, password_callback); + + if (SSL_CTX_set_default_verify_paths(ctx) != 1) + ast_log(LOG_ERROR, "Error to set_default_verify_path\n"); + if (SSL_CTX_use_certificate_chain_file(ctx, servercert_file) != 1) + ast_log(LOG_ERROR, "Error loading certificate from file\n"); + if (SSL_CTX_use_PrivateKey_file(ctx, serverkey_file, SSL_FILETYPE_PEM) != 1) + ast_log(LOG_ERROR, "Error loading private key from file\n"); + +/* SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); */ +/* SSL_CTX_set_verify_depth(ctx, 4); */ + SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback); + if (SSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != 1) + ast_log(LOG_ERROR, "Error setting cipher list (no valid ciphers)\n"); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + return ctx; +} +#endif + /*! \brief load_module: PBX load module - initialization ---*/ int load_module() { @@ -13483,6 +14473,12 @@ reload_config(); /* Load the configuration from sip.conf */ +#ifdef SIP_TCP_SUPPORT + if( tlsctx != NULL ) + SSL_CTX_free(tlsctx); + tlsctx = init_OpenSSL(); +#endif + /* Make sure we can register our sip channel type */ if (ast_channel_register(&sip_tech)) { ast_log(LOG_ERROR, "Unable to register channel type %s\n", channeltype); @@ -13591,6 +14587,15 @@ /* Free memory for local network address mask */ ast_free_ha(localaddr); +#ifdef SIP_TCP_SUPPORT + ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, { + if (iterator->ssl) { + SSL_clear(iterator->ssl); + SSL_free(iterator->ssl); + } + } ); +#endif + ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); ASTOBJ_CONTAINER_DESTROY(&userl); ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer); @@ -13602,7 +14607,11 @@ clear_sip_domains(); close(sipsock); sched_context_destroy(sched); - +#ifdef SIP_TCP_SUPPORT + close(siptcpsock); + close(siptlssock); + SSL_CTX_free(tlsctx); /* destroy TLS CTX */ +#endif return 0; }