[asterisk-commits] file: trunk r370072 - in /trunk: ./ channels/ channels/sip/ channels/sip/incl...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jul 16 07:35:12 CDT 2012


Author: file
Date: Mon Jul 16 07:35:04 2012
New Revision: 370072

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=370072
Log:
Add support for SIP over WebSocket.

This allows SIP traffic to be exchanged over a WebSocket connection which is useful for rtcweb.

Review: https://reviewboard.asterisk.org/r/2008

Modified:
    trunk/CHANGES
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/channels/sip/sdp_crypto.c
    trunk/channels/sip/security_events.c
    trunk/configs/sip.conf.sample
    trunk/include/asterisk/http_websocket.h
    trunk/res/res_http_websocket.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Jul 16 07:35:04 2012
@@ -105,6 +105,8 @@
  * Add support for lightweight NAT keepalive. If enabled a blank packet will
    be sent to the remote host at a given interval to keep the NAT mapping open.
    This can be enabled using the keepalive configuration option.
+ * Add support for WebSocket transport. This can be configured using 'ws' or 'wss'
+   as the transport.
 
 Chan_local changes
 ------------------

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Mon Jul 16 07:35:04 2012
@@ -1167,6 +1167,12 @@
 /*! \brief A per-thread temporary pvt structure */
 AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
 
+/*! \brief A per-thread buffer for transport to string conversion */
+AST_THREADSTORAGE(sip_transport_str_buf);
+
+/*! \brief Size of the SIP transport buffer */
+#define SIP_TRANSPORT_STR_BUFSIZE 128
+
 /*! \brief Authentication container for realm authentication */
 static struct sip_auth_container *authl = NULL;
 /*! \brief Global authentication container protection while adjusting the references. */
@@ -2525,6 +2531,54 @@
 	return _sip_tcp_helper_thread(tcptls_session);
 }
 
+/*! \brief SIP WebSocket connection handler */
+static void sip_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
+{
+	int res;
+
+	if (ast_websocket_set_nonblock(session)) {
+		goto end;
+	}
+
+	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
+		char *payload;
+		uint64_t payload_len;
+		enum ast_websocket_opcode opcode;
+		int fragmented;
+
+		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
+			/* We err on the side of caution and terminate the session if any error occurs */
+			break;
+		}
+
+		if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
+			struct sip_request req = { 0, };
+
+			if (!(req.data = ast_str_create(payload_len))) {
+				goto end;
+			}
+
+			if (ast_str_set(&req.data, -1, "%s", payload) == AST_DYNSTR_BUILD_FAILED) {
+				deinit_req(&req);
+				goto end;
+			}
+
+			req.socket.fd = ast_websocket_fd(session);
+			set_socket_transport(&req.socket, ast_websocket_is_secure(session) ? SIP_TRANSPORT_WSS : SIP_TRANSPORT_WS);
+			req.socket.ws_session = session;
+
+			handle_request_do(&req, ast_websocket_remote_address(session));
+			deinit_req(&req);
+
+		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
+			break;
+		}
+	}
+
+end:
+	ast_websocket_unref(session);
+}
+
 /*! \brief Check if the authtimeout has expired.
  * \param start the time when the session started
  *
@@ -2800,6 +2854,7 @@
 					we receive is not the same - we should generate an error */
 
 			req.socket.tcptls_session = tcptls_session;
+			req.socket.ws_session = NULL;
 			handle_request_do(&req, &tcptls_session->remote_address);
 		}
 
@@ -3306,29 +3361,53 @@
 	if (!strcasecmp(transport, "tls")) {
 		res |= SIP_TRANSPORT_TLS;
 	}
+	if (!strcasecmp(transport, "ws")) {
+		res |= SIP_TRANSPORT_WS;
+	}
+	if (!strcasecmp(transport, "wss")) {
+		res |= SIP_TRANSPORT_WSS;
+	}
 
 	return res;
 }
 
 /*! \brief Return configuration of transports for a device */
