? siptcpchanges2.patch Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.784 diff -u -r1.784 chan_sip.c --- channels/chan_sip.c 20 Jul 2005 17:05:18 -0000 1.784 +++ channels/chan_sip.c 5 Aug 2005 04:34:13 -0000 @@ -67,6 +67,8 @@ #include "asterisk/astosp.h" #endif +#define SIP_TCP_SUPPORT /* this will enable SIP over TCP */ + #ifndef DEFAULT_USERAGENT #define DEFAULT_USERAGENT "Asterisk PBX" #endif @@ -506,6 +508,11 @@ int redircodecs; /* Redirect codecs */ struct sockaddr_in recv; /* Received as */ struct in_addr ourip; /* Our IP */ +#ifdef SIP_TCP_SUPPORT + int securechannel; /* TLS is secure, TCP/UDP is not secure */ + int sockfd; /* socket fd used by this SIP channel*/ + char transport[4]; /* transport protocol, UDP or TCP */ +#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 */ @@ -668,6 +675,10 @@ struct ast_dnsmgr_entry *dnsmgr;/* DNS refresh manager for peer */ struct sockaddr_in addr; /* IP address of peer */ struct in_addr mask; +#ifdef SIP_TCP_SUPPORT + 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 */ @@ -748,6 +759,10 @@ static int __sip_do_register(struct sip_registry *r); static int sipsock = -1; +#ifdef SIP_TCP_SUPPORT +static int siptcpsock = -1; /* TCP listening socket */ +#endif + static struct sockaddr_in bindaddr; @@ -786,6 +801,9 @@ static int sip_do_reload(void); static int expire_register(void *data); static int callevents = 0; +#ifdef SIP_TCP_SUPPORT +static int sipsock_read(int *id, int fd, short events, void *ignore); +#endif static struct ast_channel *sip_request(const char *type, int format, void *data, int *cause); static int sip_devicestate(void *data); @@ -931,10 +949,19 @@ int res; char iabuf[INET_ADDRSTRLEN]; +#ifdef SIP_TCP_SUPPORT + if ((p->sockfd > -1) && (p->sockfd != sipsock)) { /* This is TCP */ + /* ast_verbose("TCP_Write: fd %d\n", p->sockfd); */ + res = write(p->sockfd, data, len); + } else { +#endif if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in)); else res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in)); +#ifdef SIP_TCP_SUPPORT + } +#endif if (res != len) { ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), res, strerror(errno)); } @@ -949,10 +976,17 @@ char iabuf[INET_ADDRSTRLEN]; /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ +#ifdef SIP_TCP_SUPPORT + if (ast_test_flag(p, SIP_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 (ast_test_flag(p, SIP_NAT) & SIP_NAT_RFC3581) snprintf(buf, len, "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(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif } /*--- ast_sip_ouraddrfor: NAT fix - decide which IP address to use for ASterisk server? ---*/ @@ -1257,6 +1291,49 @@ 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) { @@ -1270,7 +1347,28 @@ else ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data); } +#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) && !(ast_test_flag(p, SIP_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"); + 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")); @@ -1304,7 +1402,28 @@ else ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data); } +#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) && !(ast_test_flag(p, SIP_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"); + 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")); @@ -1669,6 +1788,11 @@ ast_copy_string(r->peermd5secret, peer->md5secret, sizeof(r->peermd5secret)); ast_copy_string(r->tohost, peer->tohost, sizeof(r->tohost)); ast_copy_string(r->fullcontact, peer->fullcontact, sizeof(r->fullcontact)); +#ifdef SIP_TCP_SUPPORT + r->sockfd = peer->tcpsockfd; + ast_copy_string(r->transport, peer->transport, sizeof(r->transport)); +#endif + if (!r->initreq.headers && !ast_strlen_zero(peer->fromdomain)) { if ((callhost = strchr(r->callid, '@'))) { strncpy(callhost + 1, peer->fromdomain, sizeof(r->callid) - (callhost - r->callid) - 2); @@ -1900,6 +2024,10 @@ free_old_route(p->route); p->route = NULL; } +#if 0 //def SIP_TCP_SUPPORT + if ((p->sockfd > -1) && (p->sockfd != sipsock)) /* if TCP, close it */ + close(p->sockfd); +#endif if (p->registry) { if (p->registry->call == p) p->registry->call = NULL; @@ -2807,6 +2935,11 @@ } else { memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); } +#ifdef SIP_TCP_SUPPORT + p->securechannel = 0; + p->sockfd = -1; + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); /* default transport protocol */ +#endif p->branch = rand(); p->tag = rand(); @@ -4315,6 +4448,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 ast_copy_string(invite, p->fullcontact, sizeof(invite)); /* Otherwise, use the username while waiting for registration */ } else if (!ast_strlen_zero(p->username)) { @@ -5196,6 +5334,9 @@ 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)) { /* No expires header */ expires = strstr(get_header(req, "Contact"), "expires="); @@ -5229,6 +5370,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 ast_copy_string(p->fullcontact, c, sizeof(p->fullcontact)); /* For the 200 OK, we should use the received contact */ snprintf(pvt->our_contact, sizeof(pvt->our_contact) - 1, "<%s>", c); @@ -5237,11 +5384,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) { @@ -5274,11 +5441,21 @@ with */ memcpy(&p->addr, &pvt->recv, sizeof(p->addr)); } +#ifdef SIP_TCP_SUPPORT + if (t && !strncasecmp(t, "tcp", strlen("tcp"))) { + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + p->tcpsockfd = pvt->sockfd; + } +#endif if (c) /* Overwrite the default username from config at registration */ ast_copy_string(p->username, c, sizeof(p->username)); else +#ifdef SIP_TCP_SUPPORT + ast_copy_string(p->username, p->name, sizeof(p->username)); +#else p->username[0] = '\0'; +#endif if (p->expire > -1) ast_sched_del(sched, p->expire); @@ -6074,7 +6251,11 @@ if (c) { *c = '\0'; c = ast_skip_blanks(c+1); +#ifdef SIP_TCP_SUPPORT + if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP")) { +#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; } @@ -7095,6 +7276,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 socket fd : %d\n", peer->tcpsockfd); + ast_cli(fd, " Transport : %s\n", peer->transport); +#endif ast_cli(fd, " Def. Username: %s\n", peer->username); ast_cli(fd, " SIP Options : "); if (peer->sipoptions) { @@ -9713,6 +9898,24 @@ len = sizeof(sin); memset(&req, 0, sizeof(req)); +#ifdef SIP_TCP_SUPPORT + if (fd != sipsock) { /* This must be TCP connection socket */ + res = getpeername(fd, (struct sockaddr *)&sin, &len); + if (res < 0) + ast_log(LOG_WARNING, "SIP TCP getpeername error: %s\n", strerror(errno)); + 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_WARNING, "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 !defined(__FreeBSD__) @@ -9730,7 +9933,11 @@ if (pedanticsipchecking) req.len = lws2sws(req.data, req.len); 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<-- SIP read from %s:%d: \n%s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), req.data); +#endif parse(&req); if (debug) { ast_verbose("--- (%d headers %d lines)", req.headers, req.lines); @@ -9769,6 +9976,12 @@ 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 */ + p->securechannel = 0; /* Only TLS is secure channel */ + if (fd != sipsock) + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); +#endif nounlock = 0; handle_request(p, &req, &sin, &recount, &nounlock); if (p->owner && !nounlock) @@ -9822,6 +10035,29 @@ 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; + } +#endif + /*--- do_monitor: The SIP monitoring thread ---*/ static void *do_monitor(void *data) { @@ -9838,6 +10074,11 @@ 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); +#endif + /* This thread monitors all the frame relay interfaces which are not yet in use (and thus do not have a separate thread) indefinitely */ /* From here on out, we die whenever asked */ @@ -10040,6 +10281,12 @@ ast_copy_string(p->tohost, peer->tohost, sizeof(p->tohost)); else ast_inet_ntoa(p->tohost, sizeof(p->tohost), peer->addr.sin_addr); +#ifdef SIP_TCP_SUPPORT + p->sockfd = peer->tcpsockfd; + ast_copy_string(p->transport, peer->transport, sizeof(p->transport)); + 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)) @@ -10605,6 +10852,10 @@ oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; +#ifdef SIP_TCP_SUPPORT + peer->tcpsockfd = -1; + ast_copy_string(peer->transport, "UDP", sizeof(peer->transport)); +#endif ast_copy_flags(peer, &global_flags, SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_DTMF | SIP_REINVITE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE | @@ -11124,6 +11375,40 @@ } } } +#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)); + } + } + } + } + } +#endif ast_mutex_unlock(&netlock); /* Release configuration from memory */ @@ -11687,6 +11972,10 @@ clear_realm_authentication(authl); close(sipsock); +#ifdef SIP_TCP_SUPPORT + if (siptcpsock > -1) + close(siptcpsock); +#endif return 0; }