785fcc5ac57c7646d78e607c03b96c8e7210c871
[asterisk/asterisk.git] / res / res_pjsip / config_transport.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 #include "asterisk.h"
20
21 #include <pjsip.h>
22 #include <pjlib.h>
23
24 #include "asterisk/res_pjsip.h"
25 #include "asterisk/res_pjsip_cli.h"
26 #include "asterisk/logger.h"
27 #include "asterisk/astobj2.h"
28 #include "asterisk/sorcery.h"
29 #include "asterisk/acl.h"
30 #include "include/res_pjsip_private.h"
31 #include "asterisk/http_websocket.h"
32
33 static int sip_transport_to_ami(const struct ast_sip_transport *transport,
34                                 struct ast_str **buf)
35 {
36         return ast_sip_sorcery_object_to_ami(transport, buf);
37 }
38
39 static int format_ami_endpoint_transport(const struct ast_sip_endpoint *endpoint,
40                                          struct ast_sip_ami *ami)
41 {
42         RAII_VAR(struct ast_str *, buf,
43                  ast_sip_create_ami_event("TransportDetail", ami), ast_free);
44         RAII_VAR(struct ast_sip_transport *,
45                  transport, ast_sorcery_retrieve_by_id(
46                          ast_sip_get_sorcery(), "transport",
47                          endpoint->transport), ao2_cleanup);
48         if (!buf) {
49                 return -1;
50         }
51
52         if (!transport) {
53                 astman_send_error_va(ami->s, ami->m, "Unable to retrieve "
54                                      "transport %s\n", endpoint->transport);
55                 return -1;
56         }
57
58         sip_transport_to_ami(transport, &buf);
59
60         ast_str_append(&buf, 0, "EndpointName: %s\r\n",
61                        ast_sorcery_object_get_id(endpoint));
62
63         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
64         return 0;
65 }
66
67 struct ast_sip_endpoint_formatter endpoint_transport_formatter = {
68         .format_ami = format_ami_endpoint_transport
69 };
70
71 static int destroy_transport_state(void *data)
72 {
73         pjsip_transport *transport = data;
74         pjsip_transport_shutdown(transport);
75         return 0;
76 }
77
78 /*! \brief Destructor for transport state information */
79 static void transport_state_destroy(void *obj)
80 {
81         struct ast_sip_transport_state *state = obj;
82
83         if (state->transport) {
84                 ast_sip_push_task_synchronous(NULL, destroy_transport_state, state->transport);
85         }
86 }
87
88 /*! \brief Destructor for transport */
89 static void transport_destroy(void *obj)
90 {
91         struct ast_sip_transport *transport = obj;
92
93         ast_string_field_free_memory(transport);
94         ast_free_ha(transport->localnet);
95
96         if (transport->external_address_refresher) {
97                 ast_dnsmgr_release(transport->external_address_refresher);
98         }
99
100         ao2_cleanup(transport->state);
101 }
102
103 /*! \brief Allocator for transport */
104 static void *transport_alloc(const char *name)
105 {
106         struct ast_sip_transport *transport = ast_sorcery_generic_alloc(sizeof(*transport), transport_destroy);
107
108         if (!transport) {
109                 return NULL;
110         }
111
112         if (ast_string_field_init(transport, 256)) {
113                 ao2_cleanup(transport);
114                 return NULL;
115         }
116
117         pjsip_tls_setting_default(&transport->tls);
118         transport->tls.ciphers = transport->ciphers;
119
120         return transport;
121 }
122
123 static void set_qos(struct ast_sip_transport *transport, pj_qos_params *qos)
124 {
125         int tos_as_dscp = transport->tos >> 2;
126
127         if (transport->tos) {
128                 qos->flags |= PJ_QOS_PARAM_HAS_DSCP;
129                 qos->dscp_val = tos_as_dscp;
130         }
131         if (transport->cos) {
132                 qos->flags |= PJ_QOS_PARAM_HAS_SO_PRIO;
133                 qos->so_prio = transport->cos;
134         }
135 }
136
137 /*! \brief Apply handler for transports */
138 static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
139 {
140         struct ast_sip_transport *transport = obj;
141         RAII_VAR(struct ast_sip_transport *, existing, ast_sorcery_retrieve_by_id(sorcery, "transport", ast_sorcery_object_get_id(obj)), ao2_cleanup);
142         pj_status_t res = -1;
143
144         if (!existing || !existing->state) {
145                 if (!(transport->state = ao2_alloc(sizeof(*transport->state), transport_state_destroy))) {
146                         ast_log(LOG_ERROR, "Transport state for '%s' could not be allocated\n", ast_sorcery_object_get_id(obj));
147                         return -1;
148                 }
149         } else {
150                 transport->state = existing->state;
151                 ao2_ref(transport->state, +1);
152         }
153
154         /* Once active a transport can not be reconfigured */
155         if (transport->state->transport || transport->state->factory) {
156                 return -1;
157         }
158
159         if (transport->host.addr.sa_family != PJ_AF_INET && transport->host.addr.sa_family != PJ_AF_INET6) {
160                 ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", ast_sorcery_object_get_id(obj));
161                 return -1;
162         }
163
164         /* Set default port if not present */
165         if (!pj_sockaddr_get_port(&transport->host)) {
166                 pj_sockaddr_set_port(&transport->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
167         }
168
169         /* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
170         if (!ast_strlen_zero(transport->external_signaling_address)) {
171                 if (transport->host.addr.sa_family == pj_AF_INET()) {
172                         transport->external_address.ss.ss_family = AF_INET;
173                 } else if (transport->host.addr.sa_family == pj_AF_INET6()) {
174                         transport->external_address.ss.ss_family = AF_INET6;
175                 } else {
176                         ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
177                                         ast_sorcery_object_get_id(obj));
178                         return -1;
179                 }
180
181                 if (ast_dnsmgr_lookup(transport->external_signaling_address, &transport->external_address, &transport->external_address_refresher, NULL) < 0) {
182                         ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", ast_sorcery_object_get_id(obj));
183                         return -1;
184                 }
185         }
186
187         if (transport->type == AST_TRANSPORT_UDP) {
188                 if (transport->host.addr.sa_family == pj_AF_INET()) {
189                         res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &transport->host.ipv4, NULL, transport->async_operations, &transport->state->transport);
190                 } else if (transport->host.addr.sa_family == pj_AF_INET6()) {
191                         res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &transport->host.ipv6, NULL, transport->async_operations, &transport->state->transport);
192                 }
193
194                 if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
195                         pj_sock_t sock;
196                         pj_qos_params qos_params;
197
198                         sock = pjsip_udp_transport_get_socket(transport->state->transport);
199                         pj_sock_get_qos_params(sock, &qos_params);
200                         set_qos(transport, &qos_params);
201                         pj_sock_set_qos_params(sock, &qos_params);
202                 }
203         } else if (transport->type == AST_TRANSPORT_TCP) {
204                 pjsip_tcp_transport_cfg cfg;
205
206                 pjsip_tcp_transport_cfg_default(&cfg, transport->host.addr.sa_family);
207                 cfg.bind_addr = transport->host;
208                 cfg.async_cnt = transport->async_operations;
209                 set_qos(transport, &cfg.qos_params);
210
211                 res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory);
212         } else if (transport->type == AST_TRANSPORT_TLS) {
213                 transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file);
214                 transport->tls.cert_file = pj_str((char*)transport->cert_file);
215                 transport->tls.privkey_file = pj_str((char*)transport->privkey_file);
216                 transport->tls.password = pj_str((char*)transport->password);
217                 set_qos(transport, &transport->tls.qos_params);
218
219                 res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &transport->tls, &transport->host, NULL, transport->async_operations, &transport->state->factory);
220         } else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
221                 if (transport->cos || transport->tos) {
222                         ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
223                 }
224                 res = PJ_SUCCESS;
225         }
226
227         if (res != PJ_SUCCESS) {
228                 char msg[PJ_ERR_MSG_SIZE];
229
230                 pj_strerror(res, msg, sizeof(msg));
231                 ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
232                 return -1;
233         }
234         return 0;
235 }
236
237 /*! \brief Custom handler for turning a string protocol into an enum */
238 static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
239 {
240         struct ast_sip_transport *transport = obj;
241
242         if (!strcasecmp(var->value, "udp")) {
243                 transport->type = AST_TRANSPORT_UDP;
244         } else if (!strcasecmp(var->value, "tcp")) {
245                 transport->type = AST_TRANSPORT_TCP;
246         } else if (!strcasecmp(var->value, "tls")) {
247                 transport->type = AST_TRANSPORT_TLS;
248         } else if (!strcasecmp(var->value, "ws")) {
249                 transport->type = AST_TRANSPORT_WS;
250         } else if (!strcasecmp(var->value, "wss")) {
251                 transport->type = AST_TRANSPORT_WSS;
252         } else {
253                 return -1;
254         }
255
256         return 0;
257 }
258
259 static const char *transport_types[] = {
260         [AST_TRANSPORT_UDP] = "udp",
261         [AST_TRANSPORT_TCP] = "tcp",
262         [AST_TRANSPORT_TLS] = "tls",
263         [AST_TRANSPORT_WS] = "ws",
264         [AST_TRANSPORT_WSS] = "wss"
265 };
266
267 static int transport_protocol_to_str(const void *obj, const intptr_t *args, char **buf)
268 {
269         const struct ast_sip_transport *transport = obj;
270
271         if (ARRAY_IN_BOUNDS(transport->type, transport_types)) {
272                 *buf = ast_strdup(transport_types[transport->type]);
273         }
274
275         return 0;
276 }
277
278 /*! \brief Custom handler for turning a string bind into a pj_sockaddr */
279 static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
280 {
281         struct ast_sip_transport *transport = obj;
282         pj_str_t buf;
283         int rc = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &transport->host);
284
285         return rc != PJ_SUCCESS ? -1 : 0;
286 }
287
288 static int transport_bind_to_str(const void *obj, const intptr_t *args, char **buf)
289 {
290         const struct ast_sip_transport *transport = obj;
291
292         if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) {
293                 return -1;
294         }
295
296         /* include port as well as brackets if IPv6 */
297         pj_sockaddr_print(&transport->host, *buf, MAX_OBJECT_FIELD, 1 | 2);
298
299         return 0;
300 }
301
302 /*! \brief Custom handler for TLS boolean settings */
303 static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
304 {
305         struct ast_sip_transport *transport = obj;
306
307         if (!strcasecmp(var->name, "verify_server")) {
308                 transport->tls.verify_server = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
309         } else if (!strcasecmp(var->name, "verify_client")) {
310                 transport->tls.verify_client = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
311         } else if (!strcasecmp(var->name, "require_client_cert")) {
312                 transport->tls.require_client_cert = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
313         } else {
314                 return -1;
315         }
316
317         return 0;
318 }
319
320 static int verify_server_to_str(const void *obj, const intptr_t *args, char **buf)
321 {
322         const struct ast_sip_transport *transport = obj;
323         *buf = ast_strdup(AST_YESNO(transport->tls.verify_server));
324         return 0;
325 }
326
327 static int verify_client_to_str(const void *obj, const intptr_t *args, char **buf)
328 {
329         const struct ast_sip_transport *transport = obj;
330         *buf = ast_strdup(AST_YESNO(transport->tls.verify_client));
331         return 0;
332 }
333
334 static int require_client_cert_to_str(const void *obj, const intptr_t *args, char **buf)
335 {
336         const struct ast_sip_transport *transport = obj;
337         *buf = ast_strdup(AST_YESNO(transport->tls.require_client_cert));
338         return 0;
339 }
340
341 /*! \brief Custom handler for TLS method setting */
342 static int transport_tls_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
343 {
344         struct ast_sip_transport *transport = obj;
345
346         if (!strcasecmp(var->value, "default")) {
347                 transport->tls.method = PJSIP_SSL_DEFAULT_METHOD;
348         } else if (!strcasecmp(var->value, "unspecified")) {
349                 transport->tls.method = PJSIP_SSL_UNSPECIFIED_METHOD;
350         } else if (!strcasecmp(var->value, "tlsv1")) {
351                 transport->tls.method = PJSIP_TLSV1_METHOD;
352         } else if (!strcasecmp(var->value, "sslv2")) {
353                 transport->tls.method = PJSIP_SSLV2_METHOD;
354         } else if (!strcasecmp(var->value, "sslv3")) {
355                 transport->tls.method = PJSIP_SSLV3_METHOD;
356         } else if (!strcasecmp(var->value, "sslv23")) {
357                 transport->tls.method = PJSIP_SSLV23_METHOD;
358         } else {
359                 return -1;
360         }
361
362         return 0;
363 }
364
365 static const char *tls_method_map[] = {
366         [PJSIP_SSL_DEFAULT_METHOD] = "default",
367         [PJSIP_SSL_UNSPECIFIED_METHOD] = "unspecified",
368         [PJSIP_TLSV1_METHOD] = "tlsv1",
369         [PJSIP_SSLV2_METHOD] = "sslv2",
370         [PJSIP_SSLV3_METHOD] = "sslv3",
371         [PJSIP_SSLV23_METHOD] = "sslv23",
372 };
373
374 static int tls_method_to_str(const void *obj, const intptr_t *args, char **buf)
375 {
376         const struct ast_sip_transport *transport = obj;
377         if (ARRAY_IN_BOUNDS(transport->tls.method, tls_method_map)) {
378                 *buf = ast_strdup(tls_method_map[transport->tls.method]);
379         }
380         return 0;
381 }
382
383 /*! \brief Helper function which turns a cipher name into an identifier */
384 static pj_ssl_cipher cipher_name_to_id(const char *name)
385 {
386         pj_ssl_cipher ciphers[100], id = 0;
387         unsigned int cipher_num = PJ_ARRAY_SIZE(ciphers);
388         int pos;
389
390         if (pj_ssl_cipher_get_availables(ciphers, &cipher_num)) {
391                 return 0;
392         }
393
394         for (pos = 0; pos < cipher_num; ++pos) {
395                 if (!pj_ssl_cipher_name(ciphers[pos]) ||
396                         strcmp(pj_ssl_cipher_name(ciphers[pos]), name)) {
397                         continue;
398                 }
399
400                 id = ciphers[pos];
401                 break;
402         }
403
404         return id;
405 }
406
407 /*! \brief Custom handler for TLS cipher setting */
408 static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
409 {
410         struct ast_sip_transport *transport = obj;
411         pj_ssl_cipher cipher;
412
413         if (transport->tls.ciphers_num == (SIP_TLS_MAX_CIPHERS - 1)) {
414                 return -1;
415         }
416
417         cipher = cipher_name_to_id(var->value);
418
419         if (!cipher) {
420                 /* TODO: Check this over/tweak - it's taken from pjsua for now */
421                 if (!strnicmp(var->value, "0x", 2)) {
422                         pj_str_t cipher_st = pj_str((char*)var->value + 2);
423                         cipher = pj_strtoul2(&cipher_st, NULL, 16);
424                 } else {
425                         cipher = atoi(var->value);
426                 }
427         }
428
429         if (pj_ssl_cipher_is_supported(cipher)) {
430                 transport->ciphers[transport->tls.ciphers_num++] = cipher;
431                 return 0;
432         } else {
433                 ast_log(LOG_ERROR, "Cipher '%s' is unsupported\n", var->value);
434                 return -1;
435         }
436 }
437
438 static int transport_tls_cipher_to_str(const void *obj, const intptr_t *args, char **buf)
439 {
440         RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
441         const struct ast_sip_transport *transport = obj;
442         int i;
443
444         if (!str) {
445                 return -1;
446         }
447
448         for (i = 0; i < transport->tls.ciphers_num; ++i) {
449                 ast_str_append(&str, 0, "%s", pj_ssl_cipher_name(transport->ciphers[i]));
450                 if (i < transport->tls.ciphers_num - 1) {
451                         ast_str_append(&str, 0, ",");
452                 }
453         }
454
455         *buf = ast_strdup(ast_str_buffer(str));
456         return 0;
457 }
458
459 /*! \brief Custom handler for localnet setting */
460 static int transport_localnet_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
461 {
462         struct ast_sip_transport *transport = obj;
463         int error = 0;
464
465         if (!(transport->localnet = ast_append_ha("d", var->value, transport->localnet, &error))) {
466                 return -1;
467         }
468
469         return error;
470 }
471
472 static int localnet_to_vl(const void *obj, struct ast_variable **fields)
473 {
474         const struct ast_sip_transport *transport = obj;
475
476         char str[MAX_OBJECT_FIELD];
477         struct ast_variable *head = NULL;
478         struct ast_ha *ha = transport->localnet;
479
480         for (; ha; ha = ha->next) {
481                 const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
482                 snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
483                         addr, ast_sockaddr_stringify_addr(&ha->netmask));
484
485                 ast_variable_list_append(&head, ast_variable_new("local_net", str, ""));
486         }
487
488         if (head) {
489                 *fields = head;
490         }
491
492         return 0;
493 }
494
495 static int localnet_to_str(const void *obj, const intptr_t *args, char **buf)
496 {
497         RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
498         const struct ast_sip_transport *transport = obj;
499
500         ast_ha_join(transport->localnet, &str);
501         *buf = ast_strdup(ast_str_buffer(str));
502         return 0;
503 }
504
505 /*! \brief Custom handler for TOS setting */
506 static int transport_tos_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
507 {
508         struct ast_sip_transport *transport = obj;
509         unsigned int value;
510
511         if (ast_str2tos(var->value, &value)) {
512                 ast_log(LOG_ERROR, "Error configuring transport '%s' - Could not "
513                         "interpret 'tos' value '%s'\n",
514                         ast_sorcery_object_get_id(transport), var->value);
515                 return -1;
516         }
517
518         if (value % 4) {
519                 value = value >> 2;
520                 value = value << 2;
521                 ast_log(LOG_WARNING,
522                         "transport '%s' - 'tos' value '%s' uses bits that are "
523                         "discarded when converted to DSCP. Using equivalent %u instead.\n",
524                         ast_sorcery_object_get_id(transport), var->value, value);
525         }
526
527         transport->tos = value;
528         return 0;
529 }
530
531 static int tos_to_str(const void *obj, const intptr_t *args, char **buf)
532 {
533         const struct ast_sip_transport *transport = obj;
534
535         if (ast_asprintf(buf, "%u", transport->tos) == -1) {
536                 return -1;
537         }
538         return 0;
539 }
540
541 static struct ao2_container *cli_get_container(void)
542 {
543         RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
544         struct ao2_container *s_container;
545
546         container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport",
547                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
548         if (!container) {
549                 return NULL;
550         }
551
552         s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
553                 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
554         if (!s_container) {
555                 return NULL;
556         }
557
558         if (ao2_container_dup(s_container, container, 0)) {
559                 ao2_ref(s_container, -1);
560                 return NULL;
561         }
562
563         return s_container;
564 }
565
566 static int cli_iterate(void *container, ao2_callback_fn callback, void *args)
567 {
568         const struct ast_sip_endpoint *endpoint = container;
569         struct ast_sip_transport *transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
570                 "transport", endpoint->transport);
571
572         if (!transport) {
573                 return -1;
574         }
575
576         return callback(transport, args, 0);
577 }
578
579 static void *cli_retrieve_by_id(const char *id)
580 {
581         return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", id);
582 }
583
584 static int cli_print_header(void *obj, void *arg, int flags)
585 {
586         struct ast_sip_cli_context *context = arg;
587         int indent = CLI_INDENT_TO_SPACES(context->indent_level);
588         int filler = CLI_MAX_WIDTH - indent - 61;
589
590         ast_assert(context->output_buffer != NULL);
591
592         ast_str_append(&context->output_buffer, 0,
593                 "%*s:  <TransportId........>  <Type>  <cos>  <tos>  <BindAddress%*.*s>\n",
594                 indent, "Transport", filler, filler, CLI_HEADER_FILLER);
595
596         return 0;
597 }
598
599 static int cli_print_body(void *obj, void *arg, int flags)
600 {
601         struct ast_sip_transport *transport = obj;
602         struct ast_sip_cli_context *context = arg;
603         char hoststr[PJ_INET6_ADDRSTRLEN];
604
605         ast_assert(context->output_buffer != NULL);
606
607         pj_sockaddr_print(&transport->host, hoststr, sizeof(hoststr), 3);
608
609         ast_str_append(&context->output_buffer, 0, "%*s:  %-21s  %6s  %5u  %5u  %s\n",
610                 CLI_INDENT_TO_SPACES(context->indent_level), "Transport",
611                 ast_sorcery_object_get_id(transport),
612                 ARRAY_IN_BOUNDS(transport->type, transport_types) ? transport_types[transport->type] : "Unknown",
613                 transport->cos, transport->tos, hoststr);
614
615         if (context->show_details
616                 || (context->show_details_only_level_0 && context->indent_level == 0)) {
617                 ast_str_append(&context->output_buffer, 0, "\n");
618                 ast_sip_cli_print_sorcery_objectset(transport, context, 0);
619         }
620
621         return 0;
622 }
623
624 static struct ast_cli_entry cli_commands[] = {
625         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports",
626                 .command = "pjsip list transports",
627                 .usage = "Usage: pjsip list transports\n"
628                                  "       List the configured PJSIP Transports\n"),
629         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transports",
630                 .command = "pjsip show transports",
631                 .usage = "Usage: pjsip show transports\n"
632                                  "       Show the configured PJSIP Transport\n"),
633         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transport",
634                 .command = "pjsip show transport",
635                 .usage = "Usage: pjsip show transport <id>\n"
636                                  "       Show the configured PJSIP Transport\n"),
637 };
638
639 static struct ast_sip_cli_formatter_entry *cli_formatter;
640
641 /*! \brief Initialize sorcery with transport support */
642 int ast_sip_initialize_sorcery_transport(void)
643 {
644         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
645
646         ast_sorcery_apply_default(sorcery, "transport", "config", "pjsip.conf,criteria=type=transport");
647
648         if (ast_sorcery_object_register_no_reload(sorcery, "transport", transport_alloc, NULL, transport_apply)) {
649                 return -1;
650         }
651
652         ast_sorcery_object_field_register(sorcery, "transport", "type", "", OPT_NOOP_T, 0, 0);
653         ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, NULL, 0, 0);
654         ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, NULL, 0, 0);
655         ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations));
656         ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file));
657         ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file));
658         ast_sorcery_object_field_register(sorcery, "transport", "priv_key_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, privkey_file));
659         ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password));
660         ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address));
661         ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535);
662         ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address));
663         ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain));
664         ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, verify_server_to_str, NULL, 0, 0);
665         ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, verify_client_to_str, NULL, 0, 0);
666         ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, require_client_cert_to_str, NULL, 0, 0);
667         ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, tls_method_to_str, NULL, 0, 0);
668         ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, transport_tls_cipher_to_str, NULL, 0, 0);
669         ast_sorcery_object_field_register_custom(sorcery, "transport", "local_net", "", transport_localnet_handler, localnet_to_str, localnet_to_vl, 0, 0);
670         ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
671         ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
672         ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
673
674         ast_sip_register_endpoint_formatter(&endpoint_transport_formatter);
675
676         cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
677         if (!cli_formatter) {
678                 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
679                 return -1;
680         }
681         cli_formatter->name = "transport";
682         cli_formatter->print_header = cli_print_header;
683         cli_formatter->print_body = cli_print_body;
684         cli_formatter->get_container = cli_get_container;
685         cli_formatter->iterate = cli_iterate;
686         cli_formatter->get_id = ast_sorcery_object_get_id;
687         cli_formatter->retrieve_by_id = cli_retrieve_by_id;
688
689         ast_sip_register_cli_formatter(cli_formatter);
690         ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
691
692         return 0;
693 }
694
695 int ast_sip_destroy_sorcery_transport(void)
696 {
697         ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
698         ast_sip_unregister_cli_formatter(cli_formatter);
699
700         return 0;
701 }