📄 utils.c
字号:
/* Copyright (C) 2002-2008 Thomas Ries <tries@gmx.net> This file is part of Siproxd. Siproxd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Siproxd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Siproxd; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "config.h"#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <limits.h>#include <time.h>#include <signal.h>#include <string.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <net/if.h>#include <sys/ioctl.h>#ifdef _SOLARIS2# include <sys/sockio.h>#endif#include <sys/types.h>#include <pwd.h>#include <osipparser2/osip_parser.h>#include "siproxd.h"#include "log.h"static char const ident[]="$Id: utils.c,v 1.53 2008/01/19 16:07:14 hb9xar Exp $";/* configuration storage */extern struct siproxd_config configuration;extern int h_errno;/* * resolve a hostname and return in_addr * handles its own little DNS cache. * * RETURNS * STS_SUCCESS on success * STS_FAILURE on failure */int get_ip_by_host(char *hostname, struct in_addr *addr) { int i, j, k, idx; time_t t1, t2; struct hostent *hostentry;#if defined(HAVE_GETHOSTBYNAME_R) struct hostent result_buffer; char tmp[GETHOSTBYNAME_BUFLEN];#endif int error; static struct { time_t expires_timestamp; /* time of expiration */ struct in_addr addr; /* IP address or 0.0.0.0 if a bad entry */ char error_count; /* counts failed resolution attempts */ char bad_entry; /* != 0 if resolving failed */ char hostname[HOSTNAME_SIZE+1]; } dns_cache[DNS_CACHE_SIZE]; static int cache_initialized=0; if (hostname == NULL) { ERROR("get_ip_by_host: NULL hostname requested"); return STS_FAILURE; } if (addr == NULL) { ERROR("get_ip_by_host: NULL in_addr passed"); return STS_FAILURE; } /* first time: initialize DNS cache */ if (cache_initialized == 0) { DEBUGC(DBCLASS_DNS, "initializing DNS cache (%i entries)", DNS_CACHE_SIZE); memset(dns_cache, 0, sizeof(dns_cache)); cache_initialized=1; } time(&t1); /* clean expired entries */ for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') continue; if ( (dns_cache[i].expires_timestamp) < t1 ) { DEBUGC(DBCLASS_DNS, "cleaning DNS cache (entry %i)", i); memset (&dns_cache[i], 0, sizeof(dns_cache[0])); } } /* * search requested entry in cache */ idx=0; for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') continue; /* empty */ if (strcasecmp(hostname, dns_cache[i].hostname) == 0) { /* match */ memcpy(addr, &dns_cache[i].addr, sizeof(struct in_addr)); if (dns_cache[i].bad_entry) { DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisted from cache: %s", hostname); return STS_FAILURE; } if (dns_cache[i].error_count > 0) { DEBUGC(DBCLASS_DNS, "DNS lookup - previous resolution failed: %s" ", attempt %i", hostname, dns_cache[i].error_count); idx=i; break; } DEBUGC(DBCLASS_DNS, "DNS lookup - from cache: %s -> %s", hostname, utils_inet_ntoa(*addr)); return STS_SUCCESS; } } /* I did not find it in cache, so I have to resolve it */ error = 0; /* need to deal with reentrant versions of gethostbyname_r() * as we may use threads... */#if defined(HAVE_GETHOSTBYNAME_R) /* gethostbyname_r() with 3 arguments (e.g. osf/1) */ #if defined(HAVE_FUNC_GETHOSTBYNAME_R_3) gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ &hostentry ); if (hostentry == NULL) error = h_errno; /* gethostbyname_r() with 5 arguments (e.g. solaris, linux libc5) */ #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5) hostentry = gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ tmp, GETHOSTBYNAME_BUFLEN, &error); /* gethostbyname_r() with 6 arguments (e.g. linux glibc) */ #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_6) gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ tmp, GETHOSTBYNAME_BUFLEN, &hostentry, &error); #else #error "gethostbyname_r() with 3, 5 or 6 arguments supported only" #endif #elif defined(HAVE_GETHOSTBYNAME) hostentry=gethostbyname(hostname); if (hostentry == NULL) error = h_errno;#else #error "need gethostbyname() or gethostbyname_r()"#endif /* Here I have 'hostentry' and 'error' */ if (hostentry==NULL) { /* * Some errors just tell us that there was no IP resolvable. * From the manpage: * HOST_NOT_FOUND * The specified host is unknown. * NO_ADDRESS or NO_DATA * The requested name is valid but does not have an IP * address. */ if ((error == HOST_NOT_FOUND) || (error == NO_ADDRESS) || (error == NO_DATA)) {#ifdef HAVE_HSTRERROR DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i [%s]", hostname, h_errno, hstrerror(error));#else DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i", hostname, error);#endif } else {#ifdef HAVE_HSTRERROR ERROR("gethostbyname(%s) failed: h_errno=%i [%s]", hostname, h_errno, hstrerror(h_errno));#else ERROR("gethostbyname(%s) failed: h_errno=%i",hostname, h_errno);#endif } } if (hostentry) { memcpy(addr, hostentry->h_addr, sizeof(struct in_addr)); DEBUGC(DBCLASS_DNS, "DNS lookup - resolved: %s -> %s", hostname, utils_inet_ntoa(*addr)); } /* if we already have the entry, skip finding a new empty one */ if (idx == 0) { /* * find an empty slot in the cache */ j=0; k=0; t1=INT_MAX; t2=INT_MAX; for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') break; if ((dns_cache[i].expires_timestamp < t1) && (dns_cache[i].bad_entry == 0)) { /* remember oldest good entry */ t1=dns_cache[i].expires_timestamp; j=i; } else if (dns_cache[i].expires_timestamp < t2) { /* remember oldest bad entry */ t2=dns_cache[i].expires_timestamp; k=i; } } /* if no empty slot found, victimize oldest one. * Give preference to the oldest "bad" entry if * one exists */ if (i >= DNS_CACHE_SIZE) { if (k > 0) i=k; else i=j; } idx=i; memset(&dns_cache[idx], 0, sizeof(dns_cache[0])); } /* * store the result in the cache */ DEBUGC(DBCLASS_DNS, "DNS lookup - store into cache, entry %i)", idx); strncpy(dns_cache[idx].hostname, hostname, HOSTNAME_SIZE); dns_cache[idx].expires_timestamp = time(NULL) + DNS_GOOD_AGE; if (hostentry) { memcpy(&dns_cache[idx].addr, addr, sizeof(struct in_addr)); dns_cache[idx].error_count = 0; dns_cache[idx].bad_entry = 0; } else { dns_cache[idx].error_count++; DEBUGC(DBCLASS_DNS, "DNS lookup - errcnt=%i", dns_cache[idx].error_count); if (dns_cache[idx].error_count >= DNS_ATTEMPTS) { DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisting entry"); dns_cache[idx].expires_timestamp = time(NULL) + DNS_BAD_AGE; dns_cache[idx].bad_entry = 1; } return STS_FAILURE; } return STS_SUCCESS;}/* * Secure enviroment: * If running as root, put myself into a chroot jail and * change UID/GID to user as requested in config file */void secure_enviroment (void) { int sts; struct passwd *passwd=NULL; DEBUGC(DBCLASS_CONFIG,"running w/uid=%i, euid=%i, gid=%i, egid=%i", (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid()); if ((getuid()==0) || (geteuid()==0)) { /* * preparation - after chrooting there will be NOTHING more around */ if (configuration.user) passwd=getpwnam(configuration.user); /* * change root directory into chroot jail */ if (configuration.chrootjail) { /* !!! * Before chrooting I must at least once trigger the resolver * as it loads some dynamic libraries. Once chrootet * these libraries will *not* be found and gethostbyname() * calls will simply fail (return NULL pointer and h_errno=0). * Also (at least for FreeBSD) syslog() needs to be called * before chroot()ing - this is done in main() by an INFO(). * Took me a while to figure THIS one out */ struct in_addr dummy; get_ip_by_host("localhost", &dummy); DEBUGC(DBCLASS_CONFIG,"chrooting to %s", configuration.chrootjail); sts = chroot(configuration.chrootjail); if (sts != 0) DEBUGC(DBCLASS_CONFIG,"chroot(%s) failed: %s", configuration.chrootjail, strerror(errno)); chdir("/"); } /* * change user ID and group ID */ if (passwd) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -