--- chan_sip.c.orig 2006-12-08 21:42:26.000000000 +0800 +++ chan_sip.c 2006-12-12 09:42:34.312360536 +0800 @@ -157,6 +157,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 DEFAULT_DEFAULT_EXPIRY 120 @@ -505,6 +516,28 @@ #define DEFAULT_QUALIFY FALSE #define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */ #define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */ + +#ifdef SIP_TCP_SUPPORT +#define DEFAULT_TRANSPORT "UDP" +#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 + #ifndef DEFAULT_USERAGENT #define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ #endif @@ -1001,6 +1034,13 @@ before strolling to the Grokyzpå (A bit unsure of this, please correct if you know more) */ + +#ifdef SIP_TCP_SUPPORT + SSL *ssl; /*!< SSL object for TLS connection */ + int sockfd; /*!< Socket fd used by this SIP channel if in TCP mode */ + char transport[4]; /*!< transport protocol: UDP, TCP or TLS */ +#endif + }; static struct sip_pvt *dialoglist = NULL; @@ -1116,6 +1156,12 @@ struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ struct sockaddr_in addr; /*!< IP address of peer */ int maxcallbitrate; /*!< Maximum Bitrate for a video call */ + +#ifdef SIP_TCP_SUPPORT + SSL *ssl; /*!< SSL object for TLS connection */ + int sockfd; /*!< Socket fd used by this SIP channel if in TCP mode */ + char transport[4]; /*!< transport protocol: UDP, TCP or TLS */ +#endif /* Qualification */ struct sip_pvt *call; /*!< Call pointer */ @@ -1131,6 +1177,12 @@ int autoframing; }; +#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 /*! \brief Registrations with other SIP proxies */ @@ -1340,6 +1392,17 @@ static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); +#ifdef SIP_TCP_SUPPORT +/*--- Function Prototypes for TCP/TLS helper functions */ +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); +SSL_CTX *init_OpenSSL(void); +DH *read_dhparams(char *dhfile); +DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength); +int password_callback(char *buf, int num, int rwflag, void *userdata); +int verify_callback(int ok, X509_STORE_CTX *store); +#endif + /*--- Device monitoring and Device/extension state handling */ static int cb_extensionstate(char *context, char* exten, int state, void *data); static int sip_devicestate(void *data); @@ -2167,6 +2230,59 @@ parse_request(dst); } +#ifdef SIP_TCP_SUPPORT +static int cleanup_tcp_connection(int fd, SSL *ssl) +{ + 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(&dialoglock); + for (p = dialoglist; p ; p = p->next) { + if (p->sockfd == fd) { + /* Found the call */ + ast_mutex_lock(&p->pvt_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->pvt_lock); + break; + } + } + ast_mutex_unlock(&dialoglock); + + ast_log(LOG_DEBUG, "Cleaned up tcp connection for fd %d, ssl %p\n", fd, ssl); + return 0; +} +#endif + /*! \brief add a blank line if no body */ static void add_blank(struct sip_request *req) { @@ -2804,6 +2920,10 @@ char host[MAXHOSTNAMELEN], *hostn; char peername[256]; +#ifdef SIP_TCP_SUPPORT + char transport[256]; +#endif /* SIP_TCP_SUPPORT */ + ast_copy_string(peername, opeer, sizeof(peername)); port = strchr(peername, ':'); if (port) @@ -2819,6 +2939,11 @@ } hostn = peername; portno = port ? atoi(port) : STANDARD_SIP_PORT; + + #ifdef SIP_TCP_SUPPORT + ast_copy_string(transport, DEFAULT_TRANSPORT, sizeof(DEFAULT_TRANSPORT)); + #endif /* SIP_TCP_SUPPORT */ + if (global_srvlookup) { char service[MAXHOSTNAMELEN]; int tportno; @@ -2840,6 +2965,9 @@ memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); dialog->sa.sin_port = htons(portno); dialog->recv = dialog->sa; +#ifdef SIP_TCP_SUPPORT + ast_copy_string(dialog->transport, transport, sizeof(dialog->transport)); +#endif /* SIP_TCP_SUPPORT */ return 0; } @@ -3021,6 +3149,13 @@ unref_registry(p->registry); } +/*#ifdef SIP_TCP_SUPPORT + if ((p->sockfd > -1) && (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) @@ -4292,6 +4427,12 @@ make_our_tag(p->tag, sizeof(p->tag)); p->ocseq = INITIAL_CSEQ; +#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 the global videosupport flag is on, we always create a RTP interface for video */ @@ -5514,12 +5655,20 @@ 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 */ - /* 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][;...] */ + /* 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][;...] */ 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, '@'); @@ -5563,11 +5712,45 @@ 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; + } +#else + 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); +#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(p->sa.sin_addr), port, p->transport); +#else ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port); +#endif /* SIP_TCP_SUPPORT */ } /*! \brief Initialize SIP response, based on SIP request */ @@ -6568,9 +6751,18 @@ { /* Construct Contact: header */ if (ourport != STANDARD_SIP_PORT) +#ifdef SIP_TCP_SUPPORT + ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport, p->transport); +#else ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport); - else +#endif +else +#ifdef SIP_TCP_SUPPORT + ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), p->transport); +#else ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip)); +#endif /* SIP_TCP_SUPPORT */ + } /*! \brief Build the Remote Party-ID & From using callingpres options */ @@ -6726,7 +6918,11 @@ snprintf(from, sizeof(from), "\"%s\" ;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), 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 { @@ -7703,7 +7899,9 @@ int port; char *host, *pt; char *contact; - +#ifdef SIP_TCP_SUPPORT + char *t, *q, *n; +#endif if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) { /* NAT: Don't trust the contact field. Just use what they came to us @@ -7717,8 +7915,51 @@ contact = ast_strdupa(pvt->fullcontact); /* We have only the part in here so we just need to parse a SIP URI.*/ +#ifdef SIP_TCP_SUPPORT + + if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL)) + if (parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL)) + ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip: or sips:) trying to use anyway\n", contact); + + /* transport parameter */ + t = NULL; + q = NULL; + t = strstr(contact, "transport="); + if( t ) { + t += 10; + } + q = strstr(contact, "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 : "" ); + } + +#else if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL)) ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact); +#endif + port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT; ast_verbose("--- set_address_from_contact host '%s'\n", host); @@ -7751,6 +7992,10 @@ struct ast_hostent ahp; struct sockaddr_in oldsin; +#ifdef SIP_TCP_SUPPORT + char *t = NULL; +#endif + ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); if (ast_strlen_zero(expires)) { /* No expires header, try look in Contact: */ @@ -7831,10 +8076,53 @@ /* Save SIP options profile */ peer->sipoptions = pvt->sipoptions; +#ifdef SIP_TCP_SUPPORT + /* Check a peer has old stalled TCP or TLS connection */ + /* if (peer->ssl) { + SSL_clear(peer->ssl); + SSL_free(peer->ssl); + peer->ssl = NULL; + } + if (peer->sockfd > 0) { + close(peer->sockfd); + peer->sockfd = -1; + }*/ + peer->sockfd = pvt->sockfd; + peer->ssl = pvt->ssl; + if (t) { + if(!strncasecmp(t, "tcp", 3)) { + ast_copy_string(peer->transport, "TCP", sizeof(peer->transport)); + } else if (!strncasecmp(t, "tls", 3)) { + ast_copy_string(peer->transport, "TLS", sizeof(peer->transport)); + if (!pvt->ssl) + ast_log(LOG_ERROR, "transport tls comes without SSL from peer %s, fd %d\n", peer->name, pvt->sockfd); + } else if (!strncasecmp(t, "udp", 3)) { + ast_copy_string(peer->transport, "UDP", sizeof(peer->transport)); + } else { + ast_log(LOG_ERROR, "Unsupported transport %s comes from peer %s, fd %d\n", t, peer->name, pvt->sockfd); + return PARSE_REGISTER_FAILED; + } + } + else { + if( pvt->sockfd == sipsock ) { + ast_copy_string(peer->transport, "UDP", sizeof(peer->transport)); + } else { + if (pvt->ssl) + ast_copy_string(peer->transport, "TLS", sizeof(peer->transport)); + else if (pvt->sockfd != sipsock) + ast_copy_string(peer->transport, "TCP", sizeof(peer->transport)); + } + } +#endif + if (!ast_strlen_zero(curi)) /* Overwrite the default username from config at registration */ ast_copy_string(peer->username, curi, sizeof(peer->username)); else - peer->username[0] = '\0'; +#ifdef SIP_TCP_SUPPORT + ast_copy_string(peer->username, peer->name, sizeof(peer->username)); +#else + peer->username[0] = '\0'; +#endif if (peer->expire > -1) { ast_sched_del(sched, peer->expire); @@ -8267,7 +8555,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(sin->sin_addr)); } @@ -8895,6 +9185,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 */ @@ -8910,7 +9204,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; } @@ -8927,9 +9234,17 @@ memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); p->sa.sin_port = htons(pt ? atoi(pt) : STANDARD_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)) { const struct sockaddr_in *dst = sip_real_dst(p); +#ifdef SIP_TCP_SUPPORT + ast_verbose("Sending to %s: %d; transport %s (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), p->transport, sip_nat_mode(p)); +#else ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), sip_nat_mode(p)); +#endif /* SIP_TCP_SUPPORT */ } } } @@ -10176,6 +10491,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(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port)); ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(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 if (!ast_strlen_zero(global_regcontext)) ast_cli(fd, " Reg. exten : %s\n", peer->regexten); ast_cli(fd, " Def. Username: %s\n", peer->username); @@ -14772,6 +15091,59 @@ 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 Read data from SIP socket \note sipsock_read locks the owner channel while we are processing the SIP message \return 1 on error, 0 on success @@ -14786,9 +15158,70 @@ socklen_t len = sizeof(sin); int nounlock; int recount = 0; +#ifdef SIP_TCP_SUPPORT + SSL *ssl = (SSL *)ignore; + int msglen = 0, n = 0; +#endif int lockretry; 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__) @@ -14811,7 +15244,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<------------->\n", ast_inet_ntoa(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<------------->\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data); +#endif /* SIP_TCP_SUPPORT */ parse_request(&req); req.method = find_sip_method(req.rlPart1); @@ -14859,6 +15296,18 @@ append_history(p, "LockFail", "Owner lock failed, transaction failed."); return 1; } +#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 */ @@ -15007,6 +15456,71 @@ } } +#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; + + 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(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; + 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(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 The SIP monitoring thread \note This thread monitors all the SIP sessions and peers that needs notification of mwi (and thus do not have a separate thread) indefinitely @@ -15025,6 +15539,13 @@ /* Add an I/O event to our SIP 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) + ast_io_add(io, siptcpsock, siptcp_accept, AST_IO_IN, NULL); + if (siptlssock > -1) + ast_io_add(io, siptlssock, siptls_accept, AST_IO_IN, NULL); +#endif /* From here on out, we die whenever asked */ for(;;) { @@ -15205,6 +15726,14 @@ else ast_string_field_set(p, tohost, ast_inet_ntoa(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)) p->ourip = __ourip; @@ -15842,6 +16371,11 @@ peer->pickupgroup = 0; peer->maxms = default_qualify; peer->prefs = default_prefs; +#ifdef SIP_TCP_SUPPORT + peer->sockfd = -1; + peer->ssl = NULL; + ast_copy_string(peer->transport, DEFAULT_TRANSPORT, sizeof(peer->transport)); +#endif } /*! \brief Create temporary peer (used in autocreatepeer mode) */ @@ -15981,7 +16515,16 @@ peer->expire = -1; ast_clear_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC); if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) { - if (ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._udp" : NULL)) { + #ifdef SIP_TCP_SUPPORT + if (ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._udp" : NULL) && + ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._tcp" : NULL) && + ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._tls" : NULL) && + ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sipinternal._udp" : NULL) && + ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sipinternal._tcp" : NULL) && + ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sipinternal._tls" : NULL)) { + #else + if (ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._udp" : NULL)) { + #endif unref_peer(peer); return NULL; } @@ -16080,6 +16623,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)); + } 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, "maxcallbitrate")) { peer->maxcallbitrate = atoi(v->value); if (peer->maxcallbitrate < 0) @@ -16144,6 +16699,9 @@ struct ast_flags dummy[2]; int auto_sip_domains = FALSE; struct sockaddr_in old_bindaddr = bindaddr; +#ifdef SIP_TCP_SUPPORT + struct sockaddr_in old_tlsbindaddr = tlsbindaddr; +#endif int registry_count = 0, peer_count = 0, user_count = 0; struct ast_flags debugflag = {0}; @@ -16343,8 +16901,25 @@ } else if (!strcasecmp(v->name, "fromdomain")) { ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain)); } else if (!strcasecmp(v->name, "outboundproxy")) { - if (ast_get_ip_or_srv(&outboundproxyip, v->value, global_srvlookup ? "_sip._udp" : NULL) < 0) - ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value); + //if (ast_get_ip_or_srv(&outboundproxyip, v->value, global_srvlookup ? "_sip._udp" : NULL) < 0) + //ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value); + + #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(&outboundproxyip, v->value, global_srvlookup ? "_sip._udp" : NULL) < 0) { + #endif + ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value); + ASTOBJ_UNREF(peer, sip_destroy_peer); + return NULL; + } + + } else if (!strcasecmp(v->name, "outboundproxyport")) { /* Port needs to be after IP */ sscanf(v->value, "%d", &format); @@ -16466,6 +17041,26 @@ } } else if (!strcasecmp(v->name, "callevents")) { global_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, "maxcallbitrate")) { default_maxcallbitrate = atoi(v->value); if (default_maxcallbitrate < 0) @@ -16638,6 +17233,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(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(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", global_tos_sip); + } + if (setsockopt(siptcpsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) + ast_log(LOG_WARNING, "Unable to set TOS to %d in SIP TCP\n", global_tos_sip); + 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(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(tlsbindaddr.sin_addr), ntohs(tlsbindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", global_tos_sip); + } + if (setsockopt(siptlssock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) + ast_log(LOG_WARNING, "Unable to set TOS to %d in SIP TLS\n", global_tos_sip); + 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 */ @@ -17315,6 +17989,108 @@ 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 PBX load module - initialization */ static int load_module(void) { @@ -17338,6 +18114,12 @@ if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */ return AST_MODULE_LOAD_DECLINE; +#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 'SIP'\n"); @@ -17442,6 +18224,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); @@ -17453,7 +18244,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; }