security_events: Fix error caused by DTD validation error
[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 = strsep(&configured_aors, ","))) {
57                 struct ast_sip_domain_alias *alias = NULL;
58
59                 if (!pj_strcmp2(&sip_uri->user, aor_name)) {
60                         break;
61                 }
62
63                 if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) {
64                         return NULL;
65                 }
66
67                 ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr);
68                 if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
69                         ast_str_append(&id, 0, "%s", alias->domain);
70                         ao2_cleanup(alias);
71                 } else {
72                         ast_str_append(&id, 0, "%s", domain_name);
73                 }
74
75                 if (!strcmp(aor_name, ast_str_buffer(id))) {
76                         ast_free(id);
77                         break;
78                 }
79         }
80
81         if (ast_strlen_zero(aor_name)) {
82                 return NULL;
83         }
84
85         return ast_sip_location_retrieve_aor(aor_name);
86 }
87
88 /*!
89  * \brief Get the path string associated with this contact and tdata
90  *
91  * \param endpoint The endpoint from which to pull associated path data
92  * \param contact_uri The URI identifying the associated contact
93  * \param path_str The place to store the retrieved path information
94  *
95  * \retval zero on success
96  * \retval non-zero on failure or no available path information
97  */
98 static int path_get_string(pj_pool_t *pool, struct ast_sip_contact *contact, pj_str_t *path_str)
99 {
100         if (!contact || ast_strlen_zero(contact->path)) {
101                 return -1;
102         }
103
104         *path_str = pj_strdup3(pool, contact->path);
105         return 0;
106 }
107
108 static int add_supported(pjsip_tx_data *tdata)
109 {
110         pjsip_supported_hdr *hdr;
111
112         hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
113         if (!hdr) {
114                 /* insert a new Supported header */
115                 hdr = pjsip_supported_hdr_create(tdata->pool);
116                 if (!hdr) {
117                         return -1;
118                 }
119
120                 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
121         }
122
123         /* add on to the existing Supported header */
124         pj_strassign(&hdr->values[hdr->count++], &PATH_SUPPORTED_NAME);
125
126         return 0;
127 }
128
129 /*!
130  * \internal
131  * \brief Adds a Route header to an outgoing request if
132  * path information is available.
133  *
134  * \param endpoint The endpoint with which this request is associated
135  * \param contact The contact to which this request is being sent
136  * \param tdata The outbound request
137  */
138 static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
139 {
140         RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
141
142         if (!endpoint) {
143                 return;
144         }
145
146         aor = find_aor(endpoint, tdata->msg->line.req.uri);
147         if (!aor || !aor->support_path) {
148                 return;
149         }
150
151         if (add_supported(tdata)) {
152                 return;
153         }
154
155         if (contact && !ast_strlen_zero(contact->path)) {
156                 ast_sip_set_outbound_proxy(tdata, contact->path);
157         }
158 }
159
160 static void path_session_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
161 {
162         path_outgoing_request(session->endpoint, session->contact, tdata);
163 }
164
165 /*!
166  * \internal
167  * \brief Adds a path header to an outgoing 2XX response
168  *
169  * \param endpoint The endpoint to which the INVITE response is to be sent
170  * \param contact The contact to which the INVITE response is to be sent
171  * \param tdata The outbound INVITE response
172  */
173 static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
174 {
175         struct pjsip_status_line status = tdata->msg->line.status;
176         pj_str_t path_dup;
177         pjsip_generic_string_hdr *path_hdr;
178         pjsip_contact_hdr *contact_hdr;
179         RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
180         pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
181         const pj_str_t REGISTER_METHOD = {"REGISTER", 8};
182
183         if (!endpoint
184                 || !pj_stristr(&REGISTER_METHOD, &cseq->method.name)
185                 || !PJSIP_IS_STATUS_IN_CLASS(status.code, 200)) {
186                 return;
187         }
188
189         contact_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
190         if (!contact_hdr) {
191                 return;
192         }
193
194         aor = find_aor(endpoint, contact_hdr->uri);
195         if (!aor || !aor->support_path || add_supported(tdata)
196                 || path_get_string(tdata->pool, contact, &path_dup)) {
197                 return;
198         }
199
200         path_hdr = pjsip_generic_string_hdr_create(tdata->pool, &PATH_NAME, &path_dup);
201         if (!path_hdr) {
202                 return;
203         }
204
205         pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)path_hdr);
206 }
207
208 static void path_session_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
209 {
210         path_outgoing_response(session->endpoint, session->contact, tdata);
211 }
212
213 static struct ast_sip_supplement path_supplement = {
214         .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
215         .outgoing_request = path_outgoing_request,
216         .outgoing_response = path_outgoing_response,
217 };
218
219 static struct ast_sip_session_supplement path_session_supplement = {
220         .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
221         .outgoing_request = path_session_outgoing_request,
222         .outgoing_response = path_session_outgoing_response,
223 };
224
225 static int load_module(void)
226 {
227         if (ast_sip_register_supplement(&path_supplement)) {
228                 return AST_MODULE_LOAD_DECLINE;
229         }
230
231         if (ast_sip_session_register_supplement(&path_session_supplement)) {
232                 ast_sip_unregister_supplement(&path_supplement);
233                 return AST_MODULE_LOAD_DECLINE;
234         }
235
236         return AST_MODULE_LOAD_SUCCESS;
237 }
238
239 static int unload_module(void)
240 {
241         ast_sip_unregister_supplement(&path_supplement);
242         ast_sip_session_unregister_supplement(&path_session_supplement);
243         return 0;
244 }
245
246 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Path Header Support",
247                 .load = load_module,
248                 .unload = unload_module,
249                 .load_pri = AST_MODPRI_APP_DEPEND,
250 );