Asterisk - The Open Source Telephony Project  21.4.1
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  <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"
32 #include "asterisk/res_pjsip_session.h"
33 #include "asterisk/module.h"
34 #include "asterisk/strings.h"
35 
36 static const pj_str_t PATH_NAME = { "Path", 4 };
37 static pj_str_t PATH_SUPPORTED_NAME = { "path", 4 };
38 
39 static 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 
51 static 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  */
76  AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(username);
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 
118 static 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  */
164 static 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 
174 static 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  */
216 static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
217 {
218  RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
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 
252 static 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  */
265 static 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 
294 static 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 
299 static struct ast_sip_supplement path_supplement = {
300  .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
301  .outgoing_request = path_outgoing_request,
302  .outgoing_response = path_outgoing_response,
303 };
304 
305 static struct ast_sip_session_supplement path_session_supplement = {
306  .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
307  .outgoing_request = path_session_outgoing_request,
308  .outgoing_response = path_session_outgoing_response,
309 };
310 
311 static int load_module(void)
312 {
313  ast_sip_register_supplement(&path_supplement);
314  ast_sip_session_register_supplement(&path_session_supplement);
315 
317 }
318 
319 static int unload_module(void)
320 {
321  ast_sip_unregister_supplement(&path_supplement);
322  ast_sip_session_unregister_supplement(&path_session_supplement);
323  return 0;
324 }
325 
326 AST_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",
332 );
struct ast_sip_endpoint * endpoint
Asterisk main include file. File version handling, generic pbx functions.
A SIP address of record.
Definition: res_pjsip.h:478
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
String manipulation functions.
const ast_string_field path
Definition: res_pjsip.h:414
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static pj_pool_t * pool
Global memory pool for configuration and timers.
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
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
A structure describing a SIP session.
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
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
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
An entity with which Asterisk communicates.
Definition: res_pjsip.h:949
unsigned int support_path
Definition: res_pjsip.h:502
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
Support for dynamic strings.
Definition: strings.h:623
Contact associated with an address of record.
Definition: res_pjsip.h:392
enum ast_sip_supplement_priority priority
A supplement to SIP message processing.
const ast_string_field aor
Definition: res_pjsip.h:414
const ast_string_field domain
Definition: res_pjsip.h:323
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 supplement to SIP message processing.
Definition: res_pjsip.h:3182
Generic container type.
const ast_string_field aors
Definition: res_pjsip.h:958
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
const ast_string_field uri
Definition: res_pjsip.h:414
struct ast_sip_contact * contact
Asterisk module definitions.
#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
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
enum ast_sip_supplement_priority priority
Definition: res_pjsip.h:3186