--- ../clean/asterisk-1.4.8/channels/chan_sip.c 2007-07-26 12:19:11.000000000 +0200 +++ channels/chan_sip.c 2007-08-08 13:05:18.000000000 +0200 @@ -25,11 +25,10 @@ * See Also: * \arg \ref AstCREDITS * - * Implementation of RFC 3261 - without S/MIME, TCP and TLS support + * Implementation of RFC 3261 - without S/MIME and TLS support * Configuration file \link Config_sip sip.conf \endlink * * - * \todo SIP over TCP * \todo SIP over TLS * \todo Better support of forking * \todo VIA branch tag transaction checking @@ -597,6 +596,7 @@ static struct sched_context *sched; /*!< The scheduling context */ static struct io_context *io; /*!< The IO context */ static int *sipsock_read_id; /*!< ID of IO entry for sipsock FD */ +static int *siptcpsock_read_id; /*!< ID of IO entry for sipsock FD */ #define DEC_CALL_LIMIT 0 #define INC_CALL_LIMIT 1 @@ -786,10 +786,12 @@ #define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< 25: ???? */ #define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< 26: Buggy CISCO MWI fix */ #define SIP_PAGE2_OUTGOING_CALL (1 << 27) /*!< 27: Is this an outgoing call? */ +#define SIP_PAGE2_TCP (1 << 28) /*!< 28: Should we use TCP with this peer*/ +#define SIP_PAGE2_TCP_CONNECTED (1 << 29) /*!< 28: Is this TCP peer connected */ #define SIP_PAGE2_FLAGS_TO_COPY \ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \ - SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI) + SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI ) /* SIP packet flags */ #define SIP_PKT_DEBUG (1 << 0) /*!< Debug this packet */ @@ -1006,6 +1008,7 @@ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ int autoframing; + int sockfd; /*!< This is the socket FD for tcp connections */ } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -1118,6 +1121,7 @@ struct sip_pvt *mwipvt; /*!< Subscription for MWI */ int lastmsg; int autoframing; + int sockfd; /*!< Socket used by this peer for tcp connections*/ }; @@ -1183,7 +1187,8 @@ /* --- Sockets and networking --------------*/ -static int sipsock = -1; /*!< Main socket for SIP network communication */ +static int sipsock = -1; /*!< Main socket for SIP network communication UDP*/ +static int siptcpsock = -1; /*!< Main socket for SIP network communication TCP*/ static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */ static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */ static char externhost[MAXHOSTNAMELEN]; /*!< External host name (possibly with dynamic DNS and DHCP */ @@ -1755,7 +1760,20 @@ { int res; const struct sockaddr_in *dst = sip_real_dst(p); - res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in)); + /* This is a TCP connection*/ + if (ast_test_flag(&p->flags[1], SIP_PAGE2_TCP)) { + if (!ast_test_flag(&p->flags[1], SIP_PAGE2_TCP_CONNECTED)) { + if (connect(p->sockfd, (const struct sockaddr *)dst, sizeof(struct sockaddr_in)) == 0) { + ast_set_flag(&p->flags[1], SIP_PAGE2_TCP_CONNECTED); + } else if (errno == EISCONN) { + ast_set_flag(&p->flags[1], SIP_PAGE2_TCP_CONNECTED); + } else { + ast_log(LOG_ERROR, "Connect Failed Sock: %i %s:%d %s\n",p->sockfd,ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), strerror(errno)); + } + } + res = write(p->sockfd, data, len); + } else + res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in)); if (res == -1) { switch (errno) { @@ -1779,8 +1797,8 @@ const char *rport = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_RFC3581 ? ";rport" : ""; /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ - ast_string_field_build(p, via, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x%s", - ast_inet_ntoa(p->ourip), ourport, p->branch, rport); + ast_string_field_build(p, via, "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x%s", + ast_test_flag(&p->flags[1], SIP_PAGE2_TCP) ? "TCP" : "UDP", ast_inet_ntoa(p->ourip), ourport, p->branch, rport); } /*! \brief NAT fix - decide which IP address to use for ASterisk server? @@ -2650,6 +2668,8 @@ ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); + ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_TCP | SIP_PAGE2_TCP_CONNECTED); + dialog->sockfd = peer->sockfd; dialog->capability = peer->capability; if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) { ast_rtp_destroy(dialog->vrtp); @@ -4272,6 +4292,7 @@ p->autokillid = -1; p->subscribed = NONE; p->stateid = -1; + p->sockfd=-1; p->prefs = default_prefs; /* Set default codecs for this call */ if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */ @@ -7645,6 +7666,10 @@ return 0; memset(&peer->addr, 0, sizeof(peer->addr)); + if (peer->sockfd > 0) { + close(peer->sockfd); + ast_clear_flag(&peer->flags[1], SIP_PAGE2_TCP_CONNECTED); + } destroy_association(peer); /* remove registration data from storage */ @@ -7722,7 +7747,7 @@ peer->addr.sin_family = AF_INET; peer->addr.sin_addr = in; peer->addr.sin_port = htons(port); - if (sipsock < 0) { + if ((sipsock < 0) || (siptcpsock < 0)){ /* SIP isn't up yet, so schedule a poke only, pretty soon */ if (peer->pokeexpire > -1) ast_sched_del(sched, peer->pokeexpire); @@ -7964,6 +7989,13 @@ if (option_verbose > 3) ast_verbose(VERBOSE_PREFIX_3 "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name); } + + /* Allocate A TCP Soccket of incoming connection*/ + if (((!ast_test_flag(&peer->flags[1], SIP_PAGE2_TCP)) || (peer->sockfd != pvt->sockfd)) && (ast_test_flag(&pvt->flags[1], SIP_PAGE2_TCP))) { + ast_set_flag(&peer->flags[1], SIP_PAGE2_TCP); + peer->sockfd=pvt->sockfd; + ast_set_flag(&peer->flags[1], SIP_PAGE2_TCP_CONNECTED); + } return PARSE_REGISTER_UPDATE; } @@ -8929,7 +8961,7 @@ if (c) { *c = '\0'; c = ast_skip_blanks(c+1); - if (strcasecmp(via, "SIP/2.0/UDP")) { + if ((strcasecmp(via, "SIP/2.0/UDP")) && (strcasecmp(via, "SIP/2.0/TCP"))) { ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via); return; } @@ -10148,6 +10180,7 @@ 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)); + ast_cli(fd, " Transport : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_TCP) ? "TCP" : "UDP"); if (!ast_strlen_zero(global_regcontext)) ast_cli(fd, " Reg. exten : %s\n", peer->regexten); ast_cli(fd, " Def. Username: %s\n", peer->username); @@ -15003,7 +15036,18 @@ int lockretry; memset(&req, 0, sizeof(req)); - res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); + if (fd == sipsock) + res = recvfrom(fd, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); + else { + if (getpeername(fd, (struct sockaddr *)&sin, &len) < 0) { + close(fd); + return 1; + } + if ((res = read(fd, req.data, sizeof(req.data) - 1)) == 0) { + close(fd); + return 1; + } + } if (res < 0) { #if !defined(__FreeBSD__) if (errno == EAGAIN) @@ -15025,7 +15069,8 @@ if (pedanticsipchecking) req.len = lws2sws(req.data, req.len); /* Fix multiline headers */ if (ast_test_flag(&req, SIP_PKT_DEBUG)) - ast_verbose("\n<--- SIP read from %s:%d --->\n%s\n<------------->\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data); + 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" : "TCP",req.data); parse_request(&req); req.method = find_sip_method(req.rlPart1); @@ -15075,6 +15120,13 @@ return 1; } nounlock = 0; + /* Is this a TCP connection ?? if so set the socket accordingly*/ + if (fd != sipsock) { + p->sockfd=fd; + ast_set_flag(&p->flags[1], SIP_PAGE2_TCP); + } else { + p->sockfd=-1; + } if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) { /* Request failed */ if (option_debug) @@ -15091,6 +15143,18 @@ return 1; } +/*! \brief Accept incoming TCP connections*/ +static int siptcpsock_accept(int *id, int fd, short events, void *ignore) +{ + struct sockaddr_in sa; + socklen_t sa_len=sizeof(sa); + int newfd; + + if ((newfd=accept(siptcpsock, (struct sockaddr *)&sa, &sa_len)) >= 0) + ast_io_add(io, newfd, sipsock_read, AST_IO_IN, NULL); + return 1; +} + /*! \brief Send message waiting indication to alert peer that they've got voicemail */ static int sip_send_mwi_to_peer(struct sip_peer *peer) { @@ -15177,6 +15241,8 @@ /* 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); + if (siptcpsock > -1) + siptcpsock_read_id = ast_io_add(io, siptcpsock, siptcpsock_accept, AST_IO_IN, NULL); /* From here on out, we die whenever asked */ for(;;) { @@ -15197,6 +15263,13 @@ else sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL); } + + if (siptcpsock > -1) { + if (siptcpsock_read_id) + siptcpsock_read_id = ast_io_change(io, siptcpsock_read_id, siptcpsock, NULL, 0, NULL); + else + siptcpsock_read_id = ast_io_add(io, siptcpsock, siptcpsock_accept, AST_IO_IN, NULL); + } } /* Check for interfaces needing to be killed */ ast_mutex_lock(&iflock); @@ -15400,6 +15473,8 @@ p->recv = peer->addr; ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); + ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_TCP | SIP_PAGE2_TCP_CONNECTED); + p->sockfd = peer->sockfd; /* Send OPTIONs to peer's fullcontact */ if (!ast_strlen_zero(peer->fullcontact)) @@ -16152,6 +16227,7 @@ /* If we have realm authentication information, remove them (reload) */ clear_realm_authentication(peer->auth); peer->auth = NULL; + peer->sockfd = -1; for (; v || ((v = alt) && !(alt=NULL)); v = v->next) { if (handle_common_options(&peerflags[0], &mask[0], v)) @@ -16822,15 +16898,20 @@ bindaddr.sin_port = ntohs(STANDARD_SIP_PORT); bindaddr.sin_family = AF_INET; ast_mutex_lock(&netlock); - if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) { - close(sipsock); - sipsock = -1; + if (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in))) { + if (sipsock > -1) { + close(sipsock); + sipsock = -1; + } + if (siptcpsock > -1) { + close(siptcpsock); + siptcpsock = -1; + } } if (sipsock < 0) { sipsock = socket(AF_INET, SOCK_DGRAM, 0); if (sipsock < 0) { ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno)); - return -1; } else { /* Allow SIP clients on the same host to access us: */ const int reuseFlag = 1; @@ -16842,22 +16923,60 @@ ast_enable_packet_fragmentation(sipsock); if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { - ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n", + ast_log(LOG_WARNING, "Failed to bind to UDP %s:%d: %s\n", ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), strerror(errno)); close(sipsock); sipsock = -1; } else { if (option_verbose > 1) { - ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", + ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on UDP %s:%d\n", ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); - ast_verbose(VERBOSE_PREFIX_2 "Using SIP TOS: %s\n", ast_tos2str(global_tos_sip)); + ast_verbose(VERBOSE_PREFIX_2 "Using SIP UDP TOS: %s\n", ast_tos2str(global_tos_sip)); } if (setsockopt(sipsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) - ast_log(LOG_WARNING, "Unable to set SIP TOS to %s\n", ast_tos2str(global_tos_sip)); + ast_log(LOG_WARNING, "Unable to set SIP UDP TOS to %s\n", ast_tos2str(global_tos_sip)); } } } + 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); + + ast_enable_packet_fragmentation(sipsock); + + if (bind(siptcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + ast_log(LOG_WARNING, "Failed to bind to TCP %s:%d: %s\n", + ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), + strerror(errno)); + close(siptcpsock); + siptcpsock = -1; + } else { + 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 Listening on TCP %s:%d\n", + ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using SIP TCP TOS: %s\n", ast_tos2str(global_tos_sip)); + } + if (setsockopt(siptcpsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) + ast_log(LOG_WARNING, "Unable to set SIP TCP TOS to %s\n", ast_tos2str(global_tos_sip)); + } + } + } + } + if ((sipsock < 0) && (siptcpsock <0)) { + return -1; + } ast_mutex_unlock(&netlock); /* Add default domains - host name, IP address and IP:port */ @@ -17698,6 +17817,7 @@ clear_realm_authentication(authl); clear_sip_domains(); close(sipsock); + close(siptcpsock); sched_context_destroy(sched); return 0;