65e4e2c05ec7f7101aaddc87071a8e828bcaf4c8
[asterisk/asterisk.git] / res / res_pjsip / config_system.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@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/sorcery.h"
26 #include "include/res_pjsip_private.h"
27 #include "asterisk/threadpool.h"
28 #include "asterisk/dns.h"
29 #include "asterisk/res_pjsip_cli.h"
30
31 #define TIMER_T1_MIN 100
32 #define DEFAULT_TIMER_T1 500
33 #define DEFAULT_TIMER_B 32000
34
35 struct system_config {
36         SORCERY_OBJECT(details);
37         /*! Transaction Timer T1 value */
38         unsigned int timert1;
39         /*! Transaction Timer B value */
40         unsigned int timerb;
41         /*! Should we use short forms for headers? */
42         unsigned int compactheaders;
43         struct {
44                 /*! Initial number of threads in the threadpool */
45                 int initial_size;
46                 /*! The amount by which the number of threads is incremented when necessary */
47                 int auto_increment;
48                 /*! Thread idle timeout in seconds */
49                 int idle_timeout;
50                 /*! Maxumum number of threads in the threadpool */
51                 int max_size;
52         } threadpool;
53         /*! Nonzero to disable switching from UDP to TCP transport */
54         unsigned int disable_tcp_switch;
55         /*!
56          * Although early media is enabled in pjproject by default, it's only
57          * enabled when the To tags are different. These options allow turning
58          * on or off the feature for different tags and same tags.
59          */
60         unsigned int follow_early_media_fork;
61         unsigned int accept_multiple_sdp_answers;
62 };
63
64 static struct ast_threadpool_options sip_threadpool_options = {
65         .version = AST_THREADPOOL_OPTIONS_VERSION,
66 };
67
68 void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options)
69 {
70         *threadpool_options = sip_threadpool_options;
71 }
72
73 static struct ast_sorcery *system_sorcery;
74
75 static void *system_alloc(const char *name)
76 {
77         struct system_config *system = ast_sorcery_generic_alloc(sizeof(*system), NULL);
78
79         if (!system) {
80                 return NULL;
81         }
82
83         return system;
84 }
85
86 static int system_apply(const struct ast_sorcery *system_sorcery, void *obj)
87 {
88         struct system_config *system = obj;
89         int min_timerb;
90
91         if (system->timert1 < TIMER_T1_MIN) {
92                 ast_log(LOG_WARNING, "Timer T1 setting is too low. Setting to %d\n", TIMER_T1_MIN);
93                 system->timert1 = TIMER_T1_MIN;
94         }
95
96         min_timerb = 64 * system->timert1;
97
98         if (system->timerb < min_timerb) {
99                 ast_log(LOG_WARNING, "Timer B setting is too low. Setting to %d\n", min_timerb);
100                 system->timerb = min_timerb;
101         }
102
103         pjsip_cfg()->tsx.t1 = system->timert1;
104         pjsip_cfg()->tsx.td = system->timerb;
105
106         pjsip_cfg()->endpt.follow_early_media_fork = system->follow_early_media_fork;
107 #ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
108         pjsip_cfg()->endpt.accept_multiple_sdp_answers = system->accept_multiple_sdp_answers;
109 #else
110         if (system->accept_multiple_sdp_answers) {
111                 ast_log(LOG_WARNING,
112                         "The accept_multiple_sdp_answers flag is not supported in this version of pjproject. Ignoring\n");
113         }
114 #endif
115
116         if (system->compactheaders) {
117                 extern pj_bool_t pjsip_use_compact_form;
118
119                 pjsip_use_compact_form = PJ_TRUE;
120         }
121
122         sip_threadpool_options.initial_size = system->threadpool.initial_size;
123         sip_threadpool_options.auto_increment = system->threadpool.auto_increment;
124         sip_threadpool_options.idle_timeout = system->threadpool.idle_timeout;
125         sip_threadpool_options.max_size = system->threadpool.max_size;
126
127         pjsip_cfg()->endpt.disable_tcp_switch =
128                 system->disable_tcp_switch ? PJ_TRUE : PJ_FALSE;
129
130         return 0;
131 }
132
133 static struct system_config *get_system_cfg(void)
134 {
135         struct system_config *cfg;
136         struct ao2_container *systems;
137         systems = ast_sorcery_retrieve_by_fields(system_sorcery, "system",
138                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
139
140         if (!systems) {
141                 return NULL;
142         }
143
144         cfg = ao2_find(systems, NULL, 0);
145         ao2_ref(systems, -1);
146         return cfg;
147 }
148
149 int sip_cli_print_system(struct ast_sip_cli_context *context)
150 {
151         struct system_config *cfg = get_system_cfg();
152
153         if (!cfg) {
154                 cfg = ast_sorcery_alloc(system_sorcery, "system", NULL);
155                 if (!cfg) {
156                         return -1;
157                 }
158         }
159
160         ast_str_append(&context->output_buffer, 0, "\nSystem Settings:\n\n");
161         ast_sip_cli_print_sorcery_objectset(cfg, context, 0);
162
163         ao2_ref(cfg, -1);
164         return 0;
165 }
166
167 int ast_sip_initialize_system(void)
168 {
169         RAII_VAR(struct ao2_container *, system_configs, NULL, ao2_cleanup);
170         RAII_VAR(struct system_config *, system, NULL, ao2_cleanup);
171
172         system_sorcery = ast_sorcery_open();
173         if (!system_sorcery) {
174                 ast_log(LOG_ERROR, "Failed to open SIP system sorcery\n");
175                 return -1;
176         }
177
178         ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system");
179
180         if (ast_sorcery_object_register_no_reload(system_sorcery, "system", system_alloc, NULL, system_apply)) {
181                 ast_log(LOG_ERROR, "Failed to register with sorcery (is res_sorcery_config loaded?)\n");
182                 ast_sorcery_unref(system_sorcery);
183                 system_sorcery = NULL;
184                 return -1;
185         }
186
187         ast_sorcery_object_field_register(system_sorcery, "system", "type", "", OPT_NOOP_T, 0, 0);
188         ast_sorcery_object_field_register(system_sorcery, "system", "timer_t1", __stringify(DEFAULT_TIMER_T1),
189                         OPT_UINT_T, 0, FLDSET(struct system_config, timert1));
190         ast_sorcery_object_field_register(system_sorcery, "system", "timer_b", __stringify(DEFAULT_TIMER_B),
191                         OPT_UINT_T, 0, FLDSET(struct system_config, timerb));
192         ast_sorcery_object_field_register(system_sorcery, "system", "compact_headers", "no",
193                         OPT_BOOL_T, 1, FLDSET(struct system_config, compactheaders));
194         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_initial_size", "0",
195                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.initial_size));
196         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_auto_increment", "5",
197                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.auto_increment));
198         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_idle_timeout", "60",
199                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.idle_timeout));
200         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "50",
201                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
202         ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "yes",
203                         OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
204         ast_sorcery_object_field_register(system_sorcery, "system", "follow_early_media_fork", "yes",
205                         OPT_BOOL_T, 1, FLDSET(struct system_config, follow_early_media_fork));
206         ast_sorcery_object_field_register(system_sorcery, "system", "accept_multiple_sdp_answers", "no",
207                         OPT_BOOL_T, 1, FLDSET(struct system_config, accept_multiple_sdp_answers));
208
209         ast_sorcery_load(system_sorcery);
210
211         system_configs = ast_sorcery_retrieve_by_fields(system_sorcery, "system",
212                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
213
214         if (ao2_container_count(system_configs)) {
215                 return 0;
216         }
217
218         /* No config present, allocate one and apply defaults */
219         system = ast_sorcery_alloc(system_sorcery, "system", NULL);
220         if (!system) {
221                 ast_log(LOG_ERROR, "Unable to allocate default system config.\n");
222                 ast_sorcery_unref(system_sorcery);
223                 return -1;
224         }
225
226         if (system_apply(system_sorcery, system)) {
227                 ast_log(LOG_ERROR, "Failed to apply default system config.\n");
228                 ast_sorcery_unref(system_sorcery);
229                 return -1;
230         }
231
232         return 0;
233 }
234
235 void ast_sip_destroy_system(void)
236 {
237         ast_sorcery_unref(system_sorcery);
238 }
239
240 static int system_create_resolver_and_set_nameservers(void *data)
241 {
242         struct ao2_container *discovered_nameservers;
243         struct ao2_iterator it_nameservers;
244         char *nameserver;
245         pj_status_t status;
246         pj_dns_resolver *resolver;
247         pj_str_t nameservers[PJ_DNS_RESOLVER_MAX_NS];
248         unsigned int count = 0;
249
250         discovered_nameservers = ast_dns_get_nameservers();
251         if (!discovered_nameservers) {
252                 ast_log(LOG_ERROR, "Could not retrieve local system nameservers, resorting to system resolution\n");
253                 return 0;
254         }
255
256         if (!ao2_container_count(discovered_nameservers)) {
257                 ast_log(LOG_ERROR, "There are no local system nameservers configured, resorting to system resolution\n");
258                 ao2_ref(discovered_nameservers, -1);
259                 return -1;
260         }
261
262         if (!(resolver = pjsip_endpt_get_resolver(ast_sip_get_pjsip_endpoint()))) {
263                 status = pjsip_endpt_create_resolver(ast_sip_get_pjsip_endpoint(), &resolver);
264                 if (status != PJ_SUCCESS) {
265                         ast_log(LOG_ERROR, "Could not create DNS resolver(%d), resorting to system resolution\n", status);
266                         ao2_ref(discovered_nameservers, -1);
267                         return 0;
268                 }
269         }
270
271         it_nameservers = ao2_iterator_init(discovered_nameservers, 0);
272         while ((nameserver = ao2_iterator_next(&it_nameservers))) {
273                 pj_strset2(&nameservers[count++], nameserver);
274                 ao2_ref(nameserver, -1);
275
276                 if (count == (PJ_DNS_RESOLVER_MAX_NS - 1)) {
277                         break;
278                 }
279         }
280         ao2_iterator_destroy(&it_nameservers);
281
282         status = pj_dns_resolver_set_ns(resolver, count, nameservers, NULL);
283
284         /* Since we no longer need the nameservers we can drop the list of them */
285         ao2_ref(discovered_nameservers, -1);
286
287         if (status != PJ_SUCCESS) {
288                 ast_log(LOG_ERROR, "Could not set nameservers on DNS resolver in PJSIP(%d), resorting to system resolution\n",
289                         status);
290                 return 0;
291         }
292
293         if (!pjsip_endpt_get_resolver(ast_sip_get_pjsip_endpoint())) {
294                 status = pjsip_endpt_set_resolver(ast_sip_get_pjsip_endpoint(), resolver);
295                 if (status != PJ_SUCCESS) {
296                         ast_log(LOG_ERROR, "Could not set DNS resolver in PJSIP(%d), resorting to system resolution\n", status);
297                         return 0;
298                 }
299         }
300
301         return 0;
302 }
303
304 void ast_sip_initialize_dns(void)
305 {
306         ast_sip_push_task_wait_servant(NULL, system_create_resolver_and_set_nameservers, NULL);
307 }