res_resolver_unbound: Fix config documentation.
[asterisk/asterisk.git] / res / res_pjsip_path.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Kinsey Moore <kmoore@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 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <pjsip.h>
28 #include <pjsip_ua.h>
29
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/res_pjsip_session.h"
32 #include "asterisk/module.h"
33 #include "asterisk/strings.h"
34
35 static const pj_str_t PATH_NAME = { "Path", 4 };
36 static pj_str_t PATH_SUPPORTED_NAME = { "path", 4 };
37
38 static struct ast_sip_aor *find_aor(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
39 {
40         char *configured_aors, *aor_name;
41         pjsip_sip_uri *sip_uri;
42         char *domain_name;
43         RAII_VAR(struct ast_str *, id, NULL, ast_free);
44
45         if (ast_strlen_zero(endpoint->aors)) {
46                 return NULL;
47         }
48
49         sip_uri = pjsip_uri_get_uri(uri);
50         domain_name = ast_alloca(sip_uri->host.slen + 1);
51         ast_copy_pj_str(domain_name, &sip_uri->host, sip_uri->host.slen + 1);
52
53         configured_aors = ast_strdupa(endpoint->aors);
54
55         /* Iterate the configured AORs to see if the user or the user+domain match */
56         while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
57                 struct ast_sip_domain_alias *alias = NULL;
58
59                 if (ast_strlen_zero(aor_name)) {
60                         continue;
61                 }
62
63                 if (!pj_strcmp2(&sip_uri->user, aor_name)) {
64                         break;
65                 }
66
67                 if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) {
68                         return NULL;
69                 }
70
71                 ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr);
72                 if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
73                         ast_str_append(&id, 0, "%s", alias->domain);
74                         ao2_cleanup(alias);
75                 } else {
76                         ast_str_append(&id, 0, "%s", domain_name);
77                 }
78
79                 if (!strcmp(aor_name, ast_str_buffer(id))) {
80                         ast_free(id);
81                         break;
82                 }
83         }
84
85         if (ast_strlen_zero(aor_name)) {
86                 return NULL;
87         }
88
89         return ast_sip_location_retrieve_aor(aor_name);
90 }
91
92 /*!
93  * \brief Get the path string associated with this contact and tdata
94  *
95  * \param endpoint The endpoint from which to pull associated path data
96  * \param contact_uri The URI identifying the associated contact
97  * \param path_str The place to store the retrieved path information
98  *
99  * \retval zero on success
100  * \retval non-zero on failure or no available path information
101  */
102 static int path_get_string(pj_pool_t *pool, struct ast_sip_contact *contact, pj_str_t *path_str)
103 {
104         if (!contact || ast_strlen_zero(contact->path)) {
105                 return -1;
106         }
107
108         *path_str = pj_strdup3(pool, contact->path);
109         return 0;
110 }
111
112 static int add_supported(pjsip_tx_data *tdata)
113 {
114         pjsip_supported_hdr *hdr;
115
116         hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
117         if (!hdr) {
118                 /* insert a new Supported header */
119                 hdr = pjsip_supported_hdr_create(tdata->pool);
120                 if (!hdr) {
121                         return -1;
122                 }
123
124                 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
125         }
126
127         /* add on to the existing Supported header */
128         pj_strassign(&hdr->values[hdr->count++], &PATH_SUPPORTED_NAME);
129
130         return 0;
131 }
132
133 /*!
134  * \internal
135  * \brief Adds a Route header to an outgoing request if
136  * path information is available.
137  *
138  * \param endpoint The endpoint with which this request is associated
139  * \param contact The contact to which this request is being sent
140  * \param tdata The outbound request
141  */
142 static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
143 {
144         RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
145
146         if (!endpoint) {
147                 return;
148         }
149
150         aor = find_aor(endpoint, tdata->msg->line.req.uri);
151         if (!aor || !aor->support_path) {
152                 return;
153         }
154
155         if (add_supported(tdata)) {
156                 return;
157         }
158
159         if (contact && !ast_strlen_zero(contact->path)) {
160                 ast_sip_set_outbound_proxy(tdata, contact->path);
161         }
162 }
163
164 static void path_session_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
165 {
166         path_outgoing_request(session->endpoint, session->contact, tdata);
167 }
168
169 /*!
170  * \internal
171  * \brief Adds a path header to an outgoing 2XX response
172  *
173  * \param endpoint The endpoint to which the INVITE response is to be sent
174  * \param contact The contact to which the INVITE response is to be sent
175  * \param tdata The outbound INVITE response
176  */
177 static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
178 {
179         struct pjsip_status_line status = tdata->msg->line.status;
180         pj_str_t path_dup;
181         pjsip_generic_string_hdr *path_hdr;
182         pjsip_contact_hdr *contact_hdr;
183         RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
184         pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
185         const pj_str_t REGISTER_METHOD = {"REGISTER", 8};
186
187         if (!endpoint
188                 || !pj_stristr(&REGISTER_METHOD, &cseq->method.name)
189                 || !PJSIP_IS_STATUS_IN_CLASS(status.code, 200)) {
190                 return;
191         }
192
193         contact_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
194         if (!contact_hdr) {
195                 return;
196         }
197
198         aor = find_aor(endpoint, contact_hdr->uri);
199         if (!aor || !aor->support_path || add_supported(tdata)
200                 || path_get_string(tdata->pool, contact, &path_dup)) {
201                 return;
202         }
203
204         path_hdr = pjsip_generic_string_hdr_create(tdata->pool, &PATH_NAME, &path_dup);
205         if (!path_hdr) {
206                 return;
207         }
208
209         pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)path_hdr);
210 }
211
212 static void path_session_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
213 {
214         path_outgoing_response(session->endpoint, session->contact, tdata);
215 }
216
217 static struct ast_sip_supplement path_supplement = {
218         .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
219         .outgoing_request = path_outgoing_request,
220         .outgoing_response = path_outgoing_response,
221 };
222
223 static struct ast_sip_session_supplement path_session_supplement = {
224         .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
225         .outgoing_request = path_session_outgoing_request,
226         .outgoing_response = path_session_outgoing_response,
227 };
228
229 static int load_module(void)
230 {
231         CHECK_PJSIP_SESSION_MODULE_LOADED();
232
233         if (ast_sip_register_supplement(&path_supplement)) {
234                 return AST_MODULE_LOAD_DECLINE;
235         }
236
237         if (ast_sip_session_register_supplement(&path_session_supplement)) {
238                 ast_sip_unregister_supplement(&path_supplement);
239                 return AST_MODULE_LOAD_DECLINE;
240         }
241
242         return AST_MODULE_LOAD_SUCCESS;
243 }
244
245 static int unload_module(void)
246 {
247         ast_sip_unregister_supplement(&path_supplement);
248         ast_sip_session_unregister_supplement(&path_session_supplement);
249         return 0;
250 }
251
252 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Path Header Support",
253         .support_level = AST_MODULE_SUPPORT_CORE,
254         .load = load_module,
255         .unload = unload_module,
256         .load_pri = AST_MODPRI_APP_DEPEND,
257 );