? core.528 ? siptcpchanges-1.0.9.patch Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.510.2.70 diff -u -r1.510.2.70 chan_sip.c --- channels/chan_sip.c 21 Jun 2005 14:15:55 -0000 1.510.2.70 +++ channels/chan_sip.c 26 Aug 2005 04:52:43 -0000 @@ -72,6 +72,19 @@ #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 @@ -167,6 +180,19 @@ static int global_ospauth = 0; #endif +#ifdef SIP_TCP_SUPPORT +#define DEFAULT_SIP_TLS_PORT 5061 /* From RFC 3261 */ +#define DEFAULT_PASSWORD "kerdosa1" +#define DEFAULT_ENTROPY "/dev/urandom" +#define DEFAULT_ROOTCACERT "/var/lib/asterisk/keys/rootcert.pem" +#define DEFAULT_ASTERISKCERT "/var/lib/asterisk/keys/server.pem" +#define DEFAULT_DH512 "/var/lib/asterisk/keys/dh512.pem" +#define DEFAULT_DH1024 "/var/lib/asterisk/keys/dh1024.pem" + +//#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" +#define CIPHER_LIST "ALL" +#endif + static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); @@ -288,6 +314,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 */ @@ -459,6 +492,12 @@ struct sockaddr_in addr; struct in_addr mask; +#ifdef SIP_TCP_SUPPORT + SSL *ssl; /* SSL object for TLS connection */ + int tcpsockfd; /* TCP connection socket is saved to here */ + char transport[4]; /* transport protocol, UDP or TCP */ +#endif + /* Qualification */ struct sip_pvt *call; /* Call pointer */ int pokeexpire; /* When to expire poke */ @@ -548,6 +587,14 @@ static int __sip_do_register(struct sip_registry *r); 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 int global_nat = SIP_NAT_RFC3581; static int global_canreinvite = REINVITE_INVITE; @@ -575,6 +622,9 @@ static void prune_peers(void); static int sip_do_reload(void); +#ifdef SIP_TCP_SUPPORT +static int sipsock_read(int *id, int fd, short events, void *ignore); +#endif /*--- sip_debug_test_addr: See if we pass debug IP filter */ static inline int sip_debug_test_addr(struct sockaddr_in *addr) @@ -601,13 +651,44 @@ /*--- __sip_xmit: Transmit SIP message ---*/ static int __sip_xmit(struct sip_pvt *p, char *data, int len) { - int res; + int res = 0; char iabuf[INET_ADDRSTRLEN]; // ast_log(LOG_WARNING, "__sip_xmit from '%s' to '%s'\n", "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr)); + +#ifdef SIP_TCP_SUPPORT + if ((p->sockfd > -1) && (p->sockfd != sipsock)) { /* This is TCP */ + /* ast_verbose("TCP_Write: fd %d\n", p->sockfd); */ + if (p->ssl) { /* TLS write */ + 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"); + if (SSL_get_shutdown(p->ssl) & SSL_RECEIVED_SHUTDOWN) + SSL_shutdown(p->ssl); + else + SSL_clear(p->ssl); + SSL_free(p->ssl); + p->ssl = NULL; + break; + } + } + } else /* TCP write */ + res = write(p->sockfd, data, len); + } else { +#endif + if (p->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 returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), res, strerror(errno)); } @@ -885,6 +966,49 @@ dst->len = src->len; parse(dst); } + +#ifdef SIP_TCP_SUPPORT +static int tcp_connect(struct sip_pvt *p) +{ + int fd = -1; + struct sockaddr_in myaddr; + char iabuf[INET_ADDRSTRLEN]; + + if (!strncasecmp(p->transport, "UDP", 3)) { + return sipsock; + } + + /* Do we need to handle NAT here? */ + + if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ast_log(LOG_WARNING, "TCP can't create socket : %s\n", strerror(errno)); + return -1; + } + + /* 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)); + + return fd; +} +#endif + /*--- send_response: Transmit response on SIP request---*/ static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) { @@ -892,13 +1016,59 @@ char iabuf[INET_ADDRSTRLEN]; struct sip_request tmp; char tmpmsg[80]; +#ifdef SIP_TCP_SUPPORT + BIO *bio; + SSL *ssl; +#endif + if (sip_debug_test_pvt(p)) { if (p->nat & SIP_NAT_ROUTE) ast_verbose("%sTransmitting (NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port)); else ast_verbose("%sTransmitting (no NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port)); } + +#ifdef SIP_TCP_SUPPORT + /* if transport is TCP and not opened connection, connect now */ + if (strncasecmp(p->transport, "UDP", 3)) { + /* make TCP connection only when not connected and no NAT/firewall */ + if ((p->sockfd < 0) && !(p->nat & SIP_NAT_ROUTE)) { + p->sockfd = tcp_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; + } + + /* If TLS, do TLS handshake */ + if (!strncasecmp(p->transport, "TLS", 3)) { + /* Initiate TLS handshake */ + bio = BIO_new_socket(p->sockfd, BIO_CLOSE); + ssl = SSL_new(tlsctx); + SSL_set_bio(ssl, bio, bio); + + if((res = SSL_connect(ssl) <= 0)) { + ast_log(LOG_ERROR, "SSL connect error"); + SSL_free(ssl); + close(p->sockfd); + p->ssl = NULL; + p->sockfd = -1; + return 2; + } + ast_log(LOG_DEBUG, "New TLS connection is opened\n"); + p->ssl = ssl; + } + ast_io_add(io, p->sockfd, sipsock_read, AST_IO_IN, NULL); + } 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 && (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")); @@ -931,7 +1101,29 @@ else ast_verbose("%sTransmitting:\n%s (no NAT) to %s:%d\n", reliable ? "Reliably " : "", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port)); } + +#ifdef SIP_TCP_SUPPORT + /* if transport is TCP and not opened connection, connect now */ + if (strncasecmp(p->transport, "UDP", 3)) { + /* make TCP connection only when not connected and no NAT/firewall */ + if ((p->sockfd < 0) && !(p->nat & SIP_NAT_ROUTE)) { + p->sockfd = tcp_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, NULL); + } 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 && p->sockfd == sipsock) { /* Only UDP needs this */ +#else if (reliable) { +#endif if (recordhistory) { parse_copy(&tmp, req); snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); @@ -1321,6 +1513,10 @@ strncpy(r->username, p->username, sizeof(r->username)-1); strncpy(r->tohost, p->tohost, sizeof(r->tohost)-1); strncpy(r->fullcontact, p->fullcontact, sizeof(r->fullcontact)-1); +#ifdef SIP_TCP_SUPPORT + r->sockfd = p->tcpsockfd; + strncpy(r->transport, p->transport, sizeof(r->transport)-1); +#endif if (!r->initreq.headers && !ast_strlen_zero(p->fromdomain)) { if ((callhost = strchr(r->callid, '@'))) { strncpy(callhost + 1, p->fromdomain, sizeof(r->callid) - (callhost - r->callid) - 2); @@ -1539,6 +1735,12 @@ } ast_mutex_unlock(®l.lock); } +#if 0 //def SIP_TCP_SUPPORT + if ((p->sockfd > -1) && (p->sockfd != sipsock)) /* if TCP/TLS, close it */ + 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) @@ -2301,6 +2503,12 @@ } else { memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); } +#ifdef SIP_TCP_SUPPORT + p->ssl = NULL; + p->sockfd = -1; + strncpy(p->transport, "UDP", sizeof(p->transport)-1); /* default transport protocol */ +#endif + p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (videosupport) p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); @@ -3709,6 +3917,11 @@ /* If we're calling a registred SIP peer, use the fullcontact to dial to the peer */ if (!ast_strlen_zero(p->fullcontact)) { /* If we have full contact, trust it */ +#ifdef SIP_TCP_SUPPORT + if (!strchr(p->fullcontact, '@')) + snprintf(invite, sizeof(invite), "sip:%s@%s:%d",p->username, p->tohost, ntohs(p->sa.sin_port)); + else +#endif strncpy(invite, p->fullcontact, sizeof(invite) - 1); /* Otherwise, use the username while waiting for registration */ } else if (!ast_strlen_zero(p->username)) { @@ -4437,6 +4650,10 @@ struct hostent *hp; struct ast_hostent ahp; struct sockaddr_in oldsin; +#ifdef SIP_TCP_SUPPORT + char *t = NULL, *q = NULL; +#endif + if (ast_strlen_zero(expires)) { expires = strstr(get_header(req, "Contact"), "expires="); if (expires) { @@ -4474,6 +4691,12 @@ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", p->name); return 0; } +#ifdef SIP_TCP_SUPPORT + /* we should remove transport, q parameter if exist */ + n = strchr(c, ';'); + if (n) + *n = '\0'; +#endif strncpy(p->fullcontact, c, sizeof(p->fullcontact) - 1); /* For the 200 OK, we should use the received contact */ snprintf(pvt->our_contact, sizeof(pvt->our_contact) - 1, "<%s>", c); @@ -4482,11 +4705,31 @@ ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c); } else c += 4; +#ifdef SIP_TCP_SUPPORT + /* transport and q parameter */ + while (n) { + n ++; + if (!strncasecmp(n, "transport=", 10)) { + t = strchr(n, '='); + t ++; + } else if (!strncasecmp(n, "q=", 2)) { + q = strchr(n, '='); + q ++; + } + n = strchr(n, ';'); + if (n) { + *n = '\0'; + } + } + if (option_verbose > 2 && t) + ast_verbose(VERBOSE_PREFIX_3 "Contact header: transport %s\n", t); +#else /* Ditch q */ n = strchr(c, ';'); if (n) { *n = '\0'; } +#endif /* Grab host */ n = strchr(c, '@'); if (!n) { @@ -4519,10 +4762,20 @@ with */ memcpy(&p->addr, &pvt->recv, sizeof(p->addr)); } +#ifdef SIP_TCP_SUPPORT + if (t && !strncasecmp(t, "tcp", strlen("tcp"))) { + strncpy(p->transport, "TCP", sizeof(p->transport)-1); + p->tcpsockfd = pvt->sockfd; + } +#endif if (c) strncpy(p->username, c, sizeof(p->username) - 1); else +#ifdef SIP_TCP_SUPPORT + strncpy(p->username, p->name, sizeof(p->username)); +#else p->username[0] = '\0'; +#endif if (p->expire > -1) ast_sched_del(sched, p->expire); if ((expiry < 1) || (expiry > max_expiry)) @@ -5270,7 +5523,11 @@ c++; while(*c && (*c < 33)) c++; +#ifdef SIP_TCP_SUPPORT + if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP") && strcasecmp(via, "SIP/2.0/TLS")) { +#else if (strcasecmp(via, "SIP/2.0/UDP")) { +#endif ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via); return -1; } @@ -5851,6 +6108,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, " TCP fd : %d\n", peer->tcpsockfd); + ast_cli(fd, " Transport : %s\n", peer->transport); +#endif ast_cli(fd, " Username : %s\n", peer->username); ast_cli(fd, " Codecs : "); ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); @@ -7789,9 +8050,64 @@ int nounlock; int recount = 0; int debug; +#ifdef SIP_TCP_SUPPORT + char iabuf[INET_ADDRSTRLEN]; + SSL *ssl = (SSL *)ignore; +#endif len = sizeof(sin); memset(&req, 0, sizeof(req)); + +#ifdef SIP_TCP_SUPPORT + if (fd != sipsock) { /* It is TCP socket */ + res = getpeername(fd, (struct sockaddr *)&sin, &len); + if (res < 0) + ast_log(LOG_WARNING, "SIP 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 */ + res = SSL_read(ssl, req.data, sizeof(req.data) - 1); + + switch(SSL_get_error(ssl, res)) { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_ZERO_RETURN: /* The peer closed the connection */ + SSL_shutdown(ssl); + ast_log(LOG_NOTICE, "The peer closed TLS Connection, shutdowned TLS\n"); + // SSL_free(ssl); + return 0; + break; + case SSL_ERROR_SYSCALL: + ast_log(LOG_ERROR, "SIP TLS: SSL_ERROR_SYSCALL, shutdown TLS\n"); + SSL_clear(ssl); + // SSL_free(ssl); + return 0; + break; + default: + ast_log(LOG_ERROR, "SIP TLS read error: ssl_err %d, %s\n", SSL_get_error(ssl, res), strerror(errno)); + SSL_clear(ssl); + ast_log(LOG_NOTICE, "TLS Connection closed\n"); + // SSL_free(ssl); + return 0; + break; + } + } else { /* This must be TCP connection socket */ + res = read(fd, req.data, sizeof(req.data) - 1); + if (res == 0) { /* The client closed the TCP connection? */ + ast_log(LOG_NOTICE, "SIP TCP connection closed : fd %d\n", fd); + close(fd); + return 0; /* Remove it */ + } + else if (res < 0) { + ast_log(LOG_ERROR, "SIP TCP read error: %s\n", strerror(errno)); + close(fd); + return 0; /* Remove it */ + } + } + } else +#endif res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); if (res < 0) { if (errno == EAGAIN) @@ -7804,7 +8120,11 @@ req.len = res; debug = sip_debug_test_addr(&sin); if (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" : "TCP", req.data); +#else ast_verbose("\n\nSip read: \n%s\n", req.data); +#endif req.len = lws2sws(req.data, req.len); parse(&req); if (debug) @@ -7834,6 +8154,16 @@ snprintf(tmp, sizeof(tmp), "%s / %s", req.data, get_header(&req, "CSeq")); append_history(p, "Rx", tmp); } +#ifdef SIP_TCP_SUPPORT + p->sockfd = fd; /* Save socket fd to send a response for TCP */ + if (fd != sipsock) { + if (ssl) { + p->ssl = ssl; /* Only TLS will have non-null ssl */ + strncpy(p->transport, "TLS", sizeof(p->transport)-1); + } else + strncpy(p->transport, "TCP", sizeof(p->transport)-1); + } +#endif nounlock = 0; handle_request(p, &req, &sin, &recount, &nounlock); if (p->owner && !nounlock) @@ -7896,6 +8226,70 @@ 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 (sipdebug) + 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 (sipdebug) + 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)); + ast_io_add(io, tlsconnfd, sipsock_read, AST_IO_IN, ssl); + return 1; +} +#endif + /*--- do_monitor: The SIP monitoring thread ---*/ static void *do_monitor(void *data) { @@ -7907,9 +8301,17 @@ int lastpeernum = -1; int curpeernum; int reloading; + /* Add an I/O event to our UDP socket */ if (sipsock > -1) 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 /* This thread monitors all the frame relay interfaces which are not yet in use (and thus do not have a separate thread) indefinitely */ @@ -8108,6 +8510,13 @@ else ast_inet_ntoa(p->tohost, sizeof(p->tohost), peer->addr.sin_addr); +#ifdef SIP_TCP_SUPPORT + p->sockfd = peer->tcpsockfd; + strncpy(p->transport, peer->transport, sizeof(p->transport)-1); + if (option_verbose > 2) + ast_verbose("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)); @@ -8488,6 +8897,10 @@ oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; +#ifdef SIP_TCP_SUPPORT + peer->tcpsockfd = -1; + strncpy(peer->transport, "UDP", sizeof(peer->transport)); +#endif peer->capability = global_capability; /* Assume can reinvite */ peer->canreinvite = global_canreinvite; @@ -8933,6 +9346,77 @@ } } } +#ifdef SIP_TCP_SUPPORT + if ((siptcpsock > -1) && (ntohs(bindaddr.sin_port) != oldport)) { + 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 { + 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 (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"); + } 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)); + } + } + } + } + } + + /* Open a TLS listening socket */ + memset(&tlsbindaddr, 0, sizeof(tlsbindaddr)); + memcpy(&tlsbindaddr.sin_addr, &bindaddr.sin_addr, sizeof(tlsbindaddr.sin_addr)); + tlsbindaddr.sin_port = ntohs(DEFAULT_SIP_TLS_PORT); + tlsbindaddr.sin_family = AF_INET; +// if ((siptlssock > -1) && (ntohs(tlsbindaddr.sin_port) != oldport)) { +// 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 { + 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 (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"); + } 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)); + } + } + } + } + } +#endif ast_mutex_unlock(&netlock); ast_destroy(cfg); @@ -9209,6 +9693,125 @@ static struct ast_cli_entry cli_sip_reload = { { "sip", "reload", NULL }, sip_reload, "Reload SIP configuration", sip_reload_usage }; + +#ifdef SIP_TCP_SUPPORT + +/* initailize SSL library */ +void init_OpenSSL(void) +{ + if (!SSL_library_init()) { + ast_log(LOG_ERROR, "SSL_library_init failed\n"); + //exit(-1); + } + SSL_load_error_strings(); + RAND_load_file(DEFAULT_ENTROPY, 1024); +} + +DH *dh512 = NULL; +DH *dh1024 = NULL; + +void init_dhparams(void) +{ + BIO *bio; + + bio = BIO_new_file(DEFAULT_DH512, "r"); + if (!bio) + ast_log(LOG_ERROR, "Error opening file dh512.pem"); + dh512 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + if (!dh512) + ast_log(LOG_ERROR, "Error reading DH parameters from dh512.pem"); + BIO_free(bio); + + bio = BIO_new_file(DEFAULT_DH1024, "r"); + if (!bio) + ast_log(LOG_ERROR, "Error opening file dh1024.pem"); + dh1024 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + if (!dh1024) + ast_log(LOG_ERROR, "Error reading DH parameters from dh1024.pem"); + BIO_free(bio); +} + +DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength) +{ + DH *ret; + + if (!dh512 || !dh1024) + init_dhparams(); + + switch (keylength) + { + case 512: + ret = dh512; + break; + case 1024: + default: /* generating DH params is too costly to do on the fly */ + ret = dh1024; + break; + } + return ret; +} + +int password_callback(char *buf, int num, int rwflag, void *userdata) +{ + if (num < (strlen(DEFAULT_PASSWORD)+1)) { + ast_log(LOG_ERROR, "password buf len %d is too small\n", num); + return 0; + } + strcpy(buf, DEFAULT_PASSWORD); + return (strlen(DEFAULT_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; +} + +SSL_CTX *setup_server_tls_ctx(void) +{ + SSL_CTX *ctx; + + ctx = SSL_CTX_new(TLSv1_method()); + + if (SSL_CTX_load_verify_locations(ctx, DEFAULT_ROOTCACERT, NULL) != 1) + ast_log(LOG_ERROR, "Error loading CA file and/or directory"); + + SSL_CTX_set_default_passwd_cb(ctx, password_callback); + + if (SSL_CTX_set_default_verify_paths(ctx) != 1) + ast_log(LOG_ERROR, "Error loading default CA file and/or directory"); + if (SSL_CTX_use_certificate_chain_file(ctx, DEFAULT_ASTERISKCERT) != 1) + ast_log(LOG_ERROR, "Error loading certificate from file"); + if (SSL_CTX_use_PrivateKey_file(ctx, DEFAULT_ASTERISKCERT, SSL_FILETYPE_PEM) != 1) + ast_log(LOG_ERROR, "Error loading private key from file"); + +// 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)"); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + return ctx; +} + +#endif + /*--- load_module: PBX load module - initialization ---*/ int load_module() { @@ -9227,6 +9830,10 @@ ast_log(LOG_WARNING, "Unable to create I/O context\n"); } +#ifdef SIP_TCP_SUPPORT + init_OpenSSL(); + tlsctx = setup_server_tls_ctx(); +#endif res = reload_config(); if (!res) { @@ -9349,6 +9956,15 @@ ast_mutex_destroy(®l.lock); close(sipsock); + +#ifdef SIP_TCP_SUPPORT + if (siptcpsock > -1) + close(siptcpsock); + if (siptlssock > -1) + close(siptlssock); + + SSL_CTX_free(tlsctx); /* destroy TLS CTX */ +#endif return 0; }