diff -urN asterisk-1.2.6/channels/chan_sip.c asterisk-1.2.6.tcp/channels/chan_sip.c --- asterisk-1.2.6/channels/chan_sip.c 2006-03-22 12:10:13.000000000 +0300 +++ asterisk-1.2.6.tcp/channels/chan_sip.c 2006-03-30 19:17:12.000000000 +0400 @@ -95,6 +95,17 @@ #define IPTOS_MINCOST 0x02 #endif +#define SIP_TCP_SUPPORT /* this will enable SIP over TCP/TLS */ + +#ifdef SIP_TCP_SUPPORT +#define OPENSSL_NO_KRB5 /* to prevent compile error */ +#include +#include +#include +#include +#include +#endif + /* #define VOCAL_DATA_HACK */ #define SIPDUMPER @@ -125,7 +136,6 @@ #define CALLERID_UNKNOWN "Unknown" - #define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ #define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ #define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ @@ -382,6 +392,27 @@ #define DEFAULT_MWITIME 10 static int global_mwitime = DEFAULT_MWITIME; /*!< Time between MWI checks for peers */ +#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); @@ -614,6 +645,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 */ @@ -782,6 +820,12 @@ struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ struct sockaddr_in addr; /*!< IP address of peer */ +#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 (qualify= checking) */ @@ -863,6 +907,12 @@ 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 struct sockaddr_in bindaddr = { 0, }; static struct sockaddr_in externip; @@ -901,6 +951,11 @@ 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); +static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime); +#endif + static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); static int sip_devicestate(void *data); static int sip_sendtext(struct ast_channel *ast, const char *text); @@ -1057,11 +1112,45 @@ 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); */ + if (p->ssl) { /* TLS write */ + struct sip_peer *peer = NULL; + res = 0; + 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, 1); + if (peer && peer->ssl) { + peer->ssl = NULL; + peer->tcpsockfd = -1; + } + break; + } + } + } else /* TCP write */ + 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:%d returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), res, strerror(errno)); } @@ -1077,9 +1166,17 @@ /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ if (ast_test_flag(p, SIP_NAT) & SIP_NAT_RFC3581) +#ifdef SIP_TCP_SUPPORT + snprintf(buf, len, "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(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x;rport", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif else /* Work around buggy UNIDEN UIP200 firmware */ +#ifdef SIP_TCP_SUPPORT + snprintf(buf, len, "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x", p->transport, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#else snprintf(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); +#endif } /*! \brief ast_sip_ouraddrfor: NAT fix - decide which IP address to use for ASterisk server? ---*/ @@ -1461,42 +1558,130 @@ parse_request(dst); } -/*! \brief send_response: Transmit response on SIP request---*/ -static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +#ifdef SIP_TCP_SUPPORT +static int cleanup_tcp_connection(int fd, SSL *ssl) { - int res; + int found = 0; + struct sip_pvt *p = NULL; + + if (fd == -1) { + ast_log(LOG_ERROR, "Tried to free up sockfd -1\n"); + return -1; + } + +// ASTOBJ_CONTAINER_RDLOCK(&peerl); + ASTOBJ_CONTAINER_TRAVERSE(&peerl, !found, { + if (iterator->tcpsockfd == fd) { + found = 1; + if (iterator->ssl) { + SSL_clear(iterator->ssl); + SSL_free(iterator->ssl); + } + else + close(iterator->tcpsockfd); + iterator->ssl = NULL; + iterator->tcpsockfd = -1; + ast_copy_string(iterator->transport, "UDP", sizeof(iterator->transport)); + } + } ); +// ASTOBJ_CONTAINER_UNLOCK(&peerl); + + /* close tcp fd and free ssl in sip_pvt */ + ast_mutex_lock(&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; + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); + 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]; - struct sip_request tmp; - char tmpmsg[80]; + BIO *bio = NULL; + SSL *ssl = NULL; + + if (!strncasecmp(p->transport, "UDP", 3)) { + return sipsock; + } - if (sip_debug_test_pvt(p)) { - if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) - ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data); - 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); + if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ast_log(LOG_WARNING, "TCP can't create socket : %s\n", strerror(errno)); + return -1; } - if (reliable) { - if (recordhistory) { - parse_copy(&tmp, req); - snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxRespRel", tmpmsg); - } - res = __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable > 1), req->method); - } else { - if (recordhistory) { - parse_copy(&tmp, req); - snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxResp", tmpmsg); + + /* 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; } - res = __sip_xmit(p, req->data, req->len); + ast_log(LOG_DEBUG, "New TLS connection is opened\n"); + p->ssl = ssl; } - if (res > 0) - return 0; - return res; + + return fd; } +#endif -/*! \brief send_request: Send SIP Request to the other part of the dialogue ---*/ -static int send_request(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +enum send_type { + SEND_TYPE_RESPONSE = 0, + SEND_TYPE_REQUEST +}; + +/*! \brief send_response_request: Transmit response on SIP request or Send SIP Request to the other part of the dialogue---*/ +static int send_response_request(enum send_type type, struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) { int res; char iabuf[INET_ADDRSTRLEN]; @@ -1505,28 +1690,73 @@ if (sip_debug_test_pvt(p)) { if (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE) +#ifdef SIP_TCP_SUPPORT + ast_verbose("%sTransmitting (NAT) to %s:%d:\n with fd %d\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), p->sockfd, req->data); +#else ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data); +#endif else +#ifdef SIP_TCP_SUPPORT + ast_verbose("%sTransmitting (no NAT) to %s:%d:\n with fd %d\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), p->sockfd, req->data); +#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); +#endif } + +#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 = 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")); - append_history(p, "TxReqRel", tmpmsg); + append_history(p, type == SEND_TYPE_RESPONSE ? "TxRespRel" : "TxReqRel", tmpmsg); } - res = __sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable > 1), req->method); + res = __sip_reliable_xmit(p, seqno, type == SEND_TYPE_RESPONSE ? 1 : 0, req->data, req->len, (reliable > 1), req->method); } else { if (recordhistory) { parse_copy(&tmp, req); snprintf(tmpmsg, sizeof(tmpmsg), "%s / %s", tmp.data, get_header(&tmp, "CSeq")); - append_history(p, "TxReq", tmpmsg); + append_history(p, type == SEND_TYPE_RESPONSE ? "TxResp" : "TxReq", tmpmsg); } res = __sip_xmit(p, req->data, req->len); } + + if ( type == SEND_TYPE_RESPONSE && res > 0) + return 0; return res; } +/*! \brief send_response: Transmit response on SIP request---*/ +static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +{ + return send_response_request( SEND_TYPE_RESPONSE, p, req, reliable, seqno ); +} + +/*! \brief send_request: Send SIP Request to the other part of the dialogue ---*/ +static int send_request(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno) +{ + return send_response_request( SEND_TYPE_REQUEST, p, req, reliable, seqno ); +} + /*! \brief get_in_brackets: Pick out text in brackets from character string ---*/ /* returns pointer to terminated stripped string. modifies input string. */ static char *get_in_brackets(char *tmp) @@ -1878,6 +2108,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; + r->ssl = peer->ssl; + 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); @@ -2137,6 +2372,13 @@ if (p->rpid_from) free(p->rpid_from); +/*#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) @@ -3065,6 +3307,12 @@ /* Start with 101 instead of 1 */ p->ocseq = 101; +#ifdef SIP_TCP_SUPPORT + p->ssl = NULL; + p->sockfd = -1; + ast_copy_string(p->transport, "UDP", sizeof(p->transport)); /* default transport protocol */ +#endif + if (sip_methods[intended_method].need_rtp) { p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (videosupport) @@ -4799,7 +5047,11 @@ snprintf(from, sizeof(from), "\"%s\" ;tag=%s", n, l, ast_strlen_zero(p->fromdomain) ? ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip) : p->fromdomain, p->tag); /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ +#ifdef SIP_TCP_SUPPORT + if (!ast_strlen_zero(p->fullcontact) && strchr(p->fullcontact, '@') != NULL) { +#else if (!ast_strlen_zero(p->fullcontact)) { +#endif /* If we have full contact, trust it */ ast_build_string(&invite, &invite_max, "%s", p->fullcontact); } else { @@ -5810,6 +6062,10 @@ struct ast_hostent ahp; struct sockaddr_in oldsin; +#ifdef SIP_TCP_SUPPORT + char *t, *q; +#endif + if (ast_strlen_zero(expires)) { /* No expires header */ expires = strcasestr(get_header(req, "Contact"), ";expires="); if (expires) { @@ -5862,6 +6118,13 @@ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", p->name); return PARSE_REGISTER_UPDATE; } +#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); @@ -5870,11 +6133,32 @@ 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 */ + t = NULL; + q = NULL; + while (n) { + n++; + if (!strncasecmp(n, "transport=", 10)) { + t = strchr(n, '='); + t ++; + } else if (!strncasecmp(n, "q=", 2)) { + q = strchr(n, '='); + q ++; + } +#endif /* Ditch q */ n = strchr(c, ';'); if (n) { *n = '\0'; } +#ifdef SIP_TCP_SUPPORT + } + + if (option_verbose > 2 && t) + ast_verbose(VERBOSE_PREFIX_3 "Contact header: transport %s\n", t); +#endif + /* Grab host */ n = strchr(c, '@'); if (!n) { @@ -5891,9 +6175,12 @@ port = atoi(pt); } else port = DEFAULT_SIP_PORT; + memcpy(&oldsin, &p->addr, sizeof(oldsin)); + if (!(ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)) { /* XXX This could block for a long time XXX */ + /* We should only do this if it's a name, not an IP */ hp = ast_gethostbyname(n, &ahp); if (!hp) { ast_log(LOG_WARNING, "Invalid host '%s'\n", n); @@ -5908,10 +6195,44 @@ 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", 3)) { + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } else if (!strncasecmp(t, "tls", 3)) { + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + 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) + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + else if (pvt->sockfd != sipsock) + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } +#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); @@ -6841,7 +7162,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") && 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; } @@ -7945,6 +8270,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, " Def. Username: %s\n", peer->username); ast_cli(fd, " SIP Options : "); if (peer->sipoptions) { @@ -11085,6 +11414,60 @@ return res; } +#ifdef SIP_TCP_SUPPORT +static int check_content_length(char *data, int readbytes, int max) +{ + int f = 0, blen = 0, msglen = 0; + char buf[SIP_MAX_PACKET]; + char *c = NULL, *header = NULL, *p; + + memcpy(buf, data, readbytes); + buf[readbytes] = 0; + c = buf; + + /* First header starts immediately */ + header = c; + while (*c) { + if (*c == '\n') { + /* We've got a new header */ + *c = 0; + + if (!strncmp(header, "Content-Length", strlen("Content-Length"))) { + p = strchr(header, ':'); + if (p) { + p ++; + blen = atoi(p); + } else { + ast_log(LOG_ERROR, "No colol in Content-Length header\n"); + return -1; + } + } + if (ast_strlen_zero(header)) { + /* Line by itself means we're now in content */ + c++; + break; + } + if (f >= SIP_MAX_HEADERS - 1) { + ast_log(LOG_WARNING, "Too many SIP headers...\n"); + } else + f++; + header = c + 1; + } else if (*c == '\r') { + /* Ignore but eliminate \r's */ + *c = 0; + } + c++; + } + msglen = (int)(c - buf) + blen; + + /* ast_verbose("blen %d, msglen %d, readbytes %d\n", blen, msglen, readbytes); */ + if ((readbytes >= msglen) || (readbytes >= max)) + return msglen; + else + return 0; +} +#endif + /*! \brief 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) @@ -11097,9 +11480,69 @@ int nounlock; int recount = 0; char iabuf[INET_ADDRSTRLEN]; +#ifdef SIP_TCP_SUPPORT + 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 !defined(__FreeBSD__) @@ -11121,7 +11564,11 @@ if (pedanticsipchecking) req.len = lws2sws(req.data, req.len); /* Fix multiline headers */ if (ast_test_flag(&req, SIP_PKT_DEBUG)) { +#ifdef SIP_TCP_SUPPORT + ast_verbose("\n<-- Sip read from %s:%d:%s \n%s\n", 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_request(&req); req.method = find_sip_method(req.rlPart1); @@ -11159,6 +11606,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 */ + ast_copy_string(p->transport, "TLS", sizeof(p->transport)); + } else + ast_copy_string(p->transport, "TCP", sizeof(p->transport)); + } +#endif nounlock = 0; if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) { /* Request failed */ @@ -11216,6 +11673,73 @@ 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( sip_debug_test_addr(&cliaddr) ) + 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( sip_debug_test_addr(&cliaddr) ) + 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 + /*! \brief do_monitor: The SIP monitoring thread ---*/ static void *do_monitor(void *data) { @@ -11231,6 +11755,13 @@ /* 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 */ @@ -11438,6 +11969,13 @@ 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; + ast_copy_string(p->transport, peer->transport, sizeof(p->transport)); + /* 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)); @@ -12093,6 +12631,11 @@ oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; +#ifdef SIP_TCP_SUPPORT + peer->tcpsockfd = -1; + peer->ssl = NULL; + ast_copy_string(peer->transport, "UDP", sizeof(peer->transport)); +#endif ast_copy_flags(peer, &global_flags, SIP_FLAGS_TO_COPY); peer->capability = global_capability; peer->rtptimeout = global_rtptimeout; @@ -12299,6 +12842,9 @@ struct ast_flags dummy; int auto_sip_domains = 0; struct sockaddr_in old_bindaddr = bindaddr; +#ifdef SIP_TCP_SUPPORT + struct sockaddr_in old_tlsbindaddr = tlsbindaddr; +#endif cfg = ast_config_load(config); @@ -12550,6 +13096,27 @@ } else if (!strcasecmp(v->name, "callevents")) { callevents = ast_true(v->value); } +#ifdef SIP_TCP_SUPPORT + else if (!strcasecmp(v->name, "tlsport")) { + if (sscanf(v->value, "%d", &ourport) == 1) { + tlsbindaddr.sin_port = htons(ourport); + } else { + ast_log(LOG_WARNING, "Invalid tlsport number '%s' at line %d of %s\n", v->value, v->lineno, config); + } + } else if (!strcasecmp(v->name, "trustcerts")) { + ast_copy_string(trustcerts_file, v->value, sizeof(trustcerts_file)); + } else if (!strcasecmp(v->name, "servercert")) { + ast_copy_string(servercert_file, v->value, sizeof(servercert_file)); + } else if (!strcasecmp(v->name, "serverkey")) { + ast_copy_string(serverkey_file, v->value, sizeof(serverkey_file)); + } else if (!strcasecmp(v->name, "serverkeypassword")) { + ast_copy_string(serverkey_password, v->value, sizeof(serverkey_password)); + } else if (!strcasecmp(v->name, "dh512param")) { + ast_copy_string(dh512param_file, v->value, sizeof(dh512param_file)); + } else if (!strcasecmp(v->name, "dh1024param")) { + ast_copy_string(dh1024param_file, v->value, sizeof(dh1024param_file)); + } +#endif /* else if (strcasecmp(v->name,"type")) * ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ @@ -12638,6 +13205,85 @@ } } } +#ifdef SIP_TCP_SUPPORT + if ((siptcpsock > -1) && (memcmp(&old_tlsbindaddr, &bindaddr, sizeof(struct sockaddr_in)))) { + close(siptcpsock); + siptcpsock = -1; + } + /* Open a TCP listening socket */ + if (siptcpsock < 0) { + siptcpsock = socket(AF_INET, SOCK_STREAM, 0); + if (siptcpsock < 0) { + ast_log(LOG_WARNING, "Unable to create SIP TCP socket: %s\n", strerror(errno)); + } else { + /* Allow SIP clients on the same host to access us: */ + const int reuseFlag = 1; + setsockopt(siptcpsock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&reuseFlag, + sizeof reuseFlag); + + if (bind(siptcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + ast_log(LOG_WARNING, "Failed to bind SIP TCP socket to %s:%d: %s\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port), + strerror(errno)); + close(siptcpsock); + siptcpsock = -1; + } else { + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_2 "SIP TCP Listening on %s:%d\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); + } + 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"); + } + } + } + } + + /* Open a TLS listening socket */ + memcpy(&tlsbindaddr.sin_addr, &bindaddr.sin_addr, sizeof(tlsbindaddr.sin_addr)); + if (!ntohs(tlsbindaddr.sin_port)) + tlsbindaddr.sin_port = ntohs(DEFAULT_SIP_TLS_PORT); + tlsbindaddr.sin_family = AF_INET; + if ((siptlssock > -1) && (memcmp(&old_tlsbindaddr, &tlsbindaddr, sizeof(struct sockaddr_in)))) { + close(siptlssock); + siptlssock = -1; + } + if (siptlssock < 0) { + siptlssock = socket(AF_INET, SOCK_STREAM, 0); + if (siptlssock < 0) { + ast_log(LOG_WARNING, "Unable to create SIP TLS socket: %s\n", strerror(errno)); + } else { + /* Allow SIP clients on the same host to access us: */ + const int reuseFlag = 1; + setsockopt(siptlssock, SOL_SOCKET, SO_REUSEADDR, ( + const char *)&reuseFlag, + sizeof reuseFlag); + + if (bind(siptlssock, (struct sockaddr *)&tlsbindaddr, sizeof(tlsbindaddr)) < 0) { + ast_log(LOG_WARNING, "Failed to bind SIP TLS socket to %s:%d: %s\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), tlsbindaddr.sin_addr), ntohs(tlsbindaddr.sin_port), + strerror(errno)); + close(siptlssock); + siptlssock = -1; + } else { + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_2 "SIP TLS Listening on %s:%d\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), tlsbindaddr.sin_addr), ntohs(tlsbindaddr.sin_port)); + ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); + } + 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"); + } + } + } + } +#endif ast_mutex_unlock(&netlock); /* Add default domains - host name, IP address and IP:port */ @@ -13106,6 +13752,108 @@ { { "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 + /*! \brief load_module: PBX load module - initialization ---*/ int load_module() { @@ -13125,6 +13873,12 @@ reload_config(); /* Load the configuration from sip.conf */ +#ifdef SIP_TCP_SUPPORT + if( tlsctx != NULL ) + SSL_CTX_free(tlsctx); + tlsctx = init_OpenSSL(); +#endif + /* Make sure we can register our sip channel type */ if (ast_channel_register(&sip_tech)) { ast_log(LOG_ERROR, "Unable to register channel type %s\n", channeltype); @@ -13239,6 +13993,15 @@ /* Free memory for local network address mask */ ast_free_ha(localaddr); +#ifdef SIP_TCP_SUPPORT + ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, { + if (iterator->ssl) { + SSL_clear(iterator->ssl); + SSL_free(iterator->ssl); + } + } ); +#endif + ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); ASTOBJ_CONTAINER_DESTROY(&userl); ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer); @@ -13250,7 +14013,11 @@ clear_sip_domains(); close(sipsock); sched_context_destroy(sched); - +#ifdef SIP_TCP_SUPPORT + close(siptcpsock); + close(siptlssock); + SSL_CTX_free(tlsctx); /* destroy TLS CTX */ +#endif return 0; } diff -urN asterisk-1.2.6/configs/sip.conf.sample asterisk-1.2.6.tcp/configs/sip.conf.sample --- asterisk-1.2.6/configs/sip.conf.sample 2006-03-16 20:58:54.000000000 +0300 +++ asterisk-1.2.6.tcp/configs/sip.conf.sample 2006-03-30 19:11:07.000000000 +0400 @@ -108,6 +108,15 @@ ; Settable per peer/user also ;notifyringing = yes ; Notify subscriptions on RINGING state +; 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