1 /*
2     Copyright (C) 2002-2008  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 #include "config.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <time.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <net/if.h>
35 #include <sys/ioctl.h>
36 
37 #ifdef HAVE_GETIFADDRS
38 # include <ifaddrs.h>
39 #endif
40 
41 #ifdef _SOLARIS2
42 # include <sys/sockio.h>
43 #endif
44 
45 #include <sys/types.h>
46 #include <pwd.h>
47 
48 #include <osipparser2/osip_parser.h>
49 
50 #include "siproxd.h"
51 #include "log.h"
52 
53 static char const ident[]="$Id: utils.c 536 2016-02-03 20:02:27Z hb9xar $";
54 
55 /* configuration storage */
56 extern struct siproxd_config configuration;
57 
58 extern int h_errno;
59 
60 
61 /*
62  * resolve a hostname and return in_addr
63  * handles its own little DNS cache.
64  *
65  * RETURNS
66  *	STS_SUCCESS on success
67  *	STS_FAILURE on failure
68  */
get_ip_by_host(char * hostname,struct in_addr * addr)69 int get_ip_by_host(char *hostname, struct in_addr *addr) {
70    int i, j, k, idx;
71    time_t t1, t2;
72    struct hostent *hostentry;
73 #if defined(HAVE_GETHOSTBYNAME_R)
74    struct hostent result_buffer;
75    char tmp[GETHOSTBYNAME_BUFLEN];
76 #endif
77    int error;
78    static struct {
79       time_t expires_timestamp;	/* time of expiration */
80       struct in_addr addr;	/* IP address or 0.0.0.0 if a bad entry */
81       char   error_count;	/* counts failed resolution attempts */
82       char   bad_entry;		/* != 0 if resolving failed */
83       char hostname[HOSTNAME_SIZE+1];
84    } dns_cache[DNS_CACHE_SIZE];
85    static int cache_initialized=0;
86 
87    if (hostname == NULL) {
88       ERROR("get_ip_by_host: NULL hostname requested");
89       return STS_FAILURE;
90    }
91 
92    if (addr == NULL) {
93       ERROR("get_ip_by_host: NULL in_addr passed");
94       return STS_FAILURE;
95    }
96 
97    /* check if passed string is already a plain IPv4 address */
98    if (utils_inet_aton(hostname, addr) > 0) {
99       /* is a plain IPv4 string - return the result directly, no lookup and cache */
100 //      DEBUGC(DBCLASS_BABBLE, "DNS lookup - shortcut, hostname is IP string [%s] -> [%s]",
101 //             hostname, utils_inet_ntoa(*addr));
102       return STS_SUCCESS;
103    }
104 
105    /* first time: initialize DNS cache */
106    if (cache_initialized == 0) {
107       DEBUGC(DBCLASS_DNS, "initializing DNS cache (%i entries)", DNS_CACHE_SIZE);
108       memset(dns_cache, 0, sizeof(dns_cache));
109       cache_initialized=1;
110    }
111 
112    time(&t1);
113    /* clean expired entries */
114    for (i=0; i<DNS_CACHE_SIZE; i++) {
115       if (dns_cache[i].hostname[0]=='\0') continue;
116       if ( (dns_cache[i].expires_timestamp) < t1 ) {
117          DEBUGC(DBCLASS_DNS, "cleaning DNS cache (entry %i)", i);
118          memset (&dns_cache[i], 0, sizeof(dns_cache[0]));
119       }
120    }
121 
122    /*
123     * search requested entry in cache
124     */
125    idx=0;
126    for (i=0; i<DNS_CACHE_SIZE; i++) {
127       if (dns_cache[i].hostname[0]=='\0') continue; /* empty */
128       if (strcasecmp(hostname, dns_cache[i].hostname) == 0) { /* match */
129          memcpy(addr, &dns_cache[i].addr, sizeof(struct in_addr));
130          if (dns_cache[i].bad_entry) {
131             DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisted from cache: %s",
132                    hostname);
133             return STS_FAILURE;
134          }
135          if (dns_cache[i].error_count > 0) {
136             DEBUGC(DBCLASS_DNS, "DNS lookup - previous resolution failed: %s"
137                    ", attempt %i", hostname, dns_cache[i].error_count);
138             idx=i;
139             break;
140          }
141          DEBUGC(DBCLASS_BABBLE, "DNS lookup - from cache: %s -> %s",
142                 hostname, utils_inet_ntoa(*addr));
143          return STS_SUCCESS;
144       }
145    }
146 
147    /* I did not find it in cache, so I have to resolve it */
148    error = 0;
149 
150    /* need to deal with reentrant versions of gethostbyname_r()
151     * as we may use threads... */
152 #if defined(HAVE_GETHOSTBYNAME_R)
153 
154    /* gethostbyname_r() with 3 arguments (e.g. osf/1) */
155    #if defined(HAVE_FUNC_GETHOSTBYNAME_R_3)
156    gethostbyname_r(hostname,		/* the FQDN */
157 		   &result_buffer,	/* the result buffer */
158 		   &hostentry
159 		   );
160    if (hostentry == NULL) error = h_errno;
161 
162    /* gethostbyname_r() with 5 arguments (e.g. solaris, linux libc5) */
163    #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5)
164    hostentry = gethostbyname_r(hostname,        /* the FQDN */
165 			       &result_buffer,  /* the result buffer */
166 			       tmp,
167 			       GETHOSTBYNAME_BUFLEN,
168 			       &error);
169 
170    /* gethostbyname_r() with 6 arguments (e.g. linux glibc) */
171    #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_6)
172    gethostbyname_r(hostname,        /* the FQDN */
173 		   &result_buffer,  /* the result buffer */
174 		   tmp,
175 		   GETHOSTBYNAME_BUFLEN,
176 		   &hostentry,
177 		   &error);
178    #else
179       #error "gethostbyname_r() with 3, 5 or 6 arguments supported only"
180    #endif
181 #elif defined(HAVE_GETHOSTBYNAME)
182    hostentry=gethostbyname(hostname);
183    if (hostentry == NULL) error = h_errno;
184 #else
185    #error "need gethostbyname() or gethostbyname_r()"
186 #endif
187    /* Here I have 'hostentry' and 'error' */
188 
189 
190    if (hostentry==NULL) {
191       /*
192        * Some errors just tell us that there was no IP resolvable.
193        * From the manpage:
194        *   HOST_NOT_FOUND
195        *      The specified host is unknown.
196        *   NO_ADDRESS or NO_DATA
197        *      The requested name is valid but does not have an IP
198        *      address.
199        */
200       if ((error == HOST_NOT_FOUND) ||
201           (error == NO_ADDRESS) ||
202           (error == NO_DATA)) {
203 #ifdef HAVE_HSTRERROR
204          DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i [%s]",
205                 hostname, h_errno, hstrerror(error));
206 #else
207          DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i",
208                 hostname, error);
209 #endif
210       } else {
211 #ifdef HAVE_HSTRERROR
212          ERROR("gethostbyname(%s) failed: h_errno=%i [%s]",
213                hostname, h_errno, hstrerror(h_errno));
214 #else
215          ERROR("gethostbyname(%s) failed: h_errno=%i",hostname, h_errno);
216 #endif
217       }
218    }
219 
220    if (hostentry) {
221       memcpy(addr, hostentry->h_addr, sizeof(struct in_addr));
222       DEBUGC(DBCLASS_DNS, "DNS lookup - resolved: %s -> %s",
223              hostname, utils_inet_ntoa(*addr));
224    }
225 
226    /* if we already have the entry, skip finding a new empty one */
227    if (idx == 0) {
228       /*
229        * find an empty slot in the cache
230        */
231       j=0;
232       k=0;
233       t1=INT_MAX;
234       t2=INT_MAX;
235       for (i=0; i<DNS_CACHE_SIZE; i++) {
236          if (dns_cache[i].hostname[0]=='\0') break;
237          if ((dns_cache[i].expires_timestamp < t1) &&
238              (dns_cache[i].bad_entry == 0)) {
239             /* remember oldest good entry */
240             t1=dns_cache[i].expires_timestamp;
241             j=i;
242          } else
243          if (dns_cache[i].expires_timestamp < t2) {
244             /* remember oldest bad entry */
245             t2=dns_cache[i].expires_timestamp;
246             k=i;
247          }
248       }
249       /* if no empty slot found, victimize oldest one.
250        * Give preference to the oldest "bad" entry if
251        * one exists */
252       if (i >= DNS_CACHE_SIZE) {
253          if (k > 0) i=k;
254          else       i=j;
255       }
256       idx=i;
257       memset(&dns_cache[idx], 0, sizeof(dns_cache[0]));
258    }
259 
260    /*
261     * store the result in the cache
262     */
263    DEBUGC(DBCLASS_DNS, "DNS lookup - store into cache, entry %i)", idx);
264    strncpy(dns_cache[idx].hostname, hostname, HOSTNAME_SIZE);
265    dns_cache[idx].expires_timestamp = time(NULL) + DNS_GOOD_AGE;
266    if (hostentry) {
267       memcpy(&dns_cache[idx].addr, addr, sizeof(struct in_addr));
268       dns_cache[idx].error_count = 0;
269       dns_cache[idx].bad_entry = 0;
270    } else {
271       dns_cache[idx].error_count++;
272       DEBUGC(DBCLASS_DNS, "DNS lookup - errcnt=%i", dns_cache[idx].error_count);
273       if (dns_cache[idx].error_count >= DNS_ATTEMPTS) {
274          DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisting entry");
275          dns_cache[idx].expires_timestamp = time(NULL) + DNS_BAD_AGE;
276          dns_cache[idx].bad_entry = 1;
277       }
278       return STS_FAILURE;
279    }
280    return STS_SUCCESS;
281 }
282 
283 
284 /*
285  * Secure enviroment:
286  * If running as root, put myself into a chroot jail and
287  * change UID/GID to user as requested in config file
288  */
secure_enviroment(void)289 void secure_enviroment (void) {
290    int sts;
291    struct passwd *passwd=NULL;
292 
293    DEBUGC(DBCLASS_CONFIG,"running w/uid=%i, euid=%i, gid=%i, egid=%i",
294           (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid());
295 
296    if ((getuid()==0) || (geteuid()==0)) {
297       /*
298        * preparation - after chrooting there will be NOTHING more around
299        */
300       if (configuration.user) passwd=getpwnam(configuration.user);
301 
302       /*
303        * change root directory into chroot jail
304        */
305       if (configuration.chrootjail) {
306          /* !!!
307           * Before chrooting I must at least once trigger the resolver
308           * as it loads some dynamic libraries. Once chrootet
309           * these libraries will *not* be found and gethostbyname()
310           * calls will simply fail (return NULL pointer and h_errno=0).
311           * Also (at least for FreeBSD) syslog() needs to be called
312           * before chroot()ing - this is done in main() by an INFO().
313           * Took me a while to figure THIS one out
314           */
315          struct in_addr dummy;
316          get_ip_by_host("localhost", &dummy);
317          DEBUGC(DBCLASS_CONFIG,"chrooting to %s",
318                 configuration.chrootjail);
319          sts = chroot(configuration.chrootjail);
320 	 if (sts != 0) DEBUGC(DBCLASS_CONFIG,"chroot(%s) failed: %s",
321 	                      configuration.chrootjail, strerror(errno));
322          sts=chdir("/");
323       }
324 
325       /*
326        * change user ID and group ID
327        */
328       if (passwd) {
329          DEBUGC(DBCLASS_CONFIG,"changing uid/gid to %s",
330                 configuration.user);
331          sts = setgid(passwd->pw_gid);
332          DEBUGC(DBCLASS_CONFIG,"changed gid to %i - %s",
333 	        (int)passwd->pw_gid, (sts==0)?"Ok":"Failed");
334 
335          sts = setegid(passwd->pw_gid);
336          DEBUGC(DBCLASS_CONFIG,"changed egid to %i - %s",
337 	        (int)passwd->pw_gid, (sts==0)?"Ok":"Failed");
338 
339          sts = seteuid(passwd->pw_uid);
340          DEBUGC(DBCLASS_CONFIG,"changed euid to %i - %s",
341 	        (int)passwd->pw_uid, (sts==0)?"Ok":"Failed");
342       }
343    }
344 }
345 
346 
347 /*
348  * get_interface_ip:
349  * fetches own IP address by interface INBOUND/OUTBOUND
350  * takes into account a possible outbound_host setting.
351  *
352  * STS_SUCCESS on returning a valid IP and interface is UP
353  * STS_FAILURE if interface is DOWN or other problem
354  */
get_interface_ip(int interface,struct in_addr * retaddr)355 int  get_interface_ip(int interface, struct in_addr *retaddr) {
356    int sts=STS_FAILURE;
357 
358    if ((interface == IF_OUTBOUND) &&
359               (configuration.outbound_host) &&
360               (strcmp(configuration.outbound_host, "")!=0)) {
361       DEBUGC(DBCLASS_DNS, "fetching outbound IP by HOSTNAME");
362       if (retaddr) {
363          sts = get_ip_by_host(configuration.outbound_host, retaddr);
364       } else {
365          sts = STS_SUCCESS;
366       }
367 
368    } else  {
369       sts = get_interface_real_ip(interface, retaddr);
370    }
371 
372    return sts;
373 }
374 
375 
376 /*
377  * get_interface_real_ip:
378  * fetches the real IP address of my interface INBOUND/OUTBOUND
379  *
380  * STS_SUCCESS on returning a valid IP and interface is UP
381  * STS_FAILURE if interface is DOWN or other problem
382  */
get_interface_real_ip(int interface,struct in_addr * retaddr)383 int  get_interface_real_ip(int interface, struct in_addr *retaddr) {
384    int sts=STS_FAILURE;
385    char *tmp=NULL;
386 
387       if (interface == IF_INBOUND) {
388          tmp = configuration.inbound_if;
389       } else if (interface == IF_OUTBOUND) {
390          tmp = configuration.outbound_if;
391       }
392 
393    if (tmp && (strcmp(tmp, "")!=0)) {
394       DEBUGC(DBCLASS_DNS, "fetching interface IP by INTERFACE [%i]", interface);
395       sts = get_ip_by_ifname(tmp, retaddr);
396       if (sts != STS_SUCCESS) {
397          ERROR("can't find interface %s - configuration error?", tmp);
398       }
399 
400    } else {
401       ERROR("Don't know what interface to look for - configuration error?");
402    }
403 
404    return sts;
405 }
406 
407 
408 /*
409  * get_ip_by_ifname:
410  * fetches own IP address by its interface name
411  *
412  * STS_SUCCESS on returning a valid IP and interface is UP
413  * STS_FAILURE if interface is DOWN or other problem
414  */
get_ip_by_ifname(char * ifname,struct in_addr * retaddr)415 int get_ip_by_ifname(char *ifname, struct in_addr *retaddr) {
416    struct in_addr ifaddr; /* resulting IP */
417    int i, j;
418    int ifflags=0, isup=0;
419    time_t t;
420    static struct {
421       time_t timestamp;
422       struct in_addr ifaddr;	/* IP */
423       int isup;			/* interface is UP */
424       char ifname[IFNAME_SIZE+1];
425    } ifaddr_cache[IFADR_CACHE_SIZE];
426    static int cache_initialized=0;
427 #ifdef HAVE_GETIFADDRS
428    struct ifaddrs *ifa;
429    struct ifaddrs *ifa_list;
430 #else
431    struct ifreq ifr;
432    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
433    int sockfd;
434 #endif
435 
436    if (ifname == NULL) {
437       WARN("get_ip_by_ifname: got NULL ifname passed - please check config"
438            "file ('if_inbound' and 'if_outbound')");
439       return STS_FAILURE;
440    }
441 
442    /* first time: initialize ifaddr cache */
443    if (cache_initialized == 0) {
444       DEBUGC(DBCLASS_DNS, "initializing ifaddr cache (%i entries)",
445              IFADR_CACHE_SIZE);
446       memset(ifaddr_cache, 0, sizeof(ifaddr_cache));
447       cache_initialized=1;
448    }
449 
450    if (retaddr) memset(retaddr, 0, sizeof(struct in_addr));
451 
452    time(&t);
453    /* clean expired entries */
454    for (i=0; i<IFADR_CACHE_SIZE; i++) {
455       if (ifaddr_cache[i].ifname[0]=='\0') continue;
456       if ( (ifaddr_cache[i].timestamp+IFADR_MAX_AGE) < t ) {
457          DEBUGC(DBCLASS_DNS, "cleaning ifaddr cache (entry %i)", i);
458          memset (&ifaddr_cache[i], 0, sizeof(ifaddr_cache[0]));
459       }
460    }
461 
462    /*
463     * search requested entry in cache
464     */
465    for (i=0; i<IFADR_CACHE_SIZE; i++) {
466       if (ifaddr_cache[i].ifname[0]=='\0') continue;
467       if (strcmp(ifname, ifaddr_cache[i].ifname) == 0) { /* match */
468          if (retaddr) memcpy(retaddr, &ifaddr_cache[i].ifaddr,
469                              sizeof(struct in_addr));
470          DEBUGC(DBCLASS_DNS, "ifaddr lookup - from cache: %s -> %s %s",
471 	        ifname, utils_inet_ntoa(ifaddr_cache[i].ifaddr),
472                 (ifaddr_cache[i].isup)? "UP":"DOWN");
473          return (ifaddr_cache[i].isup)? STS_SUCCESS: STS_FAILURE;
474       } /* if */
475    } /* for i */
476 
477    /* not found in cache, go and get it */
478 
479 #ifdef HAVE_GETIFADDRS
480    if (getifaddrs(&ifa_list)) {
481       ERROR("Error in getifaddrs: %s",strerror(errno));
482       return STS_FAILURE;
483    }
484 
485    i=0; /* use "found" marker */
486    for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) {
487       DEBUGC(DBCLASS_BABBLE,"getifaddrs - %s / %s, ifa_addr=%p, addrfamily=%i",
488              ifname, ifa->ifa_name,ifa->ifa_addr,
489              (ifa->ifa_addr)?ifa->ifa_addr->sa_family:-1);
490 
491       if (ifa && ifa->ifa_name && ifa->ifa_addr &&
492           ifa->ifa_addr->sa_family == AF_INET &&
493           strcmp(ifa->ifa_name, ifname) == 0) {
494          /* found the entry */
495          i=1;
496          memcpy(&ifaddr, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, sizeof(struct in_addr));
497          ifflags=ifa->ifa_flags;
498          DEBUGC(DBCLASS_BABBLE,"getifaddrs - MATCH, sin_addr=%s",
499                 utils_inet_ntoa(ifaddr));
500          break;
501       }
502    }
503    freeifaddrs(ifa_list);
504 
505    if (i==0) {
506       DEBUGC(DBCLASS_DNS,"Interface %s not found.", ifname);
507       return STS_FAILURE;
508    }
509 
510 #else
511    memset(&ifr, 0, sizeof(ifr));
512 
513    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
514       ERROR("Error in socket: %s",strerror(errno));
515       return STS_FAILURE;
516    }
517 
518 /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&6 ifname=0x%p",ifname);
519    strcpy(ifr.ifr_name, ifname);
520    sin->sin_family = AF_INET;
521 
522    /* get interface flags */
523    if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) != 0) {
524       ERROR("Error in ioctl SIOCGIFFLAGS: %s [%s]",
525             strerror(errno), ifname);
526       close(sockfd);
527       return STS_FAILURE;
528    }
529    ifflags=ifr.ifr_flags;
530 /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&7 ifname=0x%p",ifname);
531 
532    /* get address */
533    if(ioctl(sockfd, SIOCGIFADDR, &ifr) != 0) {
534       ERROR("Error in ioctl SIOCGIFADDR: %s (interface %s)",
535       strerror(errno), ifname);
536       close(sockfd);
537       return STS_FAILURE;
538    }
539 
540 /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&8 ifname=0x%p",ifname);
541    memcpy(&ifaddr, &sin->sin_addr, sizeof(struct in_addr));
542 /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&9 ifname=0x%p",ifname);
543    close(sockfd);
544 #endif
545 
546    if (ifflags & IFF_UP) isup=1;
547    else isup=0;
548 
549    DEBUGC(DBCLASS_DNS, "get_ip_by_ifname: if %s has IP:%s (flags=%x) %s",
550           ifname, utils_inet_ntoa(ifaddr), ifflags,
551           (isup)? "UP":"DOWN");
552 
553    /*
554     *find an empty slot in the cache
555     */
556    j=0;
557    for (i=0; i<IFADR_CACHE_SIZE; i++) {
558       if (ifaddr_cache[i].ifname[0]=='\0') break;
559       if (ifaddr_cache[i].timestamp < t) {
560          /* remember oldest entry */
561          t=ifaddr_cache[i].timestamp;
562 	 j=i;
563       }
564    }
565    /* if no empty slot found, take oldest one */
566    if (i >= IFADR_CACHE_SIZE) i=j;
567 
568    /*
569     * store the result in the cache
570     */
571    DEBUGC(DBCLASS_DNS, "ifname lookup - store into cache, entry %i)", i);
572    memset(&ifaddr_cache[i], 0, sizeof(ifaddr_cache[0]));
573    strncpy(ifaddr_cache[i].ifname, ifname, IFNAME_SIZE);
574    ifaddr_cache[i].timestamp=t;
575    memcpy(&ifaddr_cache[i].ifaddr, &ifaddr, sizeof(struct in_addr));
576    ifaddr_cache[i].isup=isup;
577 
578    if (retaddr) memcpy(retaddr, &ifaddr, sizeof(struct in_addr));
579 
580    return (isup)? STS_SUCCESS : STS_FAILURE;
581 }
582 
583 
584 /*
585  * utils_inet_ntoa:
586  * implements an inet_ntoa()
587  *
588  * Returns pointer to a STATIC character string.
589  * NOte: BE AWARE OF THE STATIC NATURE of the string! Never pass it as
590  * calling argument to a function and use it immediately or strcpy()
591  * it into a buffer.
592  * !! Any subsequent call to this function will DESTROY the previous
593  * !! value - and may result in very strange effects like magically
594  * !! changing variable value (that has been passed to a function)
595  * Been there, seen that, so TAKE CARE!
596  */
utils_inet_ntoa(struct in_addr in)597 char *utils_inet_ntoa(struct in_addr in) {
598 #if defined(HAVE_INET_NTOP)
599    static char string[INET_ADDRSTRLEN];
600    if ((inet_ntop(AF_INET, &in, string, INET_ADDRSTRLEN)) == NULL) {
601       ERROR("inet_ntop() failed: %s",strerror(errno));
602       string[0]='\0';
603    }
604    return string;
605 #elif defined(HAVE_INET_NTOA)
606    return inet_ntoa(in);
607 #else
608 #error "need inet_ntop() or inet_ntoa()"
609 #endif
610 }
611 
612 
613 /*
614  * utils_inet_aton:
615  * implements an inet_aton()
616  *
617  * converts the string in *cp and stores it into inp
618  * Returns > 0 on success
619  */
utils_inet_aton(const char * cp,struct in_addr * inp)620 int  utils_inet_aton(const char *cp, struct in_addr *inp) {
621 #if defined(HAVE_INET_PTON)
622    return inet_pton (AF_INET, cp, inp);
623 #elif defined(HAVE_INET_ATON)
624    return inet_aton(cp, inp);
625 #else
626 #error "need inet_pton() or inet_aton()"
627 #endif
628 }
629 
630 /*
631  * Create the PID file
632  */
createpidfile(char * pidfilename)633 int createpidfile(char *pidfilename) {
634    FILE *f = NULL;
635    int sts;
636    DEBUGC(DBCLASS_CONFIG,"creating PID file [%s]", pidfilename);
637    sts=unlink(pidfilename);
638    if ((sts==0) || (errno == ENOENT)) {
639       if ((f=fopen(pidfilename, "w"))) {
640          fprintf(f,"%i\n",(int)getpid());
641          fclose(f);
642       } else {
643          WARN("couldn't create new PID file: %s", strerror(errno));
644          return STS_FAILURE;
645       }
646    } else {
647       WARN("couldn't delete old PID file: %s", strerror(errno));
648       return STS_FAILURE;
649    }
650    return STS_SUCCESS;
651 }
652 
653 
654 /*
655  * compare_client_id:
656  * Compares two client_id_t structures. If both have the Contact item
657  * defined (not NULL), then compare it and return.
658  * If one (or both) do NOT have the contact item defined, then
659  * fall back on comparing the from_ip (IP address).
660  *
661  * returns:
662  * STS_SUCCESS on match
663  * STS_FAILURE on no match
664  */
compare_client_id(client_id_t cid1,client_id_t cid2)665 int  compare_client_id(client_id_t cid1, client_id_t cid2) {
666 
667    /* Prio 1: Contact - if present in both structures */
668    if ((cid1.idstring[0] != '\0') && (cid2.idstring[0] != '\0')) {
669       if (strncmp(cid1.idstring, cid2.idstring, CLIENT_ID_SIZE) == 0) {
670          DEBUGC(DBCLASS_BABBLE, "compare_client_id: contact match [%s]",
671                 cid1.idstring);
672          return STS_SUCCESS;
673       }
674       DEBUGC(DBCLASS_BABBLE, "compare_client_id: contact NO match [%s<->%s]",
675              cid1.idstring, cid2.idstring);
676       return STS_FAILURE;
677    }
678 
679    /* Prio 2: IP (always present) - fallback, if no ID string present. */
680    if (memcmp(&cid1.from_ip, &cid2.from_ip, sizeof(struct in_addr)) == 0) {
681       DEBUGC(DBCLASS_BABBLE, "compare_client_id: IP match [%s]",
682              utils_inet_ntoa(cid1.from_ip));
683       return STS_SUCCESS;
684    }
685 
686    DEBUGC(DBCLASS_BABBLE, "compare_client_id: no match");
687    return STS_FAILURE;
688 }
689 
690 
691 /*
692  * check a sockaddr_in structure for bein zero / non-zero
693  */
is_empty_sockaddr(struct sockaddr_in * sockaddr)694 int  is_empty_sockaddr(struct sockaddr_in *sockaddr) {
695    int i;
696    char *p=(char*)sockaddr;
697 
698    for (i=0; i < sizeof(struct sockaddr_in); i++) {
699       if (p[i] != 0x00) { return STS_FAILURE; }
700    }
701    return STS_SUCCESS;
702 }
703