Review Board 1.7.16


WebSocket SIP Support

Review Request #2008 - Created July 2, 2012 and submitted

Joshua Colp
websocket-sip
Reviewers
asterisk-dev
Asterisk
These changes add WebSocket transport support to chan_sip and fix some minor issues uncovered in the stack when used with WebSocket as a transport.
Tested using sipml5 javascript SIP stack. Confirmed protocol traffic is correct, that connections are shutdown properly when they should be, that registration works, and that calling works.
/trunk/channels/chan_sip.c
Revision 369847 New Change
[20] 1163 lines
[+20] [+] static struct ast_subscription_mwi_list {
1164
static void temp_pvt_cleanup(void *);
1164
static void temp_pvt_cleanup(void *);
1165

    
   
1165

   
1166
/*! \brief A per-thread temporary pvt structure */
1166
/*! \brief A per-thread temporary pvt structure */
1167
AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
1167
AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
1168

    
   
1168

   

    
   
1169
/*! \brief A per-thread buffer for transport to string conversion */

    
   
1170
AST_THREADSTORAGE(sip_transport_str_buf);

    
   
1171

   

    
   
1172
/*! \brief Size of the SIP transport buffer */

    
   
1173
#define SIP_TRANSPORT_STR_BUFSIZE 128

    
   
1174

   
1169
/*! \brief Authentication container for realm authentication */
1175
/*! \brief Authentication container for realm authentication */
1170
static struct sip_auth_container *authl = NULL;
1176
static struct sip_auth_container *authl = NULL;
1171
/*! \brief Global authentication container protection while adjusting the references. */
1177
/*! \brief Global authentication container protection while adjusting the references. */
1172
AST_MUTEX_DEFINE_STATIC(authl_lock);
1178
AST_MUTEX_DEFINE_STATIC(authl_lock);
1173

    
   
1179

   
[+20] [20] 1347 lines
[+20] [+] static void *sip_tcp_worker_fn(void *data)
2521
	struct ast_tcptls_session_instance *tcptls_session = data;
2527
	struct ast_tcptls_session_instance *tcptls_session = data;
2522

    
   
2528

   
2523
	return _sip_tcp_helper_thread(tcptls_session);
2529
	return _sip_tcp_helper_thread(tcptls_session);
2524
}
2530
}
2525

    
   
2531

   

    
   
2532
/*! \brief SIP WebSocket connection handler */

    
   
2533
static void sip_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)

    
   
2534
{

    
   
2535
	int res;

    
   
2536

   

    
   
2537
	if (ast_websocket_set_nonblock(session)) {

    
   
2538
		goto end;

    
   
2539
	}

    
   
2540

   

    
   
2541
	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {

    
   
2542
		char *payload;

    
   
2543
		uint64_t payload_len;

    
   
2544
		enum ast_websocket_opcode opcode;

    
   
2545
		int fragmented;

    
   
2546

   

    
   
2547
		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {

    
   
2548
			/* We err on the side of caution and terminate the session if any error occurs */

    
   
2549
			break;

    
   
2550
		}

    
   
2551

   

    
   
2552
		if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {

    
   
2553
			struct sip_request req = { 0, };

    
   
2554

   

    
   
2555
			if (!(req.data = ast_str_create(payload_len))) {

    
   
2556
				goto end;

    
   
2557
			}

    
   
2558

   

    
   
2559
			if (ast_str_set(&req.data, -1, "%s", payload) == AST_DYNSTR_BUILD_FAILED) {

    
   
2560
				deinit_req(&req);

    
   
2561
				goto end;

    
   
2562
			}

    
   
2563

   

    
   
2564
			req.socket.fd = ast_websocket_fd(session);

    
   
2565
			set_socket_transport(&req.socket, ast_websocket_is_secure(session) ? SIP_TRANSPORT_WSS : SIP_TRANSPORT_WS);

    
   
2566
			req.socket.ws_session = session;

    
   
2567

   

    
   
2568
			handle_request_do(&req, ast_websocket_remote_address(session));

    
   
2569
			deinit_req(&req);

    
   
2570

   

    
   
2571
		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {

    
   
2572
			break;

    
   
2573
		}

    
   
2574
	}

    
   
2575

   

    
   
2576
end:

    
   
2577
	ast_websocket_unref(session);

    
   
2578
}

    
   
2579

   
2526
/*! \brief Check if the authtimeout has expired.
2580
/*! \brief Check if the authtimeout has expired.
2527
 * \param start the time when the session started
2581
 * \param start the time when the session started
2528
 *
2582
 *
2529
 * \retval 0 the timeout has expired
2583
 * \retval 0 the timeout has expired
2530
 * \retval -1 error
2584
 * \retval -1 error
[+20] [20] 265 lines
[+20] [+] static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session)
2796
			}
2850
			}
2797
			/*! \todo XXX If there's no Content-Length or if the content-length and what
2851
			/*! \todo XXX If there's no Content-Length or if the content-length and what
2798
					we receive is not the same - we should generate an error */
2852
					we receive is not the same - we should generate an error */
2799

    
   
2853

   
2800
			req.socket.tcptls_session = tcptls_session;
2854
			req.socket.tcptls_session = tcptls_session;

    
   
2855
			req.socket.ws_session = NULL;
2801
			handle_request_do(&req, &tcptls_session->remote_address);
2856
			handle_request_do(&req, &tcptls_session->remote_address);
2802
		}
