Asterisk - The Open Source Telephony Project GIT-master-0bf3178
res_pjsip_path.c
Go to the documentation of this file.
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 <depend>res_pjsip_session</depend>
23 <support_level>core</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <pjsip.h>
29#include <pjsip_ua.h>
30
31#include "asterisk/res_pjsip.h"
33#include "asterisk/module.h"
34#include "asterisk/strings.h"
35
36static const pj_str_t PATH_NAME = { "Path", 4 };
37static pj_str_t PATH_SUPPORTED_NAME = { "path", 4 };
38
39static struct ast_sip_aor *find_aor(struct ast_sip_contact *contact)
40{
41 if (!contact) {
42 return NULL;
43 }
44 if (ast_strlen_zero(contact->aor)) {
45 return NULL;
46 }
47
48 return ast_sip_location_retrieve_aor(contact->aor);
49}
50
51static struct ast_sip_aor *find_aor2(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
52{
53 char *configured_aors, *aor_name;
54 const pj_str_t *uri_username;
55 const pj_str_t *uri_hostname;
56 char *domain_name;
57 char *username;
58 struct ast_str *id = NULL;
59
60 if (ast_strlen_zero(endpoint->aors)) {
61 return NULL;
62 }
63
64 uri_hostname = ast_sip_pjsip_uri_get_hostname(uri);
65 domain_name = ast_alloca(uri_hostname->slen + 1);
66 ast_copy_pj_str(domain_name, uri_hostname, uri_hostname->slen + 1);
67
68 uri_username = ast_sip_pjsip_uri_get_username(uri);
69 username = ast_alloca(uri_username->slen + 1);
70 ast_copy_pj_str(username, uri_username, uri_username->slen + 1);
71
72 /*
73 * We may want to match without any user options getting
74 * in the way.
75 */
77
78 configured_aors = ast_strdupa(endpoint->aors);
79
80 /* Iterate the configured AORs to see if the user or the user+domain match */
81 while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
82 struct ast_sip_domain_alias *alias = NULL;
83
84 if (ast_strlen_zero(aor_name)) {
85 continue;
86 }
87
88 if (!strcmp(username, aor_name)) {
89 break;
90 }
91
92 if (!id && !(id = ast_str_create(strlen(username) + uri_hostname->slen + 2))) {
93 aor_name = NULL;
94 break;
95 }
96
97 ast_str_set(&id, 0, "%s@", username);
98 if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
99 ast_str_append(&id, 0, "%s", alias->domain);
100 ao2_cleanup(alias);
101 } else {
102 ast_str_append(&id, 0, "%s", domain_name);
103 }
104
105 if (!strcmp(aor_name, ast_str_buffer(id))) {
106 break;
107 }
108 }
109 ast_free(id);
110
111 if (ast_strlen_zero(aor_name)) {
112 return NULL;
113 }
114
115 return ast_sip_location_retrieve_aor(aor_name);
116}
117
118static struct ast_sip_contact *find_contact(struct ast_sip_aor *aor, pjsip_uri *uri)
119{
120 struct ao2_iterator it_contacts;
121 struct ast_sip_contact *contact;
122 char contact_buf[512];
123 int contact_buf_len;
124 int res = 0;
125
126 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
127
128 if (!(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
129 /* No contacts are available, skip it as well */
130 return NULL;
131 } else if (!ao2_container_count(contacts)) {
132 /* We were given a container but no contacts are in it... */
133 return NULL;
134 }
135
136 contact_buf_len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, uri, contact_buf, 512);
137 contact_buf[contact_buf_len] = '\0';
138
139 it_contacts = ao2_iterator_init(contacts, 0);
140 for (; (contact = ao2_iterator_next(&it_contacts)); ao2_ref(contact, -1)) {
141 if (!strcmp(contact_buf, contact->uri)) {
142 res = 1;
143 break;
144 }
145 }
146 ao2_iterator_destroy(&it_contacts);
147 if (!res) {
148 return NULL;
149 }
150 return contact;
151}
152
153
154/*!
155 * \brief Get the path string associated with this contact and tdata
156 *
157 * \param pool
158 * \param contact The URI identifying the associated contact
159 * \param path_str The place to store the retrieved path information
160 *
161 * \retval zero on success
162 * \retval non-zero on failure or no available path information
163 */
164static int path_get_string(pj_pool_t *pool, struct ast_sip_contact *contact, pj_str_t *path_str)
165{
166 if (!contact || ast_strlen_zero(contact->path)) {
167 return -1;
168 }
169
170 *path_str = pj_strdup3(pool, contact->path);
171 return 0;
172}
173
174static int add_supported(pjsip_tx_data *tdata)
175{
176 pjsip_supported_hdr *hdr;
177 int i;
178
179 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
180 if (!hdr) {
181 /* insert a new Supported header */
182 hdr = pjsip_supported_hdr_create(tdata->pool);
183 if (!hdr) {
184 return -1;
185 }
186
187 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
188 }
189
190 /* Don't add the value if it's already there */
191 for (i = 0; i < hdr->count; ++i) {
192 if (pj_stricmp(&hdr->values[i], &PATH_SUPPORTED_NAME) == 0) {
193 return 0;
194 }
195 }
196
197 if (hdr->count >= PJSIP_GENERIC_ARRAY_MAX_COUNT) {
198 return -1;
199 }
200
201 /* add on to the existing Supported header */
202 pj_strassign(&hdr->values[hdr->count++], &PATH_SUPPORTED_NAME);
203
204 return 0;
205}
206
207/*!
208 * \internal
209 * \brief Adds a Route header to an outgoing request if
210 * path information is available.
211 *
212 * \param endpoint The endpoint with which this request is associated
213 * \param contact The contact to which this request is being sent
214 * \param tdata The outbound request
215 */
216static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
217{
219
220 if (!endpoint) {
221 return;
222 }
223
224 aor = find_aor(contact);
225 if (!aor) {
226 aor = find_aor2(endpoint, tdata->msg->line.req.uri);
227 }
228 if (!aor || !aor->support_path) {
229 return;
230 }
231
232 if (add_supported(tdata)) {
233 return;
234 }
235
236 if (!contact) {
237 contact = find_contact(aor, tdata->msg->line.req.uri);
238 if (contact) {
239 if (!ast_strlen_zero(contact->path)) {
240 ast_sip_set_outbound_proxy(tdata, contact->path);
241 }
242 ao2_ref(contact, -1);
243 contact = NULL;
244 }
245 } else {
246 if (!ast_strlen_zero(contact->path)) {
247 ast_sip_set_outbound_proxy(tdata, contact->path);
248 }
249 }
250}
251
252static void path_session_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
253{
254 path_outgoing_request(session->endpoint, session->contact, tdata);
255}
256
257/*!
258 * \internal
259 * \brief Adds a path header to an outgoing 2XX response
260 *
261 * \param endpoint The endpoint to which the INVITE response is to be sent
262 * \param contact The contact to which the INVITE response is to be sent
263 * \param tdata The outbound INVITE response
264 */
265static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
266{
267 struct pjsip_status_line status = tdata->msg->line.status;
268 pj_str_t path_dup;
269 pjsip_generic_string_hdr *path_hdr;
270 RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
271 pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
272 const pj_str_t REGISTER_METHOD = {"REGISTER", 8};
273
274 if (!endpoint
275 || !pj_stristr(&REGISTER_METHOD, &cseq->method.name)
276 || !PJSIP_IS_STATUS_IN_CLASS(status.code, 200)) {
277 return;
278 }
279
280 aor = find_aor(contact);
281 if (!aor || !aor->support_path || add_supported(tdata)
282 || path_get_string(tdata->pool, contact, &path_dup)) {
283 return;
284 }
285
286 path_hdr = pjsip_generic_string_hdr_create(tdata->pool, &PATH_NAME, &path_dup);
287 if (!path_hdr) {
288 return;
289 }
290
291 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)path_hdr);
292}
293
294static void path_session_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
295{
296 path_outgoing_response(session->endpoint, session->contact, tdata);
297}
298
301 .outgoing_request = path_outgoing_request,
302 .outgoing_response = path_outgoing_response,
303};
304
307 .outgoing_request = path_session_outgoing_request,
308 .outgoing_response = path_session_outgoing_response,
309};
310
311static int load_module(void)
312{
315
317}
318
319static int unload_module(void)
320{
323 return 0;
324}
325
326AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Path Header Support",
327 .support_level = AST_MODULE_SUPPORT_CORE,
328 .load = load_module,
329 .unload = unload_module,
330 .load_pri = AST_MODPRI_APP_DEPEND,
331 .requires = "res_pjsip,res_pjsip_session",
jack_status_t status
Definition: app_jack.c:146
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_mansession session
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
char * strsep(char **str, const char *delims)
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement)
Unregister a an supplement to SIP out of dialog processing.
Definition: res_pjsip.c:1476
const pj_str_t * ast_sip_pjsip_uri_get_username(pjsip_uri *uri)
Get the user portion of the pjsip_uri.
Definition: res_pjsip.c:3477
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
void ast_sip_register_supplement(struct ast_sip_supplement *supplement)
Register a supplement to SIP out of dialog processing.
Definition: res_pjsip.c:1456
#define AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(str)
Truncate the URI user field options string if enabled.
Definition: res_pjsip.h:3350
@ AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL
Definition: res_pjsip.h:3185
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
int ast_sip_set_outbound_proxy(pjsip_tx_data *tdata, const char *proxy)
Set the outbound proxy for an outbound SIP message.
Definition: res_pjsip.c:1992
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
const pj_str_t * ast_sip_pjsip_uri_get_hostname(pjsip_uri *uri)
Get the host portion of the pjsip_uri.
Definition: res_pjsip.c:3496
struct ao2_container * ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
Retrieve all contacts currently available for an AOR.
Definition: location.c:247
static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
static void path_session_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
static struct ast_sip_aor * find_aor(struct ast_sip_contact *contact)
static struct ast_sip_contact * find_contact(struct ast_sip_aor *aor, pjsip_uri *uri)
static struct ast_sip_supplement path_supplement
static int path_get_string(pj_pool_t *pool, struct ast_sip_contact *contact, pj_str_t *path_str)
Get the path string associated with this contact and tdata.
static int add_supported(pjsip_tx_data *tdata)
static void path_session_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
static const pj_str_t PATH_NAME
static struct ast_sip_aor * find_aor2(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
static pj_str_t PATH_SUPPORTED_NAME
static struct ast_sip_session_supplement path_session_supplement
static int load_module(void)
static int unload_module(void)
#define ast_sip_session_register_supplement(supplement)
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
Definition: pjsip_session.c:63
#define NULL
Definition: resample.c:96
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
A SIP address of record.
Definition: res_pjsip.h:475
Contact associated with an address of record.
Definition: res_pjsip.h:389
const ast_string_field uri
Definition: res_pjsip.h:411
struct ast_sip_endpoint * endpoint
Definition: res_pjsip.h:421
const ast_string_field aor
Definition: res_pjsip.h:411
const ast_string_field path
Definition: res_pjsip.h:411
const ast_string_field domain
Definition: res_pjsip.h:320
An entity with which Asterisk communicates.
Definition: res_pjsip.h:958
const ast_string_field aors
Definition: res_pjsip.h:987
A supplement to SIP message processing.
enum ast_sip_supplement_priority priority
A structure describing a SIP session.
A supplement to SIP message processing.
Definition: res_pjsip.h:3197
enum ast_sip_supplement_priority priority
Definition: res_pjsip.h:3201
Support for dynamic strings.
Definition: strings.h:623
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941