-static inline const char *get_transport_list(unsigned int transports) {
-	switch (transports) {
-		case SIP_TRANSPORT_UDP:
-			return "UDP";
-		case SIP_TRANSPORT_TCP:
-			return "TCP";
-		case SIP_TRANSPORT_TLS:
-			return "TLS";
-		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TCP:
-			return "TCP,UDP";
-		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TLS:
-			return "TLS,UDP";
-		case SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS:
-			return "TLS,TCP";
-		default:
-			return transports ?
-				"TLS,TCP,UDP" : "UNKNOWN";	
-	}
+static inline const char *get_transport_list(unsigned int transports)
+{
+	char *buf;
+
+	if (!transports) {
+		return "UNKNOWN";
+	}
+
+	if (!(buf = ast_threadstorage_get(&sip_transport_str_buf, SIP_TRANSPORT_STR_BUFSIZE))) {
+		return "";
+	}
+
+	memset(buf, 0, SIP_TRANSPORT_STR_BUFSIZE);
+
+	if (transports & SIP_TRANSPORT_UDP) {
+		strncat(buf, "UDP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
+	}
+	if (transports & SIP_TRANSPORT_TCP) {
+		strncat(buf, "TCP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
+	}
+	if (transports & SIP_TRANSPORT_TLS) {
+		strncat(buf, "TLS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
+	}
+	if (transports & SIP_TRANSPORT_WS) {
+		strncat(buf, "WS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
+	}
+	if (transports & SIP_TRANSPORT_WSS) {
+		strncat(buf, "WSS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
+	}
+
+	/* Remove the trailing ',' if present */
+	if (strlen(buf)) {
+		buf[strlen(buf) - 1] = 0;
+	}
+
+	return buf;
 }
 
 /*! \brief Return transport as string */
@@ -3341,6 +3420,9 @@
 		return "TCP";
 	case SIP_TRANSPORT_TLS:
 		return "TLS";
+	case SIP_TRANSPORT_WS:
+	case SIP_TRANSPORT_WSS:
+		return "WS";
 	}
 
 	return "UNKNOWN";
@@ -3352,9 +3434,13 @@
 	switch (t) {
 	case SIP_TRANSPORT_UDP:
 		return "udp";
+	case SIP_TRANSPORT_WS:
+		return "ws";
 	case SIP_TRANSPORT_TLS:
 	case SIP_TRANSPORT_TCP:
 		return "tcp";
+	case SIP_TRANSPORT_WSS:
+		return "wss";
 	}
 
 	return "udp";
@@ -3366,8 +3452,10 @@
 	switch (t) {
 	case SIP_TRANSPORT_TCP:
 	case SIP_TRANSPORT_UDP:
+	case SIP_TRANSPORT_WS:
 		return "sip";
 	case SIP_TRANSPORT_TLS:
+	case SIP_TRANSPORT_WSS:
 		return "sips";
 	}
 	return "sip";
@@ -3414,6 +3502,11 @@
 		res = ast_sendto(p->socket.fd, data->str, ast_str_strlen(data), 0, dst);
 	} else if (p->socket.tcptls_session) {
 		res = sip_tcptls_write(p->socket.tcptls_session, data->str, ast_str_strlen(data));
+	} else if (p->socket.ws_session) {
+		if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, data->str, ast_str_strlen(data)))) {
+			/* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */
+			res = ast_str_strlen(data);
+		}
 	} else {
 		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
 		return XMIT_ERROR;
@@ -4730,6 +4823,9 @@
 	if (peer->socket.tcptls_session) {
 		ao2_ref(peer->socket.tcptls_session, -1);
 		peer->socket.tcptls_session = NULL;
+	} else if (peer->socket.ws_session) {
+		ast_websocket_unref(peer->socket.ws_session);
+		peer->socket.ws_session = NULL;
 	}
 
 	ast_cc_config_params_destroy(peer->cc_params);
@@ -5298,10 +5394,15 @@
 	if (to_sock->tcptls_session) {
 		ao2_ref(to_sock->tcptls_session, -1);
 		to_sock->tcptls_session = NULL;
+	} else if (to_sock->ws_session) {
+		ast_websocket_unref(to_sock->ws_session);
+		to_sock->ws_session = NULL;
 	}
 
 	if (from_sock->tcptls_session) {
 		ao2_ref(from_sock->tcptls_session, +1);
+	} else if (from_sock->ws_session) {
+		ast_websocket_ref(from_sock->ws_session);
 	}
 
 	*to_sock = *from_sock;
@@ -6012,6 +6113,9 @@
 	if (p->socket.tcptls_session) {
 		ao2_ref(p->socket.tcptls_session, -1);
 		p->socket.tcptls_session = NULL;
+	} else if (p->socket.ws_session) {
+		ast_websocket_unref(p->socket.ws_session);
+		p->socket.ws_session = NULL;
 	}
 
 	if (p->peerauth) {
@@ -9334,7 +9438,7 @@
 		int image = FALSE;
 		int text = FALSE;
 		int processed_crypto = FALSE;
-		char protocol[5] = {0,};
+		char protocol[6] = {0,};
 		int x;
 
 		numberofports = 0;
@@ -9354,8 +9458,8 @@
 
 		/* Check for 'audio' media offer */
 		if (strncmp(m, "audio ", 6) == 0) {
-			if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-			    (sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
+			if ((sscanf(m, "audio %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+			    (sscanf(m, "audio %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
 				 * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
@@ -9377,9 +9481,21 @@
 					ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
-				if (!strcmp(protocol, "SAVP")) {
+				if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received SAVPF profle in audio offer but AVPF is not enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received SAVP profile in audio offer but AVPF is enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
 					secure_audio = 1;
-				} else if (strcmp(protocol, "AVP")) {
+				} else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVPF profile in audio offer but AVPF is not enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVP profile in audio offer but AVPF is enabled: %s\n", m);
+					continue;
+				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
 					continue;
 				}
@@ -9414,8 +9530,8 @@
 		}
 		/* Check for 'video' media offer */
 		else if (strncmp(m, "video ", 6) == 0) {
-			if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-			    (sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
+			if ((sscanf(m, "video %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+			    (sscanf(m, "video %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
 				 * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
@@ -9437,9 +9553,21 @@
 					ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
-				if (!strcmp(protocol, "SAVP")) {
+				if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received SAVPF profle in video offer but AVPF is not enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received SAVP profile in video offer but AVPF is enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
 					secure_video = 1;
-				} else if (strcmp(protocol, "AVP")) {
+				} else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVP profile in video offer but AVPF is enabled: %s\n", m);
+					continue;
+				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
 					continue;
 				}
@@ -9474,18 +9602,18 @@
 		}
 		/* Check for 'text' media offer */
 		else if (strncmp(m, "text ", 5) == 0) {
-			if ((sscanf(m, "text %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
-			    (sscanf(m, "text %30u RTP/AVP %n", &x, &len) == 1 && len > 0)) {
+			if ((sscanf(m, "text %30u/%30u RTP/%s %n", &x, &numberofports, protocol, &len) == 2 && len > 0) ||
+			    (sscanf(m, "text %30u RTP/%s %n", &x, protocol, &len) == 1 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
-				 * length is "m=text 0 RTP/AVP " + codecs + "\0" */
-				if (!(offer->decline_m_line = ast_malloc(17 + strlen(codecs) + 1))) {
+				 * length is "m=text 0 RTP/" + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(13 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
 					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
 				/* guaranteed to be exactly the right length */
-				sprintf(offer->decline_m_line, "m=text 0 RTP/AVP %s", codecs);
+				sprintf(offer->decline_m_line, "m=text 0 RTP/%s %s", protocol, codecs);
 
 				if (x == 0) {
 					ast_log(LOG_WARNING, "Ignoring text stream offer because port number is zero\n");
@@ -9495,6 +9623,17 @@
 				/* Check number of ports offered for stream */
 				if (numberofports > 1) {
 					ast_log(LOG_WARNING, "%d ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
+				}
+
+				if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVPF profile in text offer but AVPF is not enabled: %s\n", m);
+					continue;
+				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+					ast_log(LOG_WARNING, "Received AVP profile in text offer but AVPF is enabled: %s\n", m);
+					continue;
+				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+					ast_log(LOG_WARNING, "Unknown RTP profile in text offer: %s\n", m);
+					continue;
 				}
 
 				if (has_media_stream(p, SDP_TEXT)) {
@@ -10692,13 +10831,23 @@
  */
 static void set_destination(struct sip_pvt *p, char *uri)
 {
-	char *h, *maddr, hostname[256];
+	char *trans, *h, *maddr, hostname[256];
 	int hn;
 	int debug=sip_debug_test_pvt(p);
 	int tls_on = FALSE;
 
 	if (debug)
 		ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
+
+	if ((trans = strcasestr(uri, ";transport="))) {
+		trans += strlen(";transport=");
+
+		if (!strncasecmp(trans, "ws", 2)) {
+			if (debug)
+				ast_verbose("set_destination: URI is for WebSocket, we can't set destination\n");
+			return;
+		}
+	}
 
 	/* Find and parse hostname */
 	h = strchr(uri, '@');
@@ -12026,6 +12175,15 @@
 	}
 }
 
+static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure)
+{
+	if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+		return secure ? "SAVPF" : "AVPF";
+	} else {
+		return secure ? "SAVP" : "AVP";
+	}
+}
+
 /*! \brief Add Session Description Protocol message
 
     If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
@@ -12186,7 +12344,7 @@
 		if (needvideo) {
 			get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
 			ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
-				v_a_crypto ? "SAVP" : "AVP");
+				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
 
 			/* Build max bitrate string */
 			if (p->maxcallbitrate)
@@ -12207,7 +12365,7 @@
 				ast_verbose("Lets set up the text sdp\n");
 			get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
 			ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
-				t_a_crypto ? "SAVP" : "AVP");
+				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
 			if (debug) {  /* XXX should I use tdest below ? */
 				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
 			}
@@ -12224,7 +12382,7 @@
 
 		get_crypto_attrib(p, p->srtp, &a_crypto);
 		ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
-			a_crypto ? "SAVP" : "AVP");
+			       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
 
 		/* Now, start adding audio codecs. These are added in this order:
 		   - First what was requested by the calling channel
@@ -14639,6 +14797,9 @@
 		if (socket->tcptls_session) {
 			ao2_ref(socket->tcptls_session, -1);
 			socket->tcptls_session = NULL;
+		} else if (socket->ws_session) {
+			ast_websocket_unref(socket->ws_session);
+			socket->ws_session = NULL;
 		}
 	}
 }
@@ -14661,6 +14822,9 @@
 	if (peer->socket.tcptls_session) {
 		ao2_ref(peer->socket.tcptls_session, -1);
 		peer->socket.tcptls_session = NULL;
+	} else if (peer->socket.ws_session) {
+		ast_websocket_unref(peer->socket.ws_session);
+		peer->socket.ws_session = NULL;
 	}
 
 	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
@@ -16840,6 +17004,11 @@
 	uint16_t port;
 
 	ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
+
+	/* If this is via WebSocket we don't use the Via header contents at all */
+	if (!strncasecmp(via, "SIP/2.0/WS", 10)) {
+		return;
+	}
 
 	/* Work on the leftmost value of the topmost Via header */
 	c = strchr(via, ',');
@@ -20984,6 +21153,9 @@
 	if (p->socket.tcptls_session) {
 		ao2_ref(p->socket.tcptls_session, -1);
 		p->socket.tcptls_session = NULL;
+	} else if (p->socket.ws_session) {
+		ast_websocket_unref(p->socket.ws_session);
+		p->socket.ws_session = NULL;
 	}
 
 	set_socket_transport(&p->socket, transport);
@@ -27196,6 +27368,9 @@
 			(s->tcptls_session->fd != -1)) {
 		return s->tcptls_session->fd;
 	}
+	if ((s->type & (SIP_TRANSPORT_WS | SIP_TRANSPORT_WSS))) {
+		return s->ws_session ? ast_websocket_fd(s->ws_session) : -1;
+	}
 
 	/*! \todo Check this... This might be wrong, depending on the proxy configuration
 		If proxy is in "force" mode its correct.
@@ -29188,6 +29363,10 @@
 
 					if (!strncasecmp(trans, "udp", 3)) {
 						peer->transports |= SIP_TRANSPORT_UDP;
+					} else if (!strncasecmp(trans, "wss", 3)) {
+						peer->transports |= SIP_TRANSPORT_WSS;
+					} else if (!strncasecmp(trans, "ws", 2)) {
+						peer->transports |= SIP_TRANSPORT_WS;
 					} else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
 						peer->transports |= SIP_TRANSPORT_TCP;
 					} else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
@@ -29538,6 +29717,8 @@
 				ast_set2_flag(&peer->flags[2], !strcasecmp(v->value, "32"), SIP_PAGE3_SRTP_TAG_32);
 			} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
 				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
+			} else if (!strcasecmp(v->name, "avpf")) {
+				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
 			}
 		}
 
@@ -29651,7 +29832,6 @@
 	 * 3. The socket.type is not set yet. */
 	if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) ||
 		!(peer->socket.type & peer->transports) || !(peer->socket.type)) {
-
 		set_socket_transport(&peer->socket, peer->default_outbound_transport);
 	}
 
@@ -30189,6 +30369,10 @@
 					default_transports |= SIP_TRANSPORT_TCP;
 				} else if (!strncasecmp(trans, "tls", 3)) {
 					default_transports |= SIP_TRANSPORT_TLS;
+				} else if (!strncasecmp(trans, "wss", 3)) {
+					default_transports |= SIP_TRANSPORT_WSS;
+				} else if (!strncasecmp(trans, "ws", 2)) {
+					default_transports |= SIP_TRANSPORT_WS;
 				} else {
 					ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
 				}
@@ -32598,6 +32782,8 @@
 	sip_register_tests();
 	network_change_event_subscribe();
 
+	ast_websocket_add_protocol("sip", sip_websocket_callback);
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -32609,6 +32795,8 @@
 	struct ast_context *con;
 	struct ao2_iterator i;
 	int wait_count;
+
+	ast_websocket_remove_protocol("sip", sip_websocket_callback);
 
 	network_change_event_unsubscribe();
 	acl_change_event_unsubscribe();

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Mon Jul 16 07:35:04 2012
@@ -35,6 +35,7 @@
 #include "asterisk/indications.h"
 #include "asterisk/security_events.h"
 #include "asterisk/features.h"
+#include "asterisk/http_websocket.h"
 
 #ifndef FALSE
 #define FALSE    0
@@ -369,10 +370,11 @@
 #define SIP_PAGE3_NAT_AUTO_RPORT         (1 << 2)  /*!< DGP: Set SIP_NAT_FORCE_RPORT when NAT is detected */
 #define SIP_PAGE3_NAT_AUTO_COMEDIA       (1 << 3)  /*!< DGP: Set SIP_PAGE2_SYMMETRICRTP when NAT is detected */
 #define SIP_PAGE3_DIRECT_MEDIA_OUTGOING  (1 << 4)  /*!< DP: Only send direct media reinvites on outgoing calls */
+#define SIP_PAGE3_USE_AVPF               (1 << 5)  /*!< DGP: Support a minimal AVPF-compatible profile */
 
 #define SIP_PAGE3_FLAGS_TO_COPY \
 	(SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32 | SIP_PAGE3_NAT_AUTO_RPORT | SIP_PAGE3_NAT_AUTO_COMEDIA | \
-	 SIP_PAGE3_DIRECT_MEDIA_OUTGOING)
+	 SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_USE_AVPF)
 
 #define CHECK_AUTH_BUF_INITLEN   256
 
@@ -564,6 +566,8 @@
 	SIP_TRANSPORT_UDP = 1,         /*!< Unreliable transport for SIP, needs retransmissions */
 	SIP_TRANSPORT_TCP = 1 << 1,    /*!< Reliable, but unsecure */
 	SIP_TRANSPORT_TLS = 1 << 2,    /*!< TCP/TLS - reliable and secure transport for signalling */
+        SIP_TRANSPORT_WS  = 1 << 3,    /*!< WebSocket, unsecure */
+        SIP_TRANSPORT_WSS = 1 << 4,    /*!< WebSocket, secure */
 };
 
 /*! \brief Automatic peer registration behavior
@@ -769,6 +773,7 @@
 	int fd;                   /*!< Filed descriptor, the actual socket */
 	uint16_t port;
 	struct ast_tcptls_session_instance *tcptls_session;  /* If tcp or tls, a socket manager */
+	struct ast_websocket *ws_session; /*! If ws or wss, a WebSocket session */
 };
 
 /*! \brief sip_request: The data grabbed from the UDP socket
@@ -1284,7 +1289,7 @@
 	enum sip_transport default_outbound_transport;   /*!< Peer Registration may change the default outbound transport.
 	                                                     If register expires, default should be reset. to this value */
 	/* things that don't belong in flags */
-	unsigned short transports:3;    /*!< Transports (enum sip_transport) that are acceptable for this peer */
+	unsigned short transports:5;    /*!< Transports (enum sip_transport) that are acceptable for this peer */
 	unsigned short is_realtime:1;   /*!< this is a 'realtime' peer */
 	unsigned short rt_fromcontact:1;/*!< copy fromcontact from realtime */
 	unsigned short host_dynamic:1;  /*!< Dynamic Peers register with Asterisk */

Modified: trunk/channels/sip/sdp_crypto.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/sdp_crypto.c?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/channels/sip/sdp_crypto.c (original)
+++ trunk/channels/sip/sdp_crypto.c Mon Jul 16 07:35:04 2012
@@ -218,7 +218,7 @@
 		return -1;
 	}
 
-	if (session_params) {
+	if (!ast_strlen_zero(session_params)) {
 		ast_log(LOG_WARNING, "Unsupported crypto parameters: %s", session_params);
 		return -1;
 	}

Modified: trunk/channels/sip/security_events.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/security_events.c?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/channels/sip/security_events.c (original)
+++ trunk/channels/sip/security_events.c Mon Jul 16 07:35:04 2012
@@ -45,8 +45,10 @@
 	case SIP_TRANSPORT_UDP:
 		return AST_SECURITY_EVENT_TRANSPORT_UDP;
 	case SIP_TRANSPORT_TCP:
+	case SIP_TRANSPORT_WS:
 		return AST_SECURITY_EVENT_TRANSPORT_TCP;
 	case SIP_TRANSPORT_TLS:
+	case SIP_TRANSPORT_WSS:
 		return AST_SECURITY_EVENT_TRANSPORT_TLS;
 	}
 

Modified: trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/sip.conf.sample?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Mon Jul 16 07:35:04 2012
@@ -745,7 +745,7 @@
 ;
 ;register => tls://username:xxxxxx@sip-tls-proxy.example.org
 ;
-;    The 'transport' part defaults to 'udp' but may also be 'tcp' or 'tls'.
+;    The 'transport' part defaults to 'udp' but may also be 'tcp', 'tls', 'ws', or 'wss'.
 ;    Using 'udp://' explicitly is also useful in case the username part
 ;    contains a '/' ('user/name').
 
@@ -977,7 +977,10 @@
                                 ; on outgoing calls to a peer. Calls will fail with HANGUPCAUSE=58 if
                                 ; the peer does not support SRTP. Defaults to no.
 ;encryption_taglen=80           ; Set the auth tag length offered in the INVITE either 32/80 default 80
-
+;
+;avpf=yes                       ; Enable inter-operability with media streams using the AVPF RTP profile.
+				; This will cause all offers and answers to use AVPF (or SAVPF). This
+				; option may be specified at the global or peer scope.
 ;----------------------------------------- REALTIME SUPPORT ------------------------
 ; For additional information on ARA, the Asterisk Realtime Architecture,
 ; please read https://wiki.asterisk.org/wiki/display/AST/Realtime+Database+Configuration

Modified: trunk/include/asterisk/http_websocket.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/http_websocket.h?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/include/asterisk/http_websocket.h (original)
+++ trunk/include/asterisk/http_websocket.h Mon Jul 16 07:35:04 2012
@@ -176,105 +176,12 @@
  */
 int ast_websocket_is_secure(struct ast_websocket *session);
 
-#endif /* _ASTERISK_HTTP_WEBSOCKET_H */
-/*
- * Asterisk -- An open source telephony toolkit.
+/*!
+ * \brief Set the socket of a WebSocket session to be non-blocking.
  *
- * Copyright (C) 2012, Digium, Inc.
- *
- * Joshua Colp <jcolp at digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
+ * \retval 0 on success
+ * \retval -1 on failure
  */
+int ast_websocket_set_nonblock(struct ast_websocket *session);
 
-#ifndef _ASTERISK_HTTP_WEBSOCKET_H
-#define _ASTERISK_HTTP_WEBSOCKET_H
-
-#include "asterisk/module.h"
-
-/*!
- * \file http_websocket.h
- * \brief Support for WebSocket connections within the Asterisk HTTP server.
- *
- * \author Joshua Colp <jcolp at digium.com>
- *
- */
-
-/*! \brief WebSocket operation codes */
-enum ast_websocket_opcode {
-	AST_WEBSOCKET_OPCODE_TEXT = 0x1,         /*!< Text frame */
-	AST_WEBSOCKET_OPCODE_BINARY = 0x2,       /*!< Binary frame */
-	AST_WEBSOCKET_OPCODE_PING = 0x9,         /*!< Request that the other side respond with a pong */
-	AST_WEBSOCKET_OPCODE_PONG = 0xA,         /*!< Response to a ping */
-	AST_WEBSOCKET_OPCODE_CLOSE = 0x8,        /*!< Connection is being closed */
-	AST_WEBSOCKET_OPCODE_CONTINUATION = 0x0, /*!< Continuation of a previous frame */
-};
-
-/*!
- * \brief Callback for when a new connection for a sub-protocol is established
- *
- * \param f Pointer to the file instance for the session
- * \param fd File descriptor for the session
- * \param remote_address The address of the remote party
- *
- * \note Once called the ownership of the session is transferred to the sub-protocol handler. It
- *       is responsible for closing and cleaning up.
- *
- */
-typedef void (*ast_websocket_callback)(FILE *f, int fd, struct ast_sockaddr *remote_address);
-
-/*!
- * \brief Add a sub-protocol handler to the server
- *
- * \param name Name of the sub-protocol to register
- * \param callback Callback called when a new connection requesting the sub-protocol is established
- *
- * \retval 0 success
- * \retval -1 if sub-protocol handler could not be registered
- */
-int ast_websocket_add_protocol(char *name, ast_websocket_callback callback);
-
-/*!
- * \brief Remove a sub-protocol handler from the server
- *
- * \param name Name of the sub-protocol to unregister
- * \param callback Callback that was previously registered with the sub-protocol
- *
- * \retval 0 success
- * \retval -1 if sub-protocol was not found or if callback did not match
- */
-int ast_websocket_remove_protocol(char *name, ast_websocket_callback callback);
-
-/*!
- * \brief Read a WebSocket frame and handle it
- *
- * \param f Pointer to the file stream, used to respond to certain frames
- * \param buf Pointer to the buffer containing the frame
- * \param buflen Size of the buffer
- * \param payload_len Pointer to a uint64_t which will be populated with the length of the payload if present
- * \param opcode Pointer to an int which will be populated with the opcode of the frame
- *
- * \retval NULL if no payload is present
- * \retval non-NULL if payload is present, returned pointer points to beginning of payload
- */
-char *ast_websocket_read(FILE *f, char *buf, size_t buflen, uint64_t *payload_len, int *opcode);
-
-/*!
- * \brief Construct and transmit a WebSocket frame
- *
- * \param f Pointer to the file stream which the frame will be sent on
- * \param opcode WebSocket operation code to place in the frame
- * \param payload Optional pointer to a payload to add to the frame
- * \param actual_length Length of the payload (0 if no payload)
- */
-void ast_websocket_write(FILE *f, int op_code, char *payload, uint64_t actual_length);
-
-#endif /* _ASTERISK_HTTP_WEBSOCKET_H */
+#endif

Modified: trunk/res/res_http_websocket.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_http_websocket.c?view=diff&rev=370072&r1=370071&r2=370072
==============================================================================
--- trunk/res/res_http_websocket.c (original)
+++ trunk/res/res_http_websocket.c Mon Jul 16 07:35:04 2012
@@ -267,6 +267,23 @@
 	return session->secure;
 }
 
+int ast_websocket_set_nonblock(struct ast_websocket *session)
+{
+	int flags;
+
+	if ((flags = fcntl(session->fd, F_GETFL)) == -1) {
+		return -1;
+	}
+
+	flags |= O_NONBLOCK;
+
+	if ((flags = fcntl(session->fd, F_SETFL, flags)) == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
 int ast_websocket_read(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
 {
 	char buf[MAXIMUM_FRAME_SIZE] = "";




More information about the asterisk-commits mailing list