Revert "Revert "and added files""
[bcm963xx.git] / userapps / opensource / siproxd / src / register.c
1 /*
2     Copyright (C) 2002-2005  Thomas Ries <tries@gmx.net>
3
4     This file is part of Siproxd.
5     
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.
10     
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.
15     
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 
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <time.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/types.h>
29
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #include <osipparser2/osip_parser.h>
34
35 #include "siproxd.h"
36 #include "log.h"
37
38 static char const ident[]="$Id: register.c,v 1.46 2005/01/08 10:05:12 hb9xar Exp $";
39
40 /* configuration storage */
41 extern struct siproxd_config configuration;
42
43 struct urlmap_s urlmap[URLMAP_SIZE];            /* URL mapping table     */
44
45 extern int errno;
46 /*
47  * initialize the URL mapping table
48  */
49 void register_init(void) {
50    FILE *stream;
51    int sts, size, i;
52    char buff[128];
53
54    memset(urlmap, 0, sizeof(urlmap));
55
56    if (configuration.registrationfile) {
57       stream = fopen(configuration.registrationfile, "r");
58
59       if (!stream) {
60          /*
61           * the file does not exist, or the size was incorrect,
62           * delete it and start from scratch
63           */
64          unlink(configuration.registrationfile);
65          WARN("registration file not found, starting with empty table");
66       } else {
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);
75
76                #define R(X) {\
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) {\
82                   size = strlen(buff);\
83                   X    =(char*)malloc(size+1);\
84                   sts=sscanf(buff,"%s",X);\
85                } else {\
86                   X = NULL;\
87                }\
88                }
89
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);
102             }
103          }
104          fclose(stream);
105       }
106    }
107    return;
108 }
109
110
111 /*
112  * shut down the URL mapping table
113  */
114 void register_shut(void) {
115    int i;
116    FILE *stream;
117
118    if (configuration.registrationfile) {
119       /* write urlmap back to file */
120       stream = fopen(configuration.registrationfile, "w+");
121       if (!stream) {
122          /* try to unlink it and open again */
123          unlink(configuration.registrationfile);
124          stream = fopen(configuration.registrationfile, "w+");
125
126          /* open file for write failed, complain */
127          if (!stream) {
128             ERROR("unable to write registration file");
129             return;
130          }
131       }
132
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:"");
137
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);
150          }
151       }
152       fclose(stream);
153    }
154    return;
155 }
156
157
158 /*
159  * handles register requests and updates the URL mapping table
160  *
161  * RETURNS:
162  *    STS_SUCCESS : successfully registered
163  *    STS_FAILURE : registration failed
164  *    STS_NEED_AUTH : authentication needed
165  */
166 int register_client(sip_ticket_t *ticket, int force_lcl_masq) {
167    int i, j, n, sts;
168    int expires;
169    time_t time_now;
170    osip_uri_t *url1_to, *url1_contact=NULL;
171    osip_uri_t *url2_to;
172    osip_header_t *expires_hdr;
173    osip_uri_param_t *expires_param=NULL;
174    
175    /*
176     * Authorization - do only if I'm not just acting as outbound proxy
177     * but am ment to be the registrar
178     */
179    if (force_lcl_masq == 0) {
180       /*
181        * RFC 3261, Section 16.3 step 6
182        * Proxy Behavior - Request Validation - Proxy-Authorization
183        */
184       sts = authenticate_proxy(ticket);
185       if (sts == STS_FAILURE) {
186          /* failed */
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);
191          return STS_FAILURE;
192       } else if (sts == STS_NEED_AUTH) {
193          /* needed */
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;
198       }
199    }
200
201 /*
202    fetch 1st Via entry and remember this address. Incoming requests
203    for the registered address have to be passed on to that host.
204
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
209                      made by this UA"
210    
211    => Mapping is
212    To: <1--n> Contact
213    
214 */
215    time(&time_now);
216
217    DEBUGC(DBCLASS_BABBLE,"sip_register:");
218
219    /* evaluate Expires Header field */
220    osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);
221
222   /*
223    * look for an Contact expires parameter - in case of REGISTER
224    * these two are equal. The Contact expires has higher priority!
225    */
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);
231    }
232
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);
239    } else {
240       char tmp[16];
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);
247    }
248
249    url1_to=ticket->sipmsg->to->url;
250
251
252
253    /*
254     * REGISTER
255     */
256    if (expires > 0) {
257       /*
258        * First make sure, we have a prober Contact header:
259        *  - url
260        *  - url -> hostname
261        *
262        * Libosip parses an:
263        * "Contact: *"
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}
267        */
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;
272       }
273       if ((url1_contact == NULL) || (url1_contact->host == NULL)) {
274          /* Don't have required Contact fields */
275          ERROR("tried registration with empty Contact header");
276          return STS_FAILURE;
277       }
278          
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*",
282           expires);
283
284       /*
285        * Update registration. There are two possibilities:
286        * - already registered, then update the existing record
287        * - not registered, then create a new record
288        */
289
290       j=-1;
291       for (i=0; i<URLMAP_SIZE; i++) {
292          if (urlmap[i].active == 0) {
293             if (j < 0) j=i; /* remember first hole */
294             continue;
295          }
296
297          url2_to=urlmap[i].reg_url;
298
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 "
302                    "slot=%i, exp=%li",
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);
308             break;
309          }
310       }
311
312       if ( (j < 0) && (i >= URLMAP_SIZE) ) {
313          /* oops, no free entries left... */
314          ERROR("URLMAP is full - registration failed");
315          return STS_FAILURE;
316       }
317
318       if (i >= URLMAP_SIZE) {
319          /* entry not existing, create new one */
320          i=j;
321
322          /* write entry */
323          urlmap[i].active=1;
324          /* Contact: field */
325          osip_uri_clone( ((osip_contact_t*)
326                          (ticket->sipmsg->contacts->node->element))->url, 
327                          &urlmap[i].true_url);
328          /* To: field */
329          osip_uri_clone( ticket->sipmsg->to->url, 
330                     &urlmap[i].reg_url);
331
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*",
337                 i);
338
339          /*
340           * try to figure out if we ought to do some masquerading
341           */
342          osip_uri_clone( ticket->sipmsg->to->url, 
343                          &urlmap[i].masq_url);
344
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!");
348             n=0;
349          }
350
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)
357                break;
358          }
359          if (j<n) { 
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]);
369          }
370
371          /*
372           * for transparent proxying: force device to be masqueraded
373           * as with the outbound IP
374           */
375          if (force_lcl_masq) {
376             struct in_addr addr;
377             char *addrstr;
378
379             if (get_ip_by_ifname(configuration.outbound_if, &addr) !=
380                 STS_SUCCESS) {
381                ERROR("can't find outbound interface %s - configuration error?",
382                      configuration.outbound_if);
383                return STS_FAILURE;
384             }
385
386             /* host part */
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*",
392                    addrstr);
393             urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
394                                              strlen(addrstr)+1);
395             strcpy(urlmap[i].masq_url->host, addrstr);
396
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);
402             }
403          }
404
405       } else { /* if new entry */
406       /*
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
409        * we get an REGISTER
410        */
411          /* Contact: field */
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);
416          /* To: field */
417          osip_uri_free(urlmap[i].reg_url);
418          osip_uri_clone( ticket->sipmsg->to->url, 
419                     &urlmap[i].reg_url);
420       }
421       /* give some safety margin for the next update */
422       if (expires > 0) expires+=30;
423
424       /* update registration timeout */
425       urlmap[i].expires=time_now+expires;
426
427    /*
428     * un-REGISTER
429     */
430    } else { /* expires > 0 */
431       /*
432        * Remove registration
433        * Siproxd will ALWAYS remove ALL bindings for a given
434        * address-of-record
435        */
436       for (i=0; i<URLMAP_SIZE; i++) {
437          if (urlmap[i].active == 0) continue;
438
439          url2_to=urlmap[i].reg_url;
440
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);
445             urlmap[i].expires=0;
446             break;
447          }
448       }
449    }
450
451    return STS_SUCCESS;
452 }
453
454
455
456 /*
457  * cyclically called to do the aging of the URL mapping table entries
458  * and throw out expired entries.
459  */
460 void register_agemap(void) {
461    int i;
462    time_t t;
463    
464    time(&t);
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);
470          urlmap[i].active=0;
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);
475       }
476    }
477    return;
478 }
479
480
481 /*
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)
486  *
487  * RETURNS
488  *      STS_SUCCESS on success
489  *      STS_FAILURE on error
490  */
491 int register_response(sip_ticket_t *ticket, int flag) {
492    osip_message_t *response;
493    int code;
494    int sts;
495    osip_via_t *via;
496    int port;
497    char *buffer;
498    struct in_addr addr;
499    osip_header_t *expires_hdr;
500
501    /* ok -> 200, fail -> 503 */
502    switch (flag) {
503    case STS_SUCCESS:
504       code = 200;       /* OK */
505       break;
506    case STS_FAILURE:
507       code = 503;       /* failed */
508       break;
509    case STS_NEED_AUTH:
510       code = 407;       /* proxy authentication needed */
511       break;
512    default:
513       code = 503;       /* failed */
514       break;
515    }
516
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");
520       return STS_FAILURE;
521    }
522
523    /* insert the expiration header */
524    osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);
525    if (expires_hdr) {
526       osip_message_set_expires(response, expires_hdr->hvalue);
527    }
528
529    /* if we send back an proxy authentication needed, 
530       include the Proxy-Authenticate field */
531    if (code == 407) {
532       auth_include_authrq(ticket);
533    }
534
535    /* get the IP address from existing VIA header */
536    osip_message_get_via (response, 0, &via);
537    if (via == NULL) {
538       ERROR("register_response: Cannot send response - no via field");
539       return STS_FAILURE;
540    }
541
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]",
548                 via->host);
549          return STS_FAILURE;
550       }
551    }   
552
553    sts = osip_message_to_str(response, &buffer);
554    if (sts != 0) {
555       ERROR("register_response: msg_2char failed");
556       return STS_FAILURE;
557    }
558
559    /* send answer back */
560    if (via->port) {
561       port=atoi(via->port);
562    } else {
563       port=configuration.sip_listen_port;
564    }
565
566    sipsock_send(addr, port, ticket->protocol, buffer, strlen(buffer));
567
568    /* free the resources */
569    osip_message_free(response);
570    free(buffer);
571    return STS_SUCCESS;
572 }
573