res_pjsip: Add disable_tcp_switch option.
[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
30 #define TIMER_T1_MIN 100
31 #define DEFAULT_TIMER_T1 500
32 #define DEFAULT_TIMER_B 32000
33
34 struct system_config {
35         SORCERY_OBJECT(details);
36         /*! Transaction Timer T1 value */
37         unsigned int timert1;
38         /*! Transaction Timer B value */
39         unsigned int timerb;
40         /*! Should we use short forms for headers? */
41         unsigned int compactheaders;
42         struct {
43                 /*! Initial number of threads in the threadpool */
44                 int initial_size;
45                 /*! The amount by which the number of threads is incremented when necessary */
46                 int auto_increment;
47                 /*! Thread idle timeout in seconds */
48                 int idle_timeout;
49                 /*! Maxumum number of threads in the threadpool */
50                 int max_size;
51         } threadpool;
52         /*! Nonzero to disable switching from UDP to TCP transport */
53         unsigned int disable_tcp_switch;
54 };
55
56 static struct ast_threadpool_options sip_threadpool_options = {
57         .version = AST_THREADPOOL_OPTIONS_VERSION,
58 };
59
60 void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options)
61 {
62         *threadpool_options = sip_threadpool_options;
63 }
64
65 static struct ast_sorcery *system_sorcery;
66
67 static void *system_alloc(const char *name)
68 {
69         struct system_config *system = ast_sorcery_generic_alloc(sizeof(*system), NULL);
70
71         if (!system) {
72                 return NULL;
73         }
74
75         return system;
76 }
77
78 static int system_apply(const struct ast_sorcery *system_sorcery, void *obj)
79 {
80         struct system_config *system = obj;
81         int min_timerb;
82
83         if (system->timert1 < TIMER_T1_MIN) {
84                 ast_log(LOG_WARNING, "Timer T1 setting is too low. Setting to %d\n", TIMER_T1_MIN);
85                 system->timert1 = TIMER_T1_MIN;
86         }
87
88         min_timerb = 64 * system->timert1;
89
90         if (system->timerb < min_timerb) {
91                 ast_log(LOG_WARNING, "Timer B setting is too low. Setting to %d\n", min_timerb);
92                 system->timerb = min_timerb;
93         }
94
95         pjsip_cfg()->tsx.t1 = system->timert1;
96         pjsip_cfg()->tsx.td = system->timerb;
97
98         if (system->compactheaders) {
99                 extern pj_bool_t pjsip_use_compact_form;
100
101                 pjsip_use_compact_form = PJ_TRUE;
102         }
103
104         sip_threadpool_options.initial_size = system->threadpool.initial_size;
105         sip_threadpool_options.auto_increment = system->threadpool.auto_increment;
106         sip_threadpool_options.idle_timeout = system->threadpool.idle_timeout;
107         sip_threadpool_options.max_size = system->threadpool.max_size;
108
109         pjsip_cfg()->endpt.disable_tcp_switch =
110                 system->disable_tcp_switch ? PJ_TRUE : PJ_FALSE;
111
112         return 0;
113 }
114
115 int ast_sip_initialize_system(void)
116 {
117         RAII_VAR(struct ao2_container *, system_configs, NULL, ao2_cleanup);
118         RAII_VAR(struct system_config *, system, NULL, ao2_cleanup);
119
120         system_sorcery = ast_sorcery_open();
121         if (!system_sorcery) {
122                 ast_log(LOG_ERROR, "Failed to open SIP system sorcery\n");
123                 return -1;
124         }
125
126         ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system");
127
128         if (ast_sorcery_object_register_no_reload(system_sorcery, "system", system_alloc, NULL, system_apply)) {
129                 ast_log(LOG_ERROR, "Failed to register with sorcery (is res_sorcery_config loaded?)\n");
130                 ast_sorcery_unref(system_sorcery);
131                 system_sorcery = NULL;
132                 return -1;
133         }
134
135         ast_sorcery_object_field_register(system_sorcery, "system", "type", "", OPT_NOOP_T, 0, 0);
136         ast_sorcery_object_field_register(system_sorcery, "system", "timer_t1", __stringify(DEFAULT_TIMER_T1),
137                         OPT_UINT_T, 0, FLDSET(struct system_config, timert1));
138         ast_sorcery_object_field_register(system_sorcery, "system", "timer_b", __stringify(DEFAULT_TIMER_B),
139                         OPT_UINT_T, 0, FLDSET(struct system_config, timerb));
140         ast_sorcery_object_field_register(system_sorcery, "system", "compact_headers", "no",
141                         OPT_BOOL_T, 1, FLDSET(struct system_config, compactheaders));
142         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_initial_size", "0",
143                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.initial_size));
144         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_auto_increment", "5",
145                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.auto_increment));
146         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_idle_timeout", "60",
147                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.idle_timeout));
148         ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "0",
149                         OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
150         ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "no",
151                         OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
152
153         ast_sorcery_load(system_sorcery);
154
155         system_configs = ast_sorcery_retrieve_by_fields(system_sorcery, "system",
156                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
157
158         if (ao2_container_count(system_configs)) {
159                 return 0;
160         }
161
162         /* No config present, allocate one and apply defaults */
163         system = ast_sorcery_alloc(system_sorcery, "system", NULL);
164         if (!system) {
165                 ast_log(LOG_ERROR, "Unable to allocate default system config.\n");
166                 ast_sorcery_unref(system_sorcery);
167                 return -1;
168         }
169
170         if (system_apply(system_sorcery, system)) {
171                 ast_log(LOG_ERROR, "Failed to apply default system config.\n");
172                 ast_sorcery_unref(system_sorcery);
173                 return -1;
174         }
175
176         return 0;
177 }
178
179 void ast_sip_destroy_system(void)
180 {
181         ast_sorcery_unref(system_sorcery);
182 }
183
184 static int system_create_resolver_and_set_nameservers(void *data)
185 {
186         struct ao2_container *discovered_nameservers;
187         struct ao2_iterator it_nameservers;
188         char *nameserver;
189         pj_status_t status;
190         pj_dns_resolver *resolver;
191         pj_str_t nameservers[PJ_DNS_RESOLVER_MAX_NS];
192         unsigned int count = 0;
193
194         discovered_nameservers = ast_dns_get_nameservers();
195         if (!discovered_nameservers) {
196                 ast_log(LOG_ERROR, "Could not retrieve local system nameservers, resorting to system resolution\n");
197                 return 0;
198         }
199
200         if (!ao2_container_count(discovered_nameservers)) {
201                 ast_log(LOG_ERROR, "There are no local system nameservers configured, resorting to system resolution\n");
202                 ao2_ref(discovered_nameservers, -1);
203                 return -1;
204         }
205
206         if (!(resolver = pjsip_endpt_get_resolver(ast_sip_get_pjsip_endpoint()))) {
207                 status = pjsip_endpt_create_resolver(ast_sip_get_pjsip_endpoint(), &resolver);
208                 if (status != PJ_SUCCESS) {
209                         ast_log(LOG_ERROR, "Could not create DNS resolver(%d), resorting to system resolution\n", status);
210                         ao2_ref(discovered_nameservers, -1);
211                         return 0;
212                 }
213         }
214
215         it_nameservers = ao2_iterator_init(discovered_nameservers, 0);
216         while ((nameserver = ao2_iterator_next(&it_nameservers))) {
217                 pj_strset2(&nameservers[count++], nameserver);
218                 ao2_ref(nameserver, -1);
219
220                 if (count == (PJ_DNS_RESOLVER_MAX_NS - 1)) {
221                         break;
222                 }
223         }
224         ao2_iterator_destroy(&it_nameservers);
225
226         status = pj_dns_resolver_set_ns(resolver, count, nameservers, NULL);
227
228         /* Since we no longer need the nameservers we can drop the list of them */
229         ao2_ref(discovered_nameservers, -1);
230
231         if (status != PJ_SUCCESS) {
232                 ast_log(LOG_ERROR, "Could not set nameservers on DNS resolver in PJSIP(%d), resorting to system resolution\n",
233                         status);
234                 return 0;
235         }
236
237         if (!pjsip_endpt_get_resolver(ast_sip_get_pjsip_endpoint())) {
238                 status = pjsip_endpt_set_resolver(ast_sip_get_pjsip_endpoint(), resolver);
239                 if (status != PJ_SUCCESS) {
240                         ast_log(LOG_ERROR, "Could not set DNS resolver in PJSIP(%d), resorting to system resolution\n", status);
241                         return 0;
242                 }
243         }
244
245         return 0;
246 }
247
248 void ast_sip_initialize_dns(void)
249 {
250         ast_sip_push_task_synchronous(NULL, system_create_resolver_and_set_nameservers, NULL);
251 }