/*
** iplog_util.c - iplog utility functions.
** Copyright (C) 1999-2001 Ryan McCabe <odin@numb.org>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License, version 2,
** as published by the Free Software Foundation.
**
** This program 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 this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
**
** $Id: iplog_util.c,v 1.43 2001/01/01 16:02:14 odin Exp $
*/
#include <config.h>
#define _GNU_SOURCE
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <netdb.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <iplog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <pthread.h>
#include <iplog_options.h>
#include <iplog_dns.h>
typedef struct list {
struct list *next;
} list_t;
typedef struct dlist {
struct dlist *next;
struct dlist *prev;
} dlist_t;
#ifndef HAVE_DPRINTF
/*
** dprintf(3) if the system's libc lacks it.
** Works line printf on a file descriptor.
*/
# ifdef HAVE_VASPRINTF
int dprintf(int fd, const char *fmt, ...) {
va_list ap;
ssize_t ret;
u_char *buf;
va_start(ap, fmt);
vasprintf((char **) &buf, fmt, ap);
va_end(ap);
ret = write(fd, buf, strlen(buf));
free(buf);
return (ret);
}
# else /* !HAVE_VASPRINTF */
int dprintf(int fd, const char *fmt, ...) {
va_list ap;
ssize_t ret;
u_char buf[2048];
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
ret = write(fd, buf, strlen(buf));
return (ret);
}
# endif /* !HAVE_VASPRINTF */
#endif /* !HAVE_DPRINTF */
/*
** malloc(3) with error checking. Returns a pointer to the allocated memory.
*/
void *xmalloc(size_t size) {
void *ret = malloc(size);
if (ret == NULL)
fatal("Out of memory.");
return (ret);
}
/*
** calloc(3) with error checking. Returns a pointer to the allocated memory.
*/
void *xcalloc(size_t nmemb, size_t size) {
void *p = calloc(nmemb, size);
if (p == NULL)
fatal("Out of memory.");
return (p);
}
/*
** realloc(3) with error checking. Returns a pointer to the allocated
** memory.
*/
void *xrealloc(void *ptr, size_t size) {
void *ret = realloc(ptr, size);
if (ret == NULL)
fatal("Out of memory.");
return (ret);
}
/*
** strdup(3) with error checking. Returns a pointer to the new copy of the
** string.
*/
char *xstrdup(const char *s) {
char *ret = strdup(s);
if (ret == NULL)
fatal("Out of memory.");
return (ret);
}
/*
** Copy at most n-1 characters from src to dest and nul-terminate dest.
** Returns a pointer to the destination string.
*/
char *xstrncpy(char *dest, const char *src, size_t n) {
u_char *ret = dest;
if (n == 0)
return (dest);
while (--n > 0 && (*dest++ = *src++) != '\0')
;
*dest = '\0';
return (ret);
}
/*
** Append at most n-1 characters from src to dest and nul-terminate dest.
** Returns a pointer to the destination string.
*/
char *xstrncat(char *dest, const char *src, size_t n) {
u_char *ret = dest;
if (n == 0)
return (dest);
for (; *dest != '\0' ; dest++, n--)
;
while (--n > 0 && (*dest++ = *src++) != '\0')
;
*dest = '\0';
return (ret);
}
/*
** sleep(3) implemented with select.
*/
void xsleep(time_t seconds) {
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv);
}
/*
** usleep(3) implemented with select.
*/
void xusleep(u_long usec) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = usec;
select(0, NULL, NULL, NULL, &tv);
}
/*
** Resolves a port number to a service name (eg. 23 -> "telnet")
** returns "port %u" on failure.
*/
#ifndef HAVE_GETSERVBYPORT_R
u_char *serv_lookup(in_port_t port,
const u_char *proto,
u_char *buf,
size_t len)
{
static pthread_mutex_t serv_mutex = PTHREAD_MUTEX_INITIALIZER;
struct servent *se;
port &= MAX_PORT;
pthread_mutex_lock(&serv_mutex);
se = getservbyport(port, proto);
if (se != NULL)
xstrncpy(buf, se->s_name, len);
else
snprintf(buf, len, "port %u", htons(port));
pthread_mutex_unlock(&serv_mutex);
return (buf);
}
#elif HAVE_GETSERVBYPORT_RSIX
u_char *serv_lookup(in_port_t port,
const u_char *proto,
u_char *buf,
size_t len)
{
int ret;
struct servent *result, se;
u_char rbuf[1024];
port &= MAX_PORT;
ret = getservbyport_r(port, proto, &se, rbuf, sizeof(rbuf), &result);
if (ret != 0 || result == NULL)
snprintf(buf, len, "port %u", htons(port));
else
xstrncpy(buf, se.s_name, len);
return (buf);
}
#elif HAVE_GETSERVBYPORT_RFIVE
u_char *serv_lookup(in_port_t port,
const u_char *proto,
u_char *buf,
size_t len)
{
struct servent se;
u_char rbuf[1024];
port &= MAX_PORT;
if (getservbyport_r(port, proto, &se, rbuf, sizeof(rbuf)) == NULL)
snprintf(buf, len, "port %u", htons(port));
else
xstrncpy(buf, se.s_name, len);
return (buf);
}
#elif HAVE_GETSERVBYPORT_RFOUR
u_char *serv_lookup(in_port_t port,
const u_char *proto,
u_char *buf,
size_t len)
{
struct servent se;
struct servent_data sed;
memset(&sed, 0, sizeof(sed));
if (getservbyport_r(port, proto, &se, &sed) != 0)
snprintf(buf, len, "port %u", htons(port));
else
xstrncpy(buf, se.s_name, len);
return (buf);
}
#else
# error "BUG - No serv_lookup()"
#endif
#ifndef HAVE_LOCALTIME_R
/*
** Wrapper to make localtime(3) thread-safe.
*/
struct tm *localtime_r(const time_t *cur_time, struct tm *result) {
struct tm *tm;
static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&localtime_mutex);
tm = localtime(cur_time);
memcpy(result, tm, sizeof(struct tm));
pthread_mutex_unlock(&localtime_mutex);
return (result);
}
#endif
/*
** Thread-safe version of inet_ntoa(3).
*/
u_char *inet_ntoa_r(const struct in_addr *in, u_char *buf, size_t len) {
ipaddr_t addr = ntohl(in->s_addr);
snprintf(buf, len, "%u.%u.%u.%u",
((addr >> 24) & 0xff), ((addr >> 16) & 0xff),
((addr >> 8) & 0xff), (addr & 0xff));
return (buf);
}
/*
** Resolve a protocol number to its name (eg. 6 -> "TCP")
** returns "proto %u" on failure.
*/
#ifndef HAVE_GETPROTOBYNUMBER_R
u_char *proto_lookup(int proto, u_char *buf, size_t len) {
static pthread_mutex_t proto_mutex = PTHREAD_MUTEX_INITIALIZER;
struct protoent *pe;
pthread_mutex_lock(&proto_mutex);
pe = getprotobynumber(proto);
if (pe != NULL) {
xstrncpy(buf, pe->p_name, len);
pthread_mutex_unlock(&proto_mutex);
} else {
pthread_mutex_unlock(&proto_mutex);
snprintf(buf, len, "proto %u", proto);
}
return (buf);
}
#elif defined(HAVE_GETPROTOBYNUMBER_RFIVE)
u_char *proto_lookup(int proto, u_char *buf, size_t len) {
int ret;
struct protoent *result, pe;
u_char tbuf[256];
ret = getprotobynumber_r(proto, &pe, tbuf, sizeof(tbuf), &result);
if (ret != 0 || result == NULL)
snprintf(buf, len, "proto %u", proto);
else
xstrncpy(buf, pe.p_name, len);
return (buf);
}
#elif defined(HAVE_GETPROTOBYNUMBER_RFOUR)
u_char *proto_lookup(int proto, u_char *buf, size_t len) {
struct protoent pe;
u_char tbuf[256];
if (getprotobynumber_r(proto, &pe, tbuf, sizeof(tbuf)) == NULL)
snprintf(buf, len, "proto %u", proto);
else
xstrncpy(buf, pe.p_name, len);
return (buf);
}
#elif defined(HAVE_GETPROTOBYNUMBER_RTHREE)
u_char *proto_lookup(int proto, u_char *buf, size_t len) {
struct protoent pe;
struct protoent_data prd;
memset(&prd, 0, sizeof(prd));
if (getprotobynumber_r(proto, &pe, &prd) != 0)
snprintf(buf, len, "proto %u", proto);
else
xstrncpy(buf, pe.p_name, len);
return (buf);
}
#else
# error "BUG - No proto_lookup()"
#endif
/*
** Returns the FQDN of the host specified by "in." Returns its quad-dot
** notation IP address on failure (or if the resolver is disabled).
*/
u_char *host_lookup(const struct in_addr *in, bool resolv,
u_char *buf, size_t len)
{
if (resolv == false)
return (inet_ntoa_r(in, buf, len));
if (opt_enabled(DNS_CACHE))
return (get_dns_cache(in->s_addr, buf, len));
if (opt_enabled(LOG_IP)) {
_host_lookup(in, buf, len);
if (isdigit(buf[strlen(buf) - 1]))
return (buf);
if ((strlen(buf) + MAX_IPLEN + 4) < len) {
u_char sbuf[MAX_IPLEN];
u_char tbuf[len];
xstrncpy(tbuf, buf, sizeof(tbuf));
inet_ntoa_r(in, sbuf, sizeof(sbuf));
snprintf(buf, len, "%s (%s)", tbuf, sbuf);
return (buf);
} else
return (inet_ntoa_r(in, buf, len));
}
return (_host_lookup(in, buf, len));
}
/*
** The guts of host_lookup() (see above).
*/
#ifndef HAVE_GETHOSTBYADDR_R
u_char *_host_lookup(const struct in_addr *in, u_char *buf, size_t len) {
static pthread_mutex_t host_mutex = PTHREAD_MUTEX_INITIALIZER;
struct hostent *he;
pthread_mutex_lock(&host_mutex);
he = gethostbyaddr((char *) in, sizeof(struct in_addr), AF_INET);
if (he != NULL && strlen(he->h_name) < len) {
xstrncpy(buf, he->h_name, len);
pthread_mutex_unlock(&host_mutex);
} else {
pthread_mutex_unlock(&host_mutex);
inet_ntoa_r(in, buf, len);
}
return (buf);
}
#elif defined(HAVE_GETHOSTBYADDR_REIGHT)
u_char *_host_lookup(const struct in_addr *in, u_char *buf, size_t len) {
int herr, ret;
struct hostent *result, he;
u_char hbuf[1024];
ret = gethostbyaddr_r((char *) in, sizeof(struct in_addr), AF_INET,
&he, hbuf, sizeof(hbuf), &result, &herr);
#if defined __GLIBC__ && __GLIBC__ >= 2
if (ret != 0 || result == NULL || strlen(he.h_name) >= len)
#else
if (ret != 0 || strlen(he.h_name) >= len)
#endif
inet_ntoa_r(in, buf, len);
else
xstrncpy(buf, he.h_name, len);
return (buf);
}
#elif defined(HAVE_GETHOSTBYADDR_RSEVEN)
u_char *_host_lookup(const struct in_addr *in, u_char *buf, size_t len) {
int herr;
struct hostent *ret, he;
u_char hbuf[1024];
ret = gethostbyaddr_r((char *) in, sizeof(struct in_addr), AF_INET,
&he, hbuf, sizeof(hbuf), &herr);
if (ret == NULL || strlen(he.h_name) >= len)
inet_ntoa_r(in, buf, len);
else
xstrncpy(buf, he.h_name, len);
return (buf);
}
#elif defined(HAVE_GETHOSTBYADDR_RFIVE)
u_char *_host_lookup(const struct in_addr *in, u_char *buf, size_t len) {
int ret;
struct hostent he;
struct hostent_data hed;
memset(&hed, 0, sizeof(hed));
ret = gethostbyaddr_r((char *) in, sizeof(struct in_addr), AF_INET,
&he, &hed);
if (ret != 0 || strlen(he.h_name) >= len)
inet_ntoa_r(in, buf, len);
else
xstrncpy(buf, he.h_name, len);
return (buf);
}
#else
# error "BUG - No _host_lookup()"
#endif
/*
** Become a daemon.
*/
void fork_to_back(void) {
int ret;
ret = fork();
if (ret > 0)
_exit(0);
else if (ret != 0)
fatal("fork: %s", strerror(errno));
if (setsid() == -1)
fatal("setsid: %s", strerror(errno));
if (chdir("/") == -1)
fatal("chdir: %s", strerror(errno));
umask(022);
if (close(0) == -1)
fatal("close(%d): %s", 0, strerror(errno));
if (!opt_enabled(LOG_STDOUT)) {
if (close(1) == -1)
fatal("close(%d): %s", 1, strerror(errno));
}
if (close(2) == -1)
fatal("close(%d): %s", 2, strerror(errno));
ret = open("/dev/null", O_WRONLY);
if (ret != 0)
fatal("open: /dev/null: %s", strerror(errno));
if (!opt_enabled(LOG_STDOUT)) {
if (dup2(0, 1) == -1)
fatal("dup2(%d, %d): %s", 0, 1, strerror(errno));
}
if (dup2(0, 2) == -1)
fatal("dup2(%d, %d): %s", 0, 2, strerror(errno));
}
/*
** Switch to [e]uid of user and [e]gid of group, drop all supplementary groups.
*/
void drop_privs(const u_char *user, const u_char *group) {
uid_t uid;
gid_t gid;
char *nptr;
if (group != NULL) {
struct group *gr = getgrnam(group);
if (gr == NULL) {
gid = strtoul(group, &nptr, 10);
if (*nptr != '\0')
fatal("Unknown group: \"%s\"", group);
} else
gid = gr->gr_gid;
if (setgid(gid) != 0)
fatal("setgid(%d): %s", gid, strerror(errno));
if (setgroups(0, NULL) != 0)
fatal("setgroups(0, NULL): %s", strerror(errno));
}
if (user != NULL) {
struct passwd *pw = getpwnam(user);
if (pw == NULL) {
uid = strtoul(user, &nptr, 10);
if (*nptr != '\0')
fatal("Unknown user: \"%s\"", user);
} else
uid = pw->pw_uid;
if (setuid(uid) != 0)
fatal("setuid(%d): %s", uid, strerror(errno));
}
}
/*
** Logs a fatal error message and exits.
*/
#ifdef HAVE_VASPRINTF
void fatal(const u_char *fmt, ...) {
va_list ap;
u_char *message;
va_start(ap, fmt);
vasprintf((char **) &message, fmt, ap);
va_end(ap);
mysyslog("Fatal: %s", message);
exit(-1);
}
#else
void fatal(const u_char *fmt, ...) {
va_list ap;
u_char message[2048];
va_start(ap, fmt);
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
mysyslog("Fatal: %s", message);
exit(-1);
}
#endif
/*
** Read a line of up to len characters from fp into buf until
** a newline is encountered. Nul-terminate buf.
*/
int get_line(FILE *fp, u_char *buf, size_t len) {
int c;
size_t i = 0;
for (;;) {
c = getc(fp);
switch (c) {
case EOF:
if (i == 0)
return EOF;
case '\n':
if (i >= len)
i = 0;
buf[i] = '\0';
return (0);
default:
if (i == 0 && isspace(c))
break;
if (i < len)
buf[i++] = c;
}
}
return (0);
}
/*
** Write to a socket, deal with interrupted and incomplete writes. Returns
** the number of characters written to the socket on success, -1 on failure.
*/
ssize_t sock_write(int sock, void *buf, size_t len) {
ssize_t n, written = 0;
while (len > 0) {
n = write(sock, buf, len);
if (n == -1) {
if (errno == EINTR)
continue;
return (-1);
}
written += n;
len -= n;
buf += n;
}
return (written);
}
/*
** Old, unoptimized implementation of the IP checksum..
*/
int in_cksum(u_short *addr, int len) {
int nleft = len;
u_short *w = addr;
int sum = 0;
u_short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
/*
** Returns true if the resolver is not disabled and false if it is disabled.
*/
bool any_res(void) {
return (!opt_enabled(NO_RESOLV));
}
/*
** Add a node to the end of a doubly linked list.
*/
void *__dlist_append(void *data, void **head) {
dlist_t **list_head = (dlist_t **) head;
dlist_t *cur = *list_head;
dlist_t *new_node = data;
new_node->next = NULL;
if (cur == NULL) {
new_node->prev = NULL;
*list_head = new_node;
return (new_node);
}
while (cur->next != NULL)
cur = cur->next;
cur->next = new_node;
new_node->prev = cur;
return (new_node);
}
/*
** Allocate a new node, copy data there and add it to the end of a doubly
** linked list.
*/
void *__dlist_copy_append(const void *data, void **head, size_t len) {
dlist_t *new_node = xmalloc(len);
memcpy(new_node, data, len);
return (dlist_append(new_node, head));
}
/*
** Add a node to the head of a doubly linked list.
*/
void *__dlist_prepend(void *data, void **head) {
dlist_t *new_node = data;
dlist_t **list_head = (dlist_t **) head;
new_node->prev = NULL;
new_node->next = *list_head;
if (*list_head != NULL)
(*list_head)->prev = new_node;
*list_head = new_node;
return (new_node);
}
/*
** Allocate a new node, copy data there and add it to the head of the linked
** list.
*/
void *__dlist_copy_prepend(const void *data, void **head, size_t len) {
dlist_t *new_node = xmalloc(len);
memcpy(new_node, data, len);
return (dlist_prepend(new_node, head));
}
/*
** Remove a node from a doubly linked list.
*/
void *__dlist_remove(void *data, void **head) {
dlist_t *dnode = data;
dlist_t *next = dnode->next;
dlist_t **list_head = (dlist_t **) head;
if (dnode->prev == NULL) {
*list_head = next;
if (next != NULL)
next->prev = NULL;
} else {
dnode->prev->next = next;
if (next != NULL)
next->prev = dnode->prev;
}
return (next);
}
/*
** Remove a node from a doubly linked list, free the memory it occupies.
*/
void *__dlist_delete(void *data, void **head) {
void *ret = __dlist_remove(data, head);
free(data);
return (ret);
}
/*
** Add a node to the end of a linked list.
*/
void *__list_append(void *data, void **head) {
list_t *cur, *new_node = data;
new_node->next = NULL;
if (*head == NULL)
*head = new_node;
else {
cur = *head;
while (cur->next != NULL)
cur = cur->next;
cur->next = new_node;
}
return (new_node);
}
/*
** Allocate a new node, copy data there and add it to the end of a linked
** list.
*/
void *__list_copy_append(const void *data, void **head, size_t len) {
list_t *new_node = xmalloc(len);
memcpy(new_node, data, len);
return (list_append(new_node, head));
}
/*
** Add a node to the head of a linked list.
*/
void *__list_prepend(void *data, void **head) {
list_t *old_head, *new_node = data;
old_head = *head;
*head = new_node;
new_node->next = old_head;
return (*head);
}
/*
** Allocate a new node, copy data there and add it to the head of the linked
** list.
*/
void *__list_copy_prepend(const void *data, void **head, size_t len) {
list_t *new_node = xmalloc(len);
memcpy(new_node, data, len);
return (list_prepend(new_node, head));
}
/*
** Delete a node from a linked list.
*/
void *__list_delete(void *node, void **head) {
list_t *ret = NULL, *cur = *head, *temp = NULL;
while (cur != NULL) {
if (cur == node) {
ret = cur->next;
if (temp == NULL)
*head = ret;
else
temp->next = ret;
free(cur);
break;
}
temp = cur;
cur = cur->next;
}
return (ret);
}
/*
** Destroy a linked list or a doubly linked list.
*/
void list_destroy(void *list_head, void (*cleanup)(void *)) {
list_t *cur = list_head, *next;
while (cur != NULL) {
next = cur->next;
if (cleanup != NULL)
cleanup(cur);
free(cur);
cur = next;
}
}
/* vim:ts=4:sw=8:tw=0 */
syntax highlighted by Code2HTML, v. 0.9.1