2857
		}
2803

    
   
2858

   
2804
		if (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
2859
		if (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
2805
			enum sip_tcptls_alert alert;
2860
			enum sip_tcptls_alert alert;
[+20] [20] 496 lines
[+20] [+] static int get_transport_str2enum(const char *transport)
3302
		res |= SIP_TRANSPORT_TCP;
3357
		res |= SIP_TRANSPORT_TCP;
3303
	}
3358
	}
3304
	if (!strcasecmp(transport, "tls")) {
3359
	if (!strcasecmp(transport, "tls")) {
3305
		res |= SIP_TRANSPORT_TLS;
3360
		res |= SIP_TRANSPORT_TLS;
3306
	}
3361
	}

    
   
3362
	if (!strcasecmp(transport, "ws")) {

    
   
3363
		res |= SIP_TRANSPORT_WS;

    
   
3364
	}

    
   
3365
	if (!strcasecmp(transport, "wss")) {

    
   
3366
		res |= SIP_TRANSPORT_WSS;

    
   
3367
	}
3307

    
   
3368

   
3308
	return res;
3369
	return res;
3309
}
3370
}
3310

    
   
3371

   
3311
/*! \brief Return configuration of transports for a device */
3372
/*! \brief Return configuration of transports for a device */
3312
static inline const char *get_transport_list(unsigned int transports) {
3373
static inline const char *get_transport_list(unsigned int transports)
3313
	switch (transports) {
3374
{
3314
		case SIP_TRANSPORT_UDP:
3375
	char *buf;
3315
			return "UDP";
3376

   
3316
		case SIP_TRANSPORT_TCP:
3377
	if (!transports) {
3317
			return "TCP";
3378
		return "UNKNOWN";
3318
		case SIP_TRANSPORT_TLS:
3379
	}
3319
			return "TLS";
3380

   
3320
		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TCP:
3381
	if (!(buf = ast_threadstorage_get(&sip_transport_str_buf, SIP_TRANSPORT_STR_BUFSIZE))) {
3321
			return "TCP,UDP";
3382
		return "";
3322
		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TLS:
3383
	}
3323
			return "TLS,UDP";
3384

   
3324
		case SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS:
3385
	memset(buf, 0, SIP_TRANSPORT_STR_BUFSIZE);
3325
			return "TLS,TCP";
3386

   
3326
		default:
3387
	if (transports & SIP_TRANSPORT_UDP) {
3327
			return transports ?
3388
		strncat(buf, "UDP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
3328
				"TLS,TCP,UDP" : "UNKNOWN";	
3389
	}

    
   
3390
	if (transports & SIP_TRANSPORT_TCP) {

    
   
3391
		strncat(buf, "TCP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));

    
   
3392
	}

    
   
3393
	if (transports & SIP_TRANSPORT_TLS) {

    
   
3394
		strncat(buf, "TLS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
3329
	}
3395
	}

    
   
3396
	if (transports & SIP_TRANSPORT_WS) {

    
   
3397
		strncat(buf, "WS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));

    
   
3398
	}

    
   
3399
	if (transports & SIP_TRANSPORT_WSS) {

    
   
3400
		strncat(buf, "WSS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));

    
   
3401
	}

    
   
3402

   

    
   
3403
	/* Remove the trailing ',' if present */

    
   
3404
	if (strlen(buf)) {

    
   
3405
		buf[strlen(buf) - 1] = 0;

    
   
3406
	}

    
   
3407

   

    
   
3408
	return buf;
3330
}
3409
}
3331

    
   
3410

   
3332
/*! \brief Return transport as string */
3411
/*! \brief Return transport as string */
3333
const char *sip_get_transport(enum sip_transport t)
3412
const char *sip_get_transport(enum sip_transport t)
3334
{
3413
{
3335
	switch (t) {
3414
	switch (t) {
3336
	case SIP_TRANSPORT_UDP:
3415
	case SIP_TRANSPORT_UDP:
3337
		return "UDP";
3416
		return "UDP";
3338
	case SIP_TRANSPORT_TCP:
3417
	case SIP_TRANSPORT_TCP:
3339
		return "TCP";
3418
		return "TCP";
3340
	case SIP_TRANSPORT_TLS:
3419
	case SIP_TRANSPORT_TLS:
3341
		return "TLS";
3420
		return "TLS";

    
   
3421
	case SIP_TRANSPORT_WS:

    
   
3422
	case SIP_TRANSPORT_WSS:

    
   
3423
		return "WS";
3342
	}
3424
	}
3343

    
   
3425

   
3344
	return "UNKNOWN";
3426
	return "UNKNOWN";
3345
}
3427
}
3346

    
   
3428

   
3347
/*! \brief Return protocol string for srv dns query */
3429
/*! \brief Return protocol string for srv dns query */
3348
static inline const char *get_srv_protocol(enum sip_transport t)
3430
static inline const char *get_srv_protocol(enum sip_transport t)
3349
{
3431
{
3350
	switch (t) {
3432
	switch (t) {
3351
	case SIP_TRANSPORT_UDP:
3433
	case SIP_TRANSPORT_UDP:
3352
		return "udp";
3434
		return "udp";

    
   
3435
	case SIP_TRANSPORT_WS:

    
   
3436
		return "ws";
3353
	case SIP_TRANSPORT_TLS:
3437
	case SIP_TRANSPORT_TLS:
3354
	case SIP_TRANSPORT_TCP:
3438
	case SIP_TRANSPORT_TCP:
3355
		return "tcp";
3439
		return "tcp";

    
   
3440
	case SIP_TRANSPORT_WSS:

    
   
3441
		return "wss";
3356
	}
3442
	}
3357

    
   
3443

   
3358
	return "udp";
3444
	return "udp";
3359
}
3445
}
3360

    
   
3446

   
3361
/*! \brief Return service string for srv dns query */
3447
/*! \brief Return service string for srv dns query */
3362
static inline const char *get_srv_service(enum sip_transport t)
3448
static inline const char *get_srv_service(enum sip_transport t)
3363
{
3449
{
3364
	switch (t) {
3450
	switch (t) {
3365
	case SIP_TRANSPORT_TCP:
3451
	case SIP_TRANSPORT_TCP:
3366
	case SIP_TRANSPORT_UDP:
3452
	case SIP_TRANSPORT_UDP:

    
   
3453
	case SIP_TRANSPORT_WS:
3367
		return "sip";
3454
		return "sip";
3368
	case SIP_TRANSPORT_TLS:
3455
	case SIP_TRANSPORT_TLS:

    
   
3456
	case SIP_TRANSPORT_WSS:
3369
		return "sips";
3457
		return "sips";
3370
	}
3458
	}
3371
	return "sip";
3459
	return "sip";
3372
}
3460
}
3373

    
   
3461

   
[+20] [20] 36 lines
[+20] [+] static int __sip_xmit(struct sip_pvt *p, struct ast_str *data)
3410

    
   
3498

   
3411
	if (p->socket.type == SIP_TRANSPORT_UDP) {
3499
	if (p->socket.type == SIP_TRANSPORT_UDP) {
3412
		res = ast_sendto(p->socket.fd, data->str, ast_str_strlen(data), 0, dst);
3500
		res = ast_sendto(p->socket.fd, data->str, ast_str_strlen(data), 0, dst);
3413
	} else if (p->socket.tcptls_session) {
3501
	} else if (p->socket.tcptls_session) {
3414
		res = sip_tcptls_write(p->socket.tcptls_session, data->str, ast_str_strlen(data));
3502
		res = sip_tcptls_write(p->socket.tcptls_session, data->str, ast_str_strlen(data));

    
   
3503
	} else if (p->socket.ws_session) {

    
   
3504
		if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, data->str, ast_str_strlen(data)))) {

    
   
3505
			/* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */

    
   
3506
			res = ast_str_strlen(data);

    
   
3507
		}
3415
	} else {
3508
	} else {
3416
		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
3509
		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
3417
		return XMIT_ERROR;
3510
		return XMIT_ERROR;
3418
	}
3511
	}
3419

    
   
3512

   
[+20] [20] 1306 lines
[+20] [+] static void sip_destroy_peer(struct sip_peer *peer)
4726
	}
4819
	}
