? siptcpchanges.patch ? keys/dh1024.pem ? keys/dh512.pem ? keys/servercert.pem ? keys/serverkey.pem ? keys/trustcerts.pem 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 1 Sep 2005 02:13:15 -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,27 @@ static int global_ospauth = 0; #endif +#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); @@ -288,6 +322,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 +500,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 +595,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 +630,10 @@ 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); +static struct sip_peer *find_peer(char *peer, struct sockaddr_in *sin); +#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 +660,49 @@ /*--- __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 + struct sip_peer *peer = NULL; + + 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"); + SSL_clear(p->ssl); + SSL_free(p->ssl); + p->ssl = NULL; + p->sockfd = -1; + peer = find_peer(p->peername, NULL); + if (peer && peer->ssl) { + peer->ssl = NULL; + peer->tcpsockfd = -1; + } + 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 +980,129 @@ dst->len = src->len; parse(dst); } + +#ifdef SIP_TCP_SUPPORT +static int cleanup_tcp_connection(int fd, SSL *ssl) +{ + int found = 0; + struct sip_peer *peer = NULL; + struct sip_pvt *p = NULL; + + if (fd == -1) { + ast_log(LOG_ERROR, "Tried to free up sockfd -1\n"); + return -1; + } + + ast_mutex_lock(&peerl.lock); + peer = peerl.peers; + + /* close tcp fd and free ssl in sip_peer */ + while(peer && !found) { + if (peer->tcpsockfd == fd) { + found = 1; + if (peer->ssl) { + SSL_clear(peer->ssl); + SSL_free(peer->ssl); + } + else + close(peer->tcpsockfd); + peer->ssl = NULL; + peer->tcpsockfd = -1; + strncpy(peer->transport, "UDP", sizeof(peer->transport)-1); + } + peer = peer->next; + } + ast_mutex_unlock(&peerl.lock); + + /* 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; + strncpy(peer->transport, "UDP", sizeof(peer->transport)-1); + 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]; + BIO *bio = NULL; + SSL *ssl = NULL; + + if (!strncasecmp(p->transport, "UDP", 3)) { + return sipsock; + } + + 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)); + + 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; + } + ast_log(LOG_DEBUG, "New TLS connection is opened\n"); + p->ssl = ssl; + } + + 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 +1110,36 @@ char iabuf[INET_ADDRSTRLEN]; struct sip_request tmp; char tmpmsg[80]; + 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)); + ast_verbose("%sTransmitting (NAT):\n%s\n to %s:%d with fd %d\n", reliable ? "Reliably " : "", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), p->sockfd); 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)); + ast_verbose("%sTransmitting (no NAT):\n%s\n to %s:%d with fd %d\n", reliable ? "Reliably " : "", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), p->sockfd); + } + +#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 = 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 && (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 +1172,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 = 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 && 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 +1584,11 @@ 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; + r->ssl = p->ssl; + 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 +1807,12 @@ } ast_mutex_unlock(®l.lock); } +/*#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) @@ -2301,6 +2575,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); @@ -2329,10 +2609,18 @@ strncpy(p->fromdomain, default_fromdomain, sizeof(p->fromdomain) - 1); /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT if (p->nat != SIP_NAT_NEVER) - snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else + if (p->nat != SIP_NAT_NEVER) + snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); + else /* Some implementations (e.g. Uniden UIP200) can't handle rport being in the message!! */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif + if (!callid) build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); else @@ -3142,10 +3430,17 @@ if (newbranch) { p->branch ^= rand(); +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else /* Some implementations (e.g. Uniden UIP200) can't handle rport being in the message!! */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif } if (!strcasecmp(msg, "CANCEL")) { c = p->initreq.rlPart2; /* Use original URI */ @@ -3709,6 +4004,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)) { @@ -3759,10 +4059,17 @@ if (init) { /* Bump branch even on initial requests */ p->branch ^= rand(); +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else /* Work around buggy UNIDEN UIP200 firmware */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif initreqprep(&req, p, cmd, vxml_url); } else reqprep(&req, p, cmd, 0, 1); @@ -4186,10 +4493,17 @@ p->ocseq = r->ocseq; /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(via, sizeof(via), "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(via, sizeof(via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(via, sizeof(via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else /* Work around buggy UNIDEN UIP200 firmware */ snprintf(via, sizeof(via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif add_header(&req, "Via", via); add_header(&req, "From", from); add_header(&req, "To", to); @@ -4437,6 +4751,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) { @@ -4469,11 +4787,18 @@ p->fullcontact[0] = '\0'; p->useragent[0] = '\0'; p->lastms = 0; + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", p->name); 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 +4807,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 +4864,43 @@ with */ 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->tcpsockfd > 0) { + close(p->tcpsockfd); + p->tcpsockfd = -1; + }*/ + p->tcpsockfd = pvt->sockfd; + p->ssl = pvt->ssl; + if (t) { + if(!strncasecmp(t, "tcp", strlen("tcp"))) { + strncpy(p->transport, "TCP", sizeof(p->transport)-1); + } else if (!strncasecmp(t, "tls", strlen("tls"))) { + strncpy(p->transport, "TLS", sizeof(p->transport)-1); + if (!pvt->ssl) + ast_log(LOG_ERROR, "transport tls comes without SSL from peer %s, fd %d\n", p->name, pvt->sockfd); + } + } + else { + if (pvt->ssl) + strncpy(p->transport, "TLS", sizeof(p->transport)-1); + else if (pvt->sockfd != sipsock) + strncpy(p->transport, "TCP", sizeof(p->transport)-1); + } +#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 +5648,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 +6233,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); @@ -7777,6 +8163,60 @@ return 0; } +#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 + /*--- 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) @@ -7789,9 +8229,70 @@ int nounlock; int recount = 0; int debug; +#ifdef SIP_TCP_SUPPORT + char iabuf[INET_ADDRSTRLEN]; + SSL *ssl = (SSL *)ignore; + int msglen = 0, n = 0; +#endif 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 (errno == EAGAIN) @@ -7804,7 +8305,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 +8339,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) @@ -7884,10 +8399,17 @@ if (ast_sip_ouraddrfor(&p->sa.sin_addr,&p->ourip)) memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else /* UNIDEN UIP200 bug */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); /* Send MWI */ p->outgoing = 1; @@ -7896,6 +8418,71 @@ 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)); + if (!ast_io_add(io, tlsconnfd, sipsock_read, AST_IO_IN, ssl)) + ast_log(LOG_ERROR, "ist_io_add failed\n"); + return 4; +} +#endif + /*--- do_monitor: The SIP monitoring thread ---*/ static void *do_monitor(void *data) { @@ -7907,9 +8494,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,14 +8703,28 @@ else ast_inet_ntoa(p->tohost, sizeof(p->tohost), peer->addr.sin_addr); +#ifdef SIP_TCP_SUPPORT + p->sockfd = peer->tcpsockfd; + p->ssl = peer->ssl; + strncpy(p->transport, peer->transport, sizeof(p->transport)-1); + /* 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)); /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); if (peer->pokeexpire > -1) @@ -8234,10 +8843,17 @@ if (ast_sip_ouraddrfor(&p->sa.sin_addr,&p->ourip)) memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT + if (p->nat & SIP_NAT_RFC3581) + snprintf(p->via, sizeof(p->via), "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(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else if (p->nat & SIP_NAT_RFC3581) snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); else /* UNIDEN bug */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); /* We have an extension to call, don't use the full contact here */ @@ -8488,6 +9104,11 @@ oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; +#ifdef SIP_TCP_SUPPORT + peer->tcpsockfd = -1; + peer->ssl = NULL; + strncpy(peer->transport, "UDP", sizeof(peer->transport)); +#endif peer->capability = global_capability; /* Assume can reinvite */ peer->canreinvite = global_canreinvite; @@ -8667,6 +9288,9 @@ struct hostent *hp; int format; int oldport = ntohs(bindaddr.sin_port); +#ifdef SIP_TCP_SUPPORT + int oldtlsport = ntohs(tlsbindaddr.sin_port); +#endif char iabuf[INET_ADDRSTRLEN]; global_dtmfmode = SIP_DTMF_RFC2833; @@ -8686,6 +9310,9 @@ memset(&localaddr, 0, sizeof(localaddr)); memset(&externip, 0, sizeof(externip)); memset(&prefs, 0 , sizeof(struct ast_codec_pref)); +#ifdef SIP_TCP_SUPPORT + memset(&tlsbindaddr, 0, sizeof(tlsbindaddr)); +#endif /* Initialize some reasonable defaults */ strncpy(default_context, "default", sizeof(default_context) - 1); @@ -8855,6 +9482,27 @@ strncpy(mydbname, v->value, sizeof(mydbname) - 1); #endif } +#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")) { + strncpy(trustcerts_file, v->value, sizeof(trustcerts_file)); + } else if (!strcasecmp(v->name, "servercert")) { + strncpy(servercert_file, v->value, sizeof(servercert_file)); + } else if (!strcasecmp(v->name, "serverkey")) { + strncpy(serverkey_file, v->value, sizeof(serverkey_file)); + } else if (!strcasecmp(v->name, "serverkeypassword")) { + strncpy(serverkey_password, v->value, sizeof(serverkey_password)); + } else if (!strcasecmp(v->name, "dh512param")) { + strncpy(dh512param_file, v->value, sizeof(dh512param_file)); + } else if (!strcasecmp(v->name, "dh1024param")) { + strncpy(dh1024param_file, v->value, sizeof(dh1024param_file)); + } +#endif /* else if (strcasecmp(v->name,"type")) * ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ @@ -8933,6 +9581,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 */ + 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) && (ntohs(tlsbindaddr.sin_port) != oldtlsport)) { + 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 +9928,111 @@ static struct ast_cli_entry cli_sip_reload = { { "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 + /*--- load_module: PBX load module - initialization ---*/ int load_module() { @@ -9227,9 +10051,12 @@ ast_log(LOG_WARNING, "Unable to create I/O context\n"); } - res = reload_config(); if (!res) { +#ifdef SIP_TCP_SUPPORT + tlsctx = init_OpenSSL(); +#endif + /* Make sure we can register our sip channel type */ if (ast_channel_register_ex(type, tdesc, ((AST_FORMAT_MAX_AUDIO << 1) - 1), sip_request, sip_devicestate)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); @@ -9273,6 +10100,9 @@ int unload_module() { struct sip_pvt *p, *pl; +#ifdef SIP_TCP_SUPPORT + struct sip_peer *peer; +#endif /* First, take us out of the channel loop */ ast_unregister_application(app_dtmfmode); @@ -9344,11 +10174,31 @@ if (localaddr) { ast_free_ha(localaddr); } + +#ifdef SIP_TCP_SUPPORT + peer = peerl.peers; + while(peer) { + if (peer->ssl) { + SSL_clear(peer->ssl); + SSL_free(peer->ssl); + } + p = p->next; + } +#endif ast_mutex_destroy(&userl.lock); ast_mutex_destroy(&peerl.lock); 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; } Index: configs/sip.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v retrieving revision 1.40.2.3 diff -u -r1.40.2.3 sip.conf.sample --- configs/sip.conf.sample 28 Mar 2005 05:54:19 -0000 1.40.2.3 +++ configs/sip.conf.sample 1 Sep 2005 02:13:15 -0000 @@ -75,6 +75,17 @@ ; ; local system will cause loops since SIP is incapable ; ; of performing a "hairpin" call. ; + +; The configuration for TLS support +;tlsport=5061 ; TLS port (SIP standard TLS port is 5061) +;serverkeypassword=asterisk ; password for encrypted private key +;trustcerts=/var/lib/asterisk/keys/trustcerts.pem ; Trusted root CA or certificates files +;servercert=/var/lib/asterisk/keys/servercert.pem ; Asterisk server certificate file +;serverkey=/var/lib/asterisk/keys/serverkey.pem ; Asterisk server private key file +;dh512param=/var/lib/asterisk/keys/dh512.pem ; ephemeral Diffe-Hellman parameter +;dh1024param=/var/lib/asterisk/keys/dh1024.pem ; ephemeral Diffe-Hellman parameter + +; ; If regcontext is specified, Asterisk will dynamically ; create and destroy a NoOp priority 1 extension for a given ; peer who registers or unregisters with us. The actual extension