2 Copyright (C) 2002-2005 Thomas Ries <tries@gmx.net>
4 This file is part of Siproxd.
6 Siproxd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Siproxd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Siproxd; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <osipparser2/osip_parser.h>
38 static char const ident[]="$Id: register.c,v 1.46 2005/01/08 10:05:12 hb9xar Exp $";
40 /* configuration storage */
41 extern struct siproxd_config configuration;
43 struct urlmap_s urlmap[URLMAP_SIZE]; /* URL mapping table */
47 * initialize the URL mapping table
49 void register_init(void) {
54 memset(urlmap, 0, sizeof(urlmap));
56 if (configuration.registrationfile) {
57 stream = fopen(configuration.registrationfile, "r");
61 * the file does not exist, or the size was incorrect,
62 * delete it and start from scratch
64 unlink(configuration.registrationfile);
65 WARN("registration file not found, starting with empty table");
67 /* read the url table from file */
68 for (i=0;i < URLMAP_SIZE; i++) {
69 fgets(buff, sizeof(buff), stream);
70 sts=sscanf(buff, "***:%i:%i", &urlmap[i].active, &urlmap[i].expires);
71 if (urlmap[i].active) {
72 osip_uri_init(&urlmap[i].true_url);
73 osip_uri_init(&urlmap[i].masq_url);
74 osip_uri_init(&urlmap[i].reg_url);
77 fgets(buff, sizeof(buff), stream);\
78 buff[sizeof(buff)-1]='\0';\
79 if (strchr(buff, 10)) *strchr(buff, 10)='\0';\
80 if (strchr(buff, 13)) *strchr(buff, 13)='\0';\
81 if (strlen(buff) > 0) {\
83 X =(char*)malloc(size+1);\
84 sts=sscanf(buff,"%s",X);\
90 R(urlmap[i].true_url->scheme);
91 R(urlmap[i].true_url->username);
92 R(urlmap[i].true_url->host);
93 R(urlmap[i].true_url->port);
94 R(urlmap[i].masq_url->scheme);
95 R(urlmap[i].masq_url->username);
96 R(urlmap[i].masq_url->host);
97 R(urlmap[i].masq_url->port);
98 R(urlmap[i].reg_url->scheme);
99 R(urlmap[i].reg_url->username);
100 R(urlmap[i].reg_url->host);
101 R(urlmap[i].reg_url->port);
112 * shut down the URL mapping table
114 void register_shut(void) {
118 if (configuration.registrationfile) {
119 /* write urlmap back to file */
120 stream = fopen(configuration.registrationfile, "w+");
122 /* try to unlink it and open again */
123 unlink(configuration.registrationfile);
124 stream = fopen(configuration.registrationfile, "w+");
126 /* open file for write failed, complain */
128 ERROR("unable to write registration file");
133 for (i=0;i < URLMAP_SIZE; i++) {
134 fprintf(stream, "***:%i:%i\n", urlmap[i].active, urlmap[i].expires);
135 if (urlmap[i].active) {
136 #define W(X) fprintf(stream, "%s\n", (X)? X:"");
138 W(urlmap[i].true_url->scheme);
139 W(urlmap[i].true_url->username);
140 W(urlmap[i].true_url->host);
141 W(urlmap[i].true_url->port);
142 W(urlmap[i].masq_url->scheme);
143 W(urlmap[i].masq_url->username);
144 W(urlmap[i].masq_url->host);
145 W(urlmap[i].masq_url->port);
146 W(urlmap[i].reg_url->scheme);
147 W(urlmap[i].reg_url->username);
148 W(urlmap[i].reg_url->host);
149 W(urlmap[i].reg_url->port);
159 * handles register requests and updates the URL mapping table
162 * STS_SUCCESS : successfully registered
163 * STS_FAILURE : registration failed
164 * STS_NEED_AUTH : authentication needed
166 int register_client(sip_ticket_t *ticket, int force_lcl_masq) {
170 osip_uri_t *url1_to, *url1_contact=NULL;
172 osip_header_t *expires_hdr;
173 osip_uri_param_t *expires_param=NULL;
176 * Authorization - do only if I'm not just acting as outbound proxy
177 * but am ment to be the registrar
179 if (force_lcl_masq == 0) {
181 * RFC 3261, Section 16.3 step 6
182 * Proxy Behavior - Request Validation - Proxy-Authorization
184 sts = authenticate_proxy(ticket);
185 if (sts == STS_FAILURE) {
187 WARN("proxy authentication failed for %s@%s",
188 (ticket->sipmsg->to->url->username)?
189 ticket->sipmsg->to->url->username : "*NULL*",
190 ticket->sipmsg->to->url->host);
192 } else if (sts == STS_NEED_AUTH) {
194 DEBUGC(DBCLASS_REG,"proxy authentication needed for %s@%s",
195 ticket->sipmsg->to->url->username,
196 ticket->sipmsg->to->url->host);
197 return STS_NEED_AUTH;
202 fetch 1st Via entry and remember this address. Incoming requests
203 for the registered address have to be passed on to that host.
205 To: -> address to be registered
206 Contact: -> host is reachable there
207 Note: in case of un-REGISTER, the contact header may
208 contain '*' only - which means "all registrations
217 DEBUGC(DBCLASS_BABBLE,"sip_register:");
219 /* evaluate Expires Header field */
220 osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);
223 * look for an Contact expires parameter - in case of REGISTER
224 * these two are equal. The Contact expires has higher priority!
226 if (ticket->sipmsg->contacts && ticket->sipmsg->contacts->node &&
227 ticket->sipmsg->contacts->node->element) {
228 osip_contact_param_get_byname(
229 (osip_contact_t*) ticket->sipmsg->contacts->node->element,
230 EXPIRES, &expires_param);
233 if (expires_param && expires_param->gvalue) {
234 /* get expires from contact Header */
235 expires=atoi(expires_param->gvalue);
236 } else if (expires_hdr && expires_hdr->hvalue) {
237 /* get expires from expires Header */
238 expires=atoi(expires_hdr->hvalue);
241 /* it seems, the expires field is not present everywhere... */
242 DEBUGC(DBCLASS_REG,"no 'expires' header found - set time to %i sec",
243 configuration.default_expires);
244 expires=configuration.default_expires;
245 sprintf(tmp,"%i",expires);
246 osip_message_set_expires(ticket->sipmsg, tmp);
249 url1_to=ticket->sipmsg->to->url;
258 * First make sure, we have a prober Contact header:
264 * the following way (Note: Display name!! and URL is NULL)
265 * (gdb) p *((osip_contact_t*)(sip->contacts->node->element))
266 * $5 = {displayname = 0x8af8848 "*", url = 0x0, gen_params = 0x8af8838}
268 if (ticket->sipmsg->contacts && ticket->sipmsg->contacts->node &&
269 ticket->sipmsg->contacts->node->element) {
270 url1_contact=((osip_contact_t*)
271 (ticket->sipmsg->contacts->node->element))->url;
273 if ((url1_contact == NULL) || (url1_contact->host == NULL)) {
274 /* Don't have required Contact fields */
275 ERROR("tried registration with empty Contact header");
279 DEBUGC(DBCLASS_REG,"register: %s@%s expires=%i seconds",
280 (url1_contact->username) ? url1_contact->username : "*NULL*",
281 (url1_contact->host) ? url1_contact->host : "*NULL*",
285 * Update registration. There are two possibilities:
286 * - already registered, then update the existing record
287 * - not registered, then create a new record
291 for (i=0; i<URLMAP_SIZE; i++) {
292 if (urlmap[i].active == 0) {
293 if (j < 0) j=i; /* remember first hole */
297 url2_to=urlmap[i].reg_url;
299 /* check address-of-record ("public address" of user) */
300 if (compare_url(url1_to, url2_to)==STS_SUCCESS) {
301 DEBUGC(DBCLASS_REG, "found entry for %s@%s <-> %s@%s at "
303 (url1_contact->username) ? url1_contact->username : "*NULL*",
304 (url1_contact->host) ? url1_contact->host : "*NULL*",
305 (url2_to->username) ? url2_to->username : "*NULL*",
306 (url2_to->host) ? url2_to->host : "*NULL*",
307 i, urlmap[i].expires-time_now);
312 if ( (j < 0) && (i >= URLMAP_SIZE) ) {
313 /* oops, no free entries left... */
314 ERROR("URLMAP is full - registration failed");
318 if (i >= URLMAP_SIZE) {
319 /* entry not existing, create new one */
325 osip_uri_clone( ((osip_contact_t*)
326 (ticket->sipmsg->contacts->node->element))->url,
327 &urlmap[i].true_url);
329 osip_uri_clone( ticket->sipmsg->to->url,
332 DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i",
333 (url1_contact->username) ? url1_contact->username : "*NULL*",
334 (url1_contact->host) ? url1_contact->host : "*NULL*",
335 (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "*NULL*",
336 (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*",
340 * try to figure out if we ought to do some masquerading
342 osip_uri_clone( ticket->sipmsg->to->url,
343 &urlmap[i].masq_url);
345 n=configuration.mask_host.used;
346 if (n != configuration.masked_host.used) {
347 ERROR("# of mask_host is not equal to # of masked_host in config!");
351 DEBUG("%i entries in MASK config table", n);
352 for (j=0; j<n; j++) {
353 DEBUG("compare [%s] <-> [%s]",configuration.mask_host.string[j],
354 ticket->sipmsg->to->url->host);
355 if (strcmp(configuration.mask_host.string[j],
356 ticket->sipmsg->to->url->host)==0)
360 /* we are masquerading this UA, replace the host part of the url */
361 DEBUGC(DBCLASS_REG,"masquerading UA %s@%s as %s@%s",
362 (url1_contact->username) ? url1_contact->username : "*NULL*",
363 (url1_contact->host) ? url1_contact->host : "*NULL*",
364 (url1_contact->username) ? url1_contact->username : "*NULL*",
365 configuration.masked_host.string[j]);
366 urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
367 strlen(configuration.masked_host.string[j])+1);
368 strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]);
372 * for transparent proxying: force device to be masqueraded
373 * as with the outbound IP
375 if (force_lcl_masq) {
379 if (get_ip_by_ifname(configuration.outbound_if, &addr) !=
381 ERROR("can't find outbound interface %s - configuration error?",
382 configuration.outbound_if);
387 addrstr = utils_inet_ntoa(addr);
388 DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s",
389 (url1_contact->username) ? url1_contact->username : "*NULL*",
390 (url1_contact->host) ? url1_contact->host : "*NULL*",
391 (url1_contact->username) ? url1_contact->username : "*NULL*",
393 urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
395 strcpy(urlmap[i].masq_url->host, addrstr);
397 /* port number if required */
398 if (configuration.sip_listen_port != SIP_PORT) {
399 urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16);
400 sprintf(urlmap[i].masq_url->port, "%i",
401 configuration.sip_listen_port);
405 } else { /* if new entry */
407 * Some phones (like BudgeTones *may* dynamically grab a SIP port
408 * so we might want to update the true_url and reg_url each time
412 osip_uri_free(urlmap[i].true_url);
413 osip_uri_clone( ((osip_contact_t*)
414 (ticket->sipmsg->contacts->node->element))->url,
415 &urlmap[i].true_url);
417 osip_uri_free(urlmap[i].reg_url);
418 osip_uri_clone( ticket->sipmsg->to->url,
421 /* give some safety margin for the next update */
422 if (expires > 0) expires+=30;
424 /* update registration timeout */
425 urlmap[i].expires=time_now+expires;
430 } else { /* expires > 0 */
432 * Remove registration
433 * Siproxd will ALWAYS remove ALL bindings for a given
436 for (i=0; i<URLMAP_SIZE; i++) {
437 if (urlmap[i].active == 0) continue;
439 url2_to=urlmap[i].reg_url;
441 if (compare_url(url1_to, url2_to)==STS_SUCCESS) {
442 DEBUGC(DBCLASS_REG, "removing registration for %s@%s at slot=%i",
443 (url2_to->username) ? url2_to->username : "*NULL*",
444 (url2_to->host) ? url2_to->host : "*NULL*", i);
457 * cyclically called to do the aging of the URL mapping table entries
458 * and throw out expired entries.
460 void register_agemap(void) {
465 DEBUGC(DBCLASS_BABBLE,"sip_agemap, t=%i",(int)t);
466 for (i=0; i<URLMAP_SIZE; i++) {
467 if ((urlmap[i].active == 1) && (urlmap[i].expires < t)) {
468 DEBUGC(DBCLASS_REG,"cleaned entry:%i %s@%s", i,
469 urlmap[i].masq_url->username, urlmap[i].masq_url->host);
471 osip_uri_free(urlmap[i].true_url);
472 osip_uri_free(urlmap[i].masq_url);
473 osip_uri_free(urlmap[i].reg_url);
474 // osip_via_free(urlmap[i].via);
482 * send answer to a registration request.
483 * flag = STS_SUCCESS -> positive answer (200)
484 * flag = STS_FAILURE -> negative answer (503)
485 * flag = STS_NEED_AUTH -> proxy authentication needed (407)
488 * STS_SUCCESS on success
489 * STS_FAILURE on error
491 int register_response(sip_ticket_t *ticket, int flag) {
492 osip_message_t *response;
499 osip_header_t *expires_hdr;
501 /* ok -> 200, fail -> 503 */
507 code = 503; /* failed */
510 code = 407; /* proxy authentication needed */
513 code = 503; /* failed */
517 /* create the response template */
518 if ((response=msg_make_template_reply(ticket, code))==NULL) {
519 ERROR("register_response: error in msg_make_template_reply");
523 /* insert the expiration header */
524 osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);
526 osip_message_set_expires(response, expires_hdr->hvalue);
529 /* if we send back an proxy authentication needed,
530 include the Proxy-Authenticate field */
532 auth_include_authrq(ticket);
535 /* get the IP address from existing VIA header */
536 osip_message_get_via (response, 0, &via);
538 ERROR("register_response: Cannot send response - no via field");
542 /* name resolution needed? */
543 if (utils_inet_aton(via->host,&addr) == 0) {
544 /* yes, get IP address */
545 sts = get_ip_by_host(via->host, &addr);
546 if (sts == STS_FAILURE) {
547 DEBUGC(DBCLASS_REG, "register_response: cannot resolve VIA [%s]",
553 sts = osip_message_to_str(response, &buffer);
555 ERROR("register_response: msg_2char failed");
559 /* send answer back */
561 port=atoi(via->port);
563 port=configuration.sip_listen_port;
566 sipsock_send(addr, port, ticket->protocol, buffer, strlen(buffer));
568 /* free the resources */
569 osip_message_free(response);