4727

    
   
4820

   
4728
	if (peer->socket.tcptls_session) {
4821
	if (peer->socket.tcptls_session) {
4729
		ao2_ref(peer->socket.tcptls_session, -1);
4822
		ao2_ref(peer->socket.tcptls_session, -1);
4730
		peer->socket.tcptls_session = NULL;
4823
		peer->socket.tcptls_session = NULL;

    
   
4824
	} else if (peer->socket.ws_session) {

    
   
4825
		ast_websocket_unref(peer->socket.ws_session);

    
   
4826
		peer->socket.ws_session = NULL;
4731
	}
4827
	}
4732

    
   
4828

   
4733
	ast_cc_config_params_destroy(peer->cc_params);
4829
	ast_cc_config_params_destroy(peer->cc_params);
4734

    
   
4830

   
4735
	ast_string_field_free_memory(peer);
4831
	ast_string_field_free_memory(peer);
[+20] [20] 558 lines
[+20] [+] static void set_t38_capabilities(struct sip_pvt *p)
5294
static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
5390
static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
5295
{
5391
{
5296
	if (to_sock->tcptls_session) {
5392
	if (to_sock->tcptls_session) {
5297
		ao2_ref(to_sock->tcptls_session, -1);
5393
		ao2_ref(to_sock->tcptls_session, -1);
5298
		to_sock->tcptls_session = NULL;
5394
		to_sock->tcptls_session = NULL;

    
   
5395
	} else if (to_sock->ws_session) {

    
   
5396
		ast_websocket_unref(to_sock->ws_session);

    
   
5397
		to_sock->ws_session = NULL;
5299
	}
5398
	}
5300

    
   
5399

   
5301
	if (from_sock->tcptls_session) {
5400
	if (from_sock->tcptls_session) {
5302
		ao2_ref(from_sock->tcptls_session, +1);
5401
		ao2_ref(from_sock->tcptls_session, +1);

    
   
5402
	} else if (from_sock->ws_session) {

    
   
5403
		ast_websocket_ref(from_sock->ws_session);
5303
	}
5404
	}
5304

    
   
5405

   
5305
	*to_sock = *from_sock;
5406
	*to_sock = *from_sock;
5306
}
5407
}
5307

    
   
5408

   
[+20] [20] 699 lines
[+20] [+] void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
6007
	}
6108
	}
6008

    
   
6109

   
6009
	if (p->socket.tcptls_session) {
6110
	if (p->socket.tcptls_session) {
6010
		ao2_ref(p->socket.tcptls_session, -1);
6111
		ao2_ref(p->socket.tcptls_session, -1);
6011
		p->socket.tcptls_session = NULL;
6112
		p->socket.tcptls_session = NULL;

    
   
6113
	} else if (p->socket.ws_session) {

    
   
6114
		ast_websocket_unref(p->socket.ws_session);

    
   
6115
		p->socket.ws_session = NULL;
6012
	}
6116
	}
6013

    
   
6117

   
6014
	if (p->peerauth) {
6118
	if (p->peerauth) {
6015
		ao2_t_ref(p->peerauth, -1, "Removing active peer authentication");
6119
		ao2_t_ref(p->peerauth, -1, "Removing active peer authentication");
6016
		p->peerauth = NULL;
6120
		p->peerauth = NULL;
[+20] [20] 3312 lines
[+20] [+] static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
9329
		int audio = FALSE;
9433
		int audio = FALSE;
9330
		int video = FALSE;
9434
		int video = FALSE;
9331
		int image = FALSE;
9435
		int image = FALSE;
9332
		int text = FALSE;
9436
		int text = FALSE;
9333
		int processed_crypto = FALSE;
9437
		int processed_crypto = FALSE;
9334
		char protocol[5] = {0,};
9438
		char protocol[6] = {0,};
9335
		int x;
9439
		int x;
9336

    
   
9440

   
9337
		numberofports = 0;
9441
		numberofports = 0;
9338
		len = -1;
9442
		len = -1;
9339
		start = next;
9443
		start = next;
[+20] [20] 9 lines
[+20] static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
9349
		AST_LIST_INSERT_TAIL(&p->offered_media, offer, next);
9453
		AST_LIST_INSERT_TAIL(&p->offered_media, offer, next);
9350
		offer->type = SDP_UNKNOWN;
9454
		offer->type = SDP_UNKNOWN;
9351

    
   
9455

   
9352
		/* Check for 'audio' media offer */
9456
		/* Check for 'audio' media offer */
9353
		if (strncmp(m, "audio ", 6) == 0) {
9457
		if (strncmp(m, "audio ", 6) == 0) {
9354
			if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
9458
			if ((sscanf(m, "audio %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
9355
			    (sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
9459
			    (sscanf(m, "audio %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
9356
				codecs = m + len;
9460
				codecs = m + len;
9357
				/* produce zero-port m-line since it may be needed later
9461
				/* produce zero-port m-line since it may be needed later
9358
				 * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
9462
				 * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
9359
				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
9463
				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
9360
					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
9464
					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
[+20] [20] 11 lines
[+20] static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
9372
				/* Check number of ports offered for stream */
9476
				/* Check number of ports offered for stream */
9373
				if (numberofports > 1) {
9477
				if (numberofports > 1) {
9374
					ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
9478
					ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
9375
				}
9479
				}
9376

    
   
9480

   
9377
				if (!strcmp(protocol, "SAVP")) {
9481
				if (!strcmp(protocol, "SAVP") || (!strcmp(protocol, "SAVPF") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF))) {
9378
					secure_audio = 1;
9482
					secure_audio = 1;
9379
				} else if (strcmp(protocol, "AVP")) {
9483
				} else if ((!strcmp(protocol, "AVPF") || !strcmp(protocol, "SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {

    
   
9484
					ast_log(LOG_WARNING, "Received AVPF profile in audio offer but AVPF is not enabled: %s\n", m);

    
   
9485
					continue;

    
   
9486
				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
9380
					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
9487
					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
9381
					continue;
9488
					continue;
9382
				}
9489
				}
9383

    
   
9490

   
9384
				if (has_media_stream(p, SDP_AUDIO)) {
9491
				if (has_media_stream(p, SDP_AUDIO)) {
[+20] [20] 24 lines
[+20] static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
9409
				goto process_sdp_cleanup;
9516
				goto process_sdp_cleanup;
9410
			}
9517
			}
9411
		}
9518
		}
9412
		/* Check for 'video' media offer */
9519
		/* Check for 'video' media offer */
9413
		else if (strncmp(m, "video ", 6) == 0) {
9520
		else if (strncmp(m, "video ", 6) == 0) {
9414
			if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
9521
			if ((sscanf(m, "video %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
9415
			    (sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
9522
			    (sscanf(m, "video %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
9416
				codecs = m + len;
9523
				codecs = m + len;
9417
				/* produce zero-port m-line since it may be needed later
9524
				/* produce zero-port m-line since it may be needed later
9418
				 * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
9525
				 * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
9419
				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
9526
				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
9420
					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
9527
					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
[+20] [20] 11 lines
[+20] static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
9432
				/* Check number of ports offered for stream */
9539
				/* Check number of ports offered for stream */
9433
				if (numberofports > 1) {
9540
				if (numberofports > 1) {
9434
					ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
9541
					ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
9435
				}
9542
				}
9436

    
   
9543

   
9437
				if (!strcmp(protocol, "SAVP")) {
9544
				if (!strcmp(protocol, "SAVP") || (!strcmp(protocol, "SAVPF") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF))) {
9438
					secure_video = 1;
9545
					secure_video = 1;
9439
				} else if (strcmp(protocol, "AVP")) {
9546
				} else if ((!strcmp(protocol, "AVPF") || !strcmp(protocol, "SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {

    
   
9547
					ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);

    
   
9548
					continue;

    
   
9549
				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
9440
					ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
9550
					ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
9441
					continue;
9551
					continue;
9442
				}
9552
				}
9443

    
   
9553

   
9444
				if (has_media_stream(p, SDP_VIDEO)) {
9554
				if (has_media_stream(p, SDP_VIDEO)) {
[+20] [20] 1241 lines
[+20] [+] static void add_route(struct sip_request *req, struct sip_route *route)
10686
 *
10796
 *
10687
 * If there's a sips: uri scheme, TLS will be required.
10797
 * If there's a sips: uri scheme, TLS will be required.
10688
 */
10798
 */
10689
static void set_destination(struct sip_pvt *p, char *uri)
10799
static void set_destination(struct sip_pvt *p, char *uri)
10690
{
10800
{
10691
	char *h, *maddr, hostname[256];
10801
	char *trans, *h, *maddr, hostname[256];
10692
	int hn;
10802
	int hn;
10693
	int debug=sip_debug_test_pvt(p);
10803
	int debug=sip_debug_test_pvt(p);
10694
	int tls_on = FALSE;
10804
	int tls_on = FALSE;
10695

    
   
10805

   
10696
	if (debug)
10806
	if (debug)
10697
		ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
10807
		ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
10698

    
   
10808

   

    
   
10809
	if ((trans = strcasestr(uri, ";transport="))) {

    
   
10810
		trans += strlen(";transport=");

    
   
10811

   

    
   
10812
		if (!strncasecmp(trans, "ws", 2)) {

    
   
10813
			if (debug)

    
   
10814
				ast_verbose("set_destination: URI is for WebSocket, we can't set destination\n");

    
   
10815
			return;

    
   
10816
		}

    
   
10817
	}

    
   
10818

   
10699
	/* Find and parse hostname */
10819
	/* Find and parse hostname */
10700
	h = strchr(uri, '@');
10820
	h = strchr(uri, '@');
10701
	if (h)
10821
	if (h)
10702
		++h;
10822
		++h;
10703
	else {
10823
	else {
[+20] [20] 1330 lines
[+20] [+] static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const char **a_crypto)
12034
			ast_log(LOG_WARNING, "No SRTP key management enabled\n");
12154
			ast_log(LOG_WARNING, "No SRTP key management enabled\n");
12035
		}
12155
		}
12036
	}
12156
	}
12037
}
12157
}
12038

    
   
12158

   

    
   
12159
static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure)

    
   
12160
{

    
   
12161
	if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {

    
   
12162
		return secure ? "SAVPF" : "AVPF";

    
   
12163
	} else {

    
   
12164
		return secure ? "SAVP" : "AVP";

    
   
12165
	}

    
   
12166
}

    
   
12167

   
12039
/*! \brief Add Session Description Protocol message
12168
/*! \brief Add Session Description Protocol message
12040

    
   
12169

   
12041
    If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
12170
    If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
12042
    is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions
12171
    is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions
12043
    without modifying the media session in any way.
12172
    without modifying the media session in any way.
[+20] [20] 150 lines
[+20] [+] static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38)
12194
		/* Ok, we need video. Let's add what we need for video and set codecs.
12323
		/* Ok, we need video. Let's add what we need for video and set codecs.
12195
		   Video is handled differently than audio since we can not transcode. */
12324
		   Video is handled differently than audio since we can not transcode. */
12196
		if (needvideo) {
12325
		if (needvideo) {
12197
			get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
12326
			get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
12198
			ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
12327
			ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
12199
				v_a_crypto ? "SAVP" : "AVP");
12328
				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
12200

    
   
12329

   
12201
			/* Build max bitrate string */
12330
			/* Build max bitrate string */
12202
			if (p->maxcallbitrate)
12331
			if (p->maxcallbitrate)
12203
				snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
12332
				snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
12204
			if (debug) {
12333
			if (debug) {
[+20] [20] 10 lines
[+20] static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38)
12215
		if (needtext) {
12344
		if (needtext) {
12216
			if (sipdebug_text)
12345
			if (sipdebug_text)
12217
				ast_verbose("Lets set up the text sdp\n");
12346
				ast_verbose("Lets set up the text sdp\n");
12218
			get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
12347
			get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
12219
			ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
12348
			ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
12220
				t_a_crypto ? "SAVP" : "AVP");
12349
				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
12221
			if (debug) {  /* XXX should I use tdest below ? */
12350
			if (debug) {  /* XXX should I use tdest below ? */
12222
				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
12351
				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
12223
			}
12352
			}
12224

    
   
12353

   
12225
			if (!doing_directmedia) {
12354
			if (!doing_directmedia) {
[+20] [20] 6 lines
[+20] static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38)
12232
		/* We break with the "recommendation" and send our IP, in order that our
12361
		/* We break with the "recommendation" and send our IP, in order that our
12233
		   peer doesn't have to ast_gethostbyname() us */
12362
		   peer doesn't have to ast_gethostbyname() us */
12234

    
   
12363

   
12235
		get_crypto_attrib(p, p->srtp, &a_crypto);
12364
		get_crypto_attrib(p, p->srtp, &a_crypto);
12236
		ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
12365
		ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
12237
			a_crypto ? "SAVP" : "AVP");
12366
			       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
12238

    
   
12367

   
12239
		/* Now, start adding audio codecs. These are added in this order:
12368
		/* Now, start adding audio codecs. These are added in this order:
12240
		   - First what was requested by the calling channel
12369
		   - First what was requested by the calling channel
12241
		   - Then preferences in order from sip.conf device config for this peer/user
12370
		   - Then preferences in order from sip.conf device config for this peer/user
12242
		   - Then other codecs in capabilities, including video
12371
		   - Then other codecs in capabilities, including video
[+20] [20] 2402 lines
[+20] [+] static void set_socket_transport(struct sip_socket *socket, int transport)
14645
		socket->fd = -1;
14774
		socket->fd = -1;
14646
		socket->type = transport;
14775
		socket->type = transport;
14647
		if (socket->tcptls_session) {
14776
		if (socket->tcptls_session) {
14648
			ao2_ref(socket->tcptls_session, -1);
14777
			ao2_ref(socket->tcptls_session, -1);
14649
			socket->tcptls_session = NULL;
14778
			socket->tcptls_session = NULL;

    
   
14779
		} else if (socket->ws_session) {

    
   
14780
			ast_websocket_unref(socket->ws_session);

    
   
14781
			socket->ws_session = NULL;
14650
		}
14782
		}
14651
	}
14783
	}
14652
}
14784
}
14653

    
   
14785

   
14654
/*! \brief Expire registration of SIP peer */
14786
/*! \brief Expire registration of SIP peer */
[+20] [20] 12 lines
[+20] [+] static int expire_register(const void *data)
14667
	set_socket_transport(&peer->socket, peer->default_outbound_transport);
14799
	set_socket_transport(&peer->socket, peer->default_outbound_transport);
14668

    
   
14800

   
14669
	if (peer->socket.tcptls_session) {
14801
	if (peer->socket.tcptls_session) {
14670
		ao2_ref(peer->socket.tcptls_session, -1);
14802
		ao2_ref(peer->socket.tcptls_session, -1);
14671
		peer->socket.tcptls_session = NULL;
14803
		peer->socket.tcptls_session = NULL;

    
   
14804
	} else if (peer->socket.ws_session) {

    
   
14805
		ast_websocket_unref(peer->socket.ws_session);

    
   
14806
		peer->socket.ws_session = NULL;
14672
	}
14807
	}
14673

    
   
14808

   
14674
	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
14809
	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
14675
	register_peer_exten(peer, FALSE);	/* Remove regexten */
14810
	register_peer_exten(peer, FALSE);	/* Remove regexten */
14676
	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
14811
	ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
[+20] [20] 2154 lines
[+20] [+] static void check_via(struct sip_pvt *p, struct sip_request *req)
16831
	struct ast_sockaddr tmp = { { 0, } };
16966
	struct ast_sockaddr tmp = { { 0, } };
16832
	uint16_t port;
16967
	uint16_t port;
16833

    
   
16968

   
16834
	ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
16969
	ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
16835

    
   
16970

   

    
   
16971
	/* If this is via WebSocket we don't use the Via header contents at all */

    
   
16972
	if (!strncasecmp(via, "SIP/2.0/WS", 10)) {

    
   
16973
		return;

    
   
16974
	}

    
   
16975

   
16836
	/* Work on the leftmost value of the topmost Via header */
16976
	/* Work on the leftmost value of the topmost Via header */
16837
	c = strchr(via, ',');
16977
	c = strchr(via, ',');
16838
	if (c)
16978
	if (c)
16839
		*c = '\0';
16979
		*c = '\0';
16840

    
   
16980

   
[+20] [20] 4133 lines
[+20] [+] static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward)
20974
	contact_number = remove_uri_parameters(contact_number);
21114
	contact_number = remove_uri_parameters(contact_number);
20975

    
   
21115

   
20976
	if (p->socket.tcptls_session) {
21116
	if (p->socket.tcptls_session) {
20977
		ao2_ref(p->socket.tcptls_session, -1);
21117
		ao2_ref(p->socket.tcptls_session, -1);
20978
		p->socket.tcptls_session = NULL;
21118
		p->socket.tcptls_session = NULL;

    
   
21119
	} else if (p->socket.ws_session) {

    
   
21120
		ast_websocket_unref(p->socket.ws_session);

    
   
21121
		p->socket.ws_session = NULL;
20979
	}
21122
	}
20980

    
   
21123

   
20981
	set_socket_transport(&p->socket, transport);
21124
	set_socket_transport(&p->socket, transport);
20982

    
   
21125

   
20983
	if (set_call_forward && ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
21126
	if (set_call_forward && ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
[+20] [20] 6202 lines
[+20] [+] static int sip_prepare_socket(struct sip_pvt *p)
27186
	if ((s->type & (SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS)) &&
27329
	if ((s->type & (SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS)) &&
27187
			(s->tcptls_session) &&
27330
			(s->tcptls_session) &&
27188
			(s->tcptls_session->fd != -1)) {
27331
			(s->tcptls_session->fd != -1)) {
27189
		return s->tcptls_session->fd;
27332
		return s->tcptls_session->fd;
27190
	}
27333
	}

    
   
27334
	if ((s->type & (SIP_TRANSPORT_WS | SIP_TRANSPORT_WSS))) {

    
   
27335
		return s->ws_session ? ast_websocket_fd(s->ws_session) : -1;

    
   
27336
	}
27191

    
   
27337

   
27192
	/*! \todo Check this... This might be wrong, depending on the proxy configuration
27338
	/*! \todo Check this... This might be wrong, depending on the proxy configuration
27193
		If proxy is in "force" mode its correct.
27339
		If proxy is in "force" mode its correct.
27194
	 */
27340
	 */
27195
	if (p->outboundproxy && p->outboundproxy->transport) {
27341
	if (p->outboundproxy && p->outboundproxy->transport) {
[+20] [20] 1964 lines
[+20] [+] static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
29160
				while ((trans = strsep(&val, ","))) {
29306
				while ((trans = strsep(&val, ","))) {
29161
					trans = ast_skip_blanks(trans);
29307
					trans = ast_skip_blanks(trans);
29162

    
   
29308

   
29163
					if (!strncasecmp(trans, "udp", 3)) {
29309
					if (!strncasecmp(trans, "udp", 3)) {
29164
						peer->transports |= SIP_TRANSPORT_UDP;
29310
						peer->transports |= SIP_TRANSPORT_UDP;

    
   
29311
					} else if (!strncasecmp(trans, "wss", 3)) {

    
   
29312
						peer->transports |= SIP_TRANSPORT_WSS;

    
   
29313
					} else if (!strncasecmp(trans, "ws", 2)) {

    
   
29314
						peer->transports |= SIP_TRANSPORT_WS;
29165
					} else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
29315
					} else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
29166
						peer->transports |= SIP_TRANSPORT_TCP;
29316
						peer->transports |= SIP_TRANSPORT_TCP;
29167
					} else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
29317
					} else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
29168
						peer->transports |= SIP_TRANSPORT_TLS;
29318
						peer->transports |= SIP_TRANSPORT_TLS;
29169
					} else if (!strncasecmp(trans, "tcp", 3) || !strncasecmp(trans, "tls", 3)) {
29319
					} else if (!strncasecmp(trans, "tcp", 3) || !strncasecmp(trans, "tls", 3)) {
[+20] [20] 340 lines
[+20] static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
29510
				ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_USE_SRTP);
29660
				ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_USE_SRTP);
29511
			} else if (!strcasecmp(v->name, "encryption_taglen")) {
29661
			} else if (!strcasecmp(v->name, "encryption_taglen")) {
29512
				ast_set2_flag(&peer->flags[2], !strcasecmp(v->value, "32"), SIP_PAGE3_SRTP_TAG_32);
29662
				ast_set2_flag(&peer->flags[2], !strcasecmp(v->value, "32"), SIP_PAGE3_SRTP_TAG_32);
29513
			} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
29663
			} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
29514
				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
29664
				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);

    
   
29665
			} else if (!strcasecmp(v->name, "avpf")) {

    
   
29666
				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
29515
			}
29667
			}
29516
		}
29668
		}
29517

    
   
29669

   
29518
		/* These apply to devstate lookups */
29670
		/* These apply to devstate lookups */
29519
		if (realtime && !strcasecmp(v->name, "lastms")) {
29671
		if (realtime && !strcasecmp(v->name, "lastms")) {
[+20] [20] 103 lines
[+20] static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
29623
	 * 1. Registration is not present and the socket.type and default transport types are different.
29775
	 * 1. Registration is not present and the socket.type and default transport types are different.
29624
	 * 2. The socket.type is not an acceptable transport type after rebuilding peer.
29776
	 * 2. The socket.type is not an acceptable transport type after rebuilding peer.
29625
	 * 3. The socket.type is not set yet. */
29777
	 * 3. The socket.type is not set yet. */
29626
	if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) ||
29778
	if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) ||
29627
		!(peer->socket.type & peer->transports) || !(peer->socket.type)) {
29779
		!(peer->socket.type & peer->transports) || !(peer->socket.type)) {
29628

    
   

   
29629
		set_socket_transport(&peer->socket, peer->default_outbound_transport);
29780
		set_socket_transport(&peer->socket, peer->default_outbound_transport);
29630
	}
29781
	}
29631

    
   
29782

   
29632
	if (ast_str_strlen(fullcontact)) {
29783
	if (ast_str_strlen(fullcontact)) {
29633
		ast_string_field_set(peer, fullcontact, ast_str_buffer(fullcontact));
29784
		ast_string_field_set(peer, fullcontact, ast_str_buffer(fullcontact));
[+20] [20] 521 lines
[+20] [+] static int reload_config(enum channelreloadreason reason)
30155
					default_transports |= SIP_TRANSPORT_UDP;
30306
					default_transports |= SIP_TRANSPORT_UDP;
30156
				} else if (!strncasecmp(trans, "tcp", 3)) {
30307
				} else if (!strncasecmp(trans, "tcp", 3)) {
30157
					default_transports |= SIP_TRANSPORT_TCP;
30308
					default_transports |= SIP_TRANSPORT_TCP;
30158
				} else if (!strncasecmp(trans, "tls", 3)) {
30309
				} else if (!strncasecmp(trans, "tls", 3)) {
30159
					default_transports |= SIP_TRANSPORT_TLS;
30310
					default_transports |= SIP_TRANSPORT_TLS;

    
   
30311
				} else if (!strncasecmp(trans, "wss", 3)) {

    
   
30312
					default_transports |= SIP_TRANSPORT_WSS;

    
   
30313
				} else if (!strncasecmp(trans, "ws", 2)) {

    
   
30314
					default_transports |= SIP_TRANSPORT_WS;
30160
				} else {
30315
				} else {
30161
					ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
30316
					ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
30162
				}
30317
				}
30163
				if (default_primary_transport == 0) {
30318
				if (default_primary_transport == 0) {
30164
					default_primary_transport = default_transports;
30319
					default_primary_transport = default_transports;
[+20] [20] 2394 lines
[+20] [+] static int load_module(void)
32559

    
   
32714

   
32560

    
   
32715

   
32561
	sip_register_tests();
32716
	sip_register_tests();
32562
	network_change_event_subscribe();
32717
	network_change_event_subscribe();
32563

    
   
32718

   

    
   
32719
	ast_websocket_add_protocol("sip", sip_websocket_callback);

    
   
32720

   
32564
	return AST_MODULE_LOAD_SUCCESS;
32721
	return AST_MODULE_LOAD_SUCCESS;
32565
}
32722
}
32566

    
   
32723

   
32567
/*! \brief PBX unload module API */
32724
/*! \brief PBX unload module API */
32568
static int unload_module(void)
32725
static int unload_module(void)
32569
{
32726
{
32570
	struct sip_pvt *p;
32727
	struct sip_pvt *p;
32571
	struct sip_threadinfo *th;
32728
	struct sip_threadinfo *th;
32572
	struct ast_context *con;
32729
	struct ast_context *con;
32573
	struct ao2_iterator i;
32730
	struct ao2_iterator i;
32574
	int wait_count;
32731
	int wait_count;
32575

    
   
32732

   

    
   
32733
	ast_websocket_remove_protocol("sip", sip_websocket_callback);

    
   
32734

   
32576
	network_change_event_unsubscribe();
32735
	network_change_event_unsubscribe();
32577

    
   
32736

   
32578
	ast_sched_dump(sched);
32737
	ast_sched_dump(sched);
32579
	
32738
	
32580
	/* First, take us out of the channel type list */
32739
	/* First, take us out of the channel type list */
[+20] [20] 173 lines
/trunk/channels/sip/sdp_crypto.c
Revision 369836 New Change
 
/trunk/channels/sip/security_events.c
Revision 369836 New Change
 
/trunk/channels/sip/include/sip.h
Revision 369836 New Change
 
/trunk/configs/sip.conf.sample
Revision 369836 New Change
 
/trunk/include/asterisk/http_websocket.h
Revision 369836 New Change
 
/trunk/res/res_http_websocket.c
Revision 369836 New Change
 
  1. /trunk/channels/chan_sip.c: Loading...
  2. /trunk/channels/sip/sdp_crypto.c: Loading...
  3. /trunk/channels/sip/security_events.c: Loading...
  4. /trunk/channels/sip/include/sip.h: Loading...
  5. /trunk/configs/sip.conf.sample: Loading...
  6. /trunk/include/asterisk/http_websocket.h: Loading...
  7. /trunk/res/res_http_websocket.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.