/*
* utils.c
*
* $Id: utils.c,v 1.69 2007/05/18 03:35:23 mjl Exp $
*
* Copyright (C) 2003-2007 The University of Waikato
*
* This program 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, version 2.
*
* 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
*
*/
#if defined(__APPLE__)
#define _BSD_SOCKLEN_T_
#define HAVE_SOCKADDR_SA_LEN
#define HAVE_OSSWAPINT16
#include <libkern/OSByteOrder.h>
#include <stdint.h>
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#define HAVE_SOCKADDR_SA_LEN
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/utsname.h>
#if !defined(__sun__)
#include <sys/sysctl.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#if defined(AF_LINK)
#include <net/if_dl.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>
#include <unistd.h>
#include <assert.h>
#if defined(DMALLOC)
#include <dmalloc.h>
#endif
#if defined(__sun__)
# define s6_addr32 _S6_un._S6_u32
#elif !defined(s6_addr32)
# define s6_addr32 __u6_addr.__u6_addr32
#endif
#if defined(__linux__)
#include <byteswap.h>
#endif
#include "utils.h"
#if defined(HAVE_SOCKADDR_SA_LEN)
int sockaddr_len(const struct sockaddr *sa)
{
return sa->sa_len;
}
#else
int sockaddr_len(const struct sockaddr *sa)
{
if(sa->sa_family == AF_INET) return sizeof(struct sockaddr_in);
if(sa->sa_family == AF_INET6) return sizeof(struct sockaddr_in6);
#if defined(AF_LINK)
if(sa->sa_family == AF_LINK) return sizeof(struct sockaddr_dl);
#endif
return -1;
}
#endif
void *addr_dup(const int af, const void *addr_in)
{
void *addr;
size_t size;
if(af == AF_INET)
{
size = sizeof(struct in_addr);
}
else if(af == AF_INET6)
{
size = sizeof(struct in6_addr);
}
else return NULL;
if((addr = malloc(size)) != NULL)
{
memcpy(addr, addr_in, size);
}
return addr;
}
struct sockaddr *sockaddr_dup(const struct sockaddr *sa)
{
struct sockaddr *addr;
int len;
if((len = sockaddr_len(sa)) <= 0)
{
return NULL;
}
if((addr = malloc((size_t)len)) != NULL)
{
memcpy(addr, sa, (size_t)len);
}
return addr;
}
int sockaddr_compose(struct sockaddr *sa,
const int af, const void *addr, const int port)
{
socklen_t len;
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
assert(port >= 0);
assert(port <= 65535);
if(af == AF_INET)
{
len = sizeof(struct sockaddr_in);
memset(sa, 0, len);
sin4 = (struct sockaddr_in *)sa;
if(addr != NULL) memcpy(&sin4->sin_addr, addr, sizeof(struct in_addr));
sin4->sin_port = htons(port);
}
else if(af == AF_INET6)
{
len = sizeof(struct sockaddr_in6);
memset(sa, 0, len);
sin6 = (struct sockaddr_in6 *)sa;
if(addr != NULL) memcpy(&sin6->sin6_addr, addr, sizeof(struct in6_addr));
sin6->sin6_port = htons(port);
}
else return -1;
#if defined(HAVE_SOCKADDR_SA_LEN)
sa->sa_len = len;
#endif
sa->sa_family = af;
return 0;
}
char *sockaddr_tostr(const struct sockaddr *sa, char *buf, const size_t len)
{
char addr[128];
if(sa->sa_family == AF_INET)
{
if(inet_ntop(AF_INET, &((const struct sockaddr_in *)sa)->sin_addr,
addr, sizeof(addr)) == NULL)
{
return NULL;
}
snprintf(buf, len, "%s:%d", addr,
((const struct sockaddr_in *)sa)->sin_port);
}
else if(sa->sa_family == AF_INET6)
{
if(inet_ntop(AF_INET6, &((const struct sockaddr_in6 *)sa)->sin6_addr,
addr, sizeof(addr)) == NULL)
{
return NULL;
}
snprintf(buf, len, "%s.%d", addr,
((const struct sockaddr_in6 *)sa)->sin6_port);
}
else
{
return NULL;
}
return buf;
}
int addr4_cmp(const void *va, const void *vb)
{
const struct in_addr *a = (const struct in_addr *)va;
const struct in_addr *b = (const struct in_addr *)vb;
if(a->s_addr < b->s_addr) return -1;
if(a->s_addr > b->s_addr) return 1;
return 0;
}
int addr6_cmp(const void *va, const void *vb)
{
const struct in6_addr *a = (const struct in6_addr *)va;
const struct in6_addr *b = (const struct in6_addr *)vb;
int i;
for(i=0; i<4; i++)
{
if(a->s6_addr32[i] < b->s6_addr32[i]) return -1;
if(a->s6_addr32[i] > b->s6_addr32[i]) return 1;
}
return 0;
}
/*
* addr_cmp:
* this function is used to provide a sorting order, not for advising the
* numerical order of the addresses passed in.
*/
int addr_cmp(int af, const void *a, const void *b)
{
if(af == AF_INET) return addr4_cmp(a, b);
if(af == AF_INET6) return addr6_cmp(a, b);
return 0;
}
/*
* memdup
*
* duplicate some memory.
*/
#ifndef DMALLOC
void *memdup(const void *ptr, const size_t len)
{
void *d;
if((d = malloc(len)) != NULL)
{
memcpy(d, ptr, len);
}
return d;
}
#endif
/*
* malloc_zero
*
* allocate some memory, zero it, and return a pointer to it.
*/
#if !defined(DMALLOC)
void *malloc_zero(const size_t size)
{
void *ptr;
if((ptr = malloc(size)) != NULL)
{
memset(ptr, 0, size);
}
return ptr;
}
#endif
int gettimeofday_wrap(struct timeval *tv)
{
struct timezone tz;
return gettimeofday(tv, &tz);
}
int timeval_cmp(const struct timeval *a, const struct timeval *b)
{
if(a->tv_sec < b->tv_sec) return -1;
if(a->tv_sec > b->tv_sec) return 1;
if(a->tv_usec < b->tv_usec) return -1;
if(a->tv_usec > b->tv_usec) return 1;
return 0;
}
void timeval_add_msec(struct timeval *tv, const int msec)
{
tv->tv_sec += (msec / 1000);
tv->tv_usec += ((msec % 1000) * 1000);
/* check for overflow of usec's */
if(tv->tv_usec >= 1000000)
{
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
else if(tv->tv_usec < 0)
{
tv->tv_sec--;
tv->tv_usec += 1000000;
}
return;
}
/*
* timeval_diff_msec
* return the millisecond difference between the two timevals.
* a - b
*/
int64_t timeval_diff_msec(const struct timeval *a, const struct timeval *b)
{
int64_t temp, a_sec, a_usec, b_sec, b_usec;
a_sec = (int64_t)a->tv_sec * (int64_t)1000;
a_usec = (int64_t)a->tv_usec / (int64_t)1000;
b_sec = (int64_t)b->tv_sec * (int64_t)1000;
b_usec = (int64_t)b->tv_usec / (int64_t)1000;
temp = a_sec - b_sec + a_usec - b_usec;
return temp;
}
void timeval_add_usec(struct timeval *tv, const int64_t usec)
{
tv->tv_sec += (usec / 1000000);
tv->tv_usec += (usec % 1000000);
/* check for underflow */
if(tv->tv_usec < 0)
{
tv->tv_sec--;
tv->tv_usec += 1000000;
}
/* check for overflow */
else if(tv->tv_usec >= 1000000)
{
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
return;
}
/*
* timeval_add_tv
*
* given a timeval struct, add the time in the second timeval struct to
* it.
*/
void timeval_add_tv(struct timeval *tv, const struct timeval *add)
{
assert(add->tv_sec >= 0);
assert(add->tv_usec >= 0);
tv->tv_usec += add->tv_usec;
/* check for overflow */
if(tv->tv_usec > 1000000)
{
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
tv->tv_sec += add->tv_sec;
return;
}
/*
* timeval_diff_usec
* return the microsecond difference between the two timevals.
* a - b
*/
int64_t timeval_diff_usec(const struct timeval *a, const struct timeval *b)
{
int64_t temp, a_sec, b_sec;
a_sec = (int64_t)a->tv_sec * (int64_t)1000000;
b_sec = (int64_t)b->tv_sec * (int64_t)1000000;
temp = a_sec - b_sec + a->tv_usec - b->tv_usec;
return temp;
}
void timeval_rtt(struct timeval *rtt,
const struct timeval *from, const struct timeval *to)
{
rtt->tv_sec = to->tv_sec - from->tv_sec;
rtt->tv_usec = to->tv_usec - from->tv_usec;
if(rtt->tv_usec < 0)
{
rtt->tv_sec--;
rtt->tv_usec += 1000000;
}
return;
}
void timeval_cpy(struct timeval *dst, const struct timeval *src)
{
memcpy(dst, src, sizeof(struct timeval));
return;
}
int fcntl_unset(const int fd, const int flags)
{
int i;
if((i = fcntl(fd, F_GETFL, 0)) == -1)
{
return -1;
}
if(fcntl(fd, F_SETFL, i & (~flags)) == -1)
{
return -1;
}
return 0;
}
int fcntl_set(const int fd, const int flags)
{
int i;
if((i = fcntl(fd, F_GETFL, 0)) == -1)
{
return -1;
}
if(fcntl(fd, F_SETFL, i | flags) == -1)
{
return -1;
}
return 0;
}
int string_isprint(const char *str, const size_t len)
{
size_t i = 0;
for(i=0; i<len; i++)
{
if(isprint((int)str[i]) != 0)
{
continue;
}
else if(str[i] == '\0')
{
break;
}
else return 0;
}
return 1;
}
int string_tolong(const char *str, long *l)
{
char *endptr;
*l = strtol(str, &endptr, 0);
if(*l == 0)
{
if(errno == EINVAL) return -1;
}
else if(*l == LONG_MIN || *l == LONG_MAX)
{
if(errno == ERANGE) return -1;
}
return 0;
}
/*
* string_isnumber
*
* scan the word to establish if it an integer.
*/
int string_isnumber(const char *str)
{
int i = 1;
if(str[0] != '-' && str[0] != '+' && isdigit((int)str[0]) == 0)
{
return 0;
}
while(str[i] != '\0')
{
if(isdigit((int)str[i]) != 0)
{
i++;
continue;
}
return 0;
}
return 1;
}
/*
* string_isfloat
*
* scan the word to establish if it is a float.
*/
int string_isfloat(const char *str)
{
int seen_dp = 0;
int i = 1;
if(str[0] != '-' && str[0] != '+' && isdigit((int)str[0]) == 0)
{
if(str[0] == '.')
{
seen_dp = 1;
}
else return 0;
}
while(str[i] != '\0')
{
if(isdigit((int)str[i]) != 0)
{
i++;
continue;
}
else if(str[i] == '.')
{
/* if the decimal point has already been seen */
if(seen_dp == 1)
{
return 0;
}
i++;
seen_dp = 1;
continue;
}
return 0;
}
return 1;
}
/*
* string_nextword
*
* scan for the next word occurance in the string, after the current
* word. if there is no other word after this one, return NULL.
*/
char *string_nextword(char *buf)
{
/* scan for a start of a word */
while(*buf != '\0' && isspace((int)*buf) == 0)
{
buf++;
}
if(*buf == '\0')
{
return NULL;
}
*buf = '\0';
buf++;
/* now scan for the end of the word */
while(*buf != '\0' && isspace((int)*buf) != 0)
{
buf++;
}
if(*buf == '\0')
{
return NULL;
}
return buf;
}
/*
* string_nullterm
*
* null terminate the string pointed to by buf at the first occurance of
* a character in the delim string.
*
* if null termination occurs, this function returns a pointer to the first
* byte of the buf (i.e. the buf parameter passed)
*/
char *string_nullterm(char *buf, const char *delim)
{
const char *dtmp;
char *tmp;
if(delim == NULL || *delim == '\0' || (tmp = buf) == NULL)
{
return NULL;
}
while(*tmp != '\0')
{
dtmp = delim;
while(*dtmp != '\0')
{
if(*tmp != *dtmp)
{
dtmp++;
continue;
}
*tmp = '\0';
return buf;
}
tmp++;
}
return NULL;
}
char *string_nullterm_char(char *buf, const char delim)
{
char *tmp;
if((tmp = buf) == NULL)
{
return NULL;
}
while(*tmp != '\0')
{
if(*tmp == delim)
{
*tmp = '\0';
return buf;
}
tmp++;
}
return NULL;
}
char *string_lastof(char *str, const char *delim)
{
char *lastof = NULL;
const char *d;
int i;
if(delim == NULL || *delim == '\0' || str == NULL)
{
return NULL;
}
for(i=0; str[i] != '\0'; i++)
{
for(d = delim; *d != '\0'; d++)
{
if(str[i] == *d)
{
lastof = &str[i];
break;
}
}
}
return lastof;
}
char *string_lastof_char(char *str, const char delim)
{
char *lastof = NULL;
int i;
if(str == NULL)
{
return NULL;
}
for(i=0; str[i] != '\0'; i++)
{
if(str[i] == delim)
{
lastof = &str[i];
}
}
return lastof;
}
int ishex(char c)
{
if((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'))
{
return 1;
}
return 0;
}
int read_wrap(const int fd, void *ptr, size_t *rc_out, const size_t rt)
{
uint8_t *buf;
int ret = 0;
ssize_t r;
size_t rc;
assert(rt > 0);
assert(ptr != NULL);
buf = (uint8_t *)ptr;
for(rc = 0; rc < rt; rc += r)
{
if((r = read(fd, buf+rc, rt-rc)) < 0)
{
if(errno == EINTR)
{
r = 0;
continue;
}
ret = -1;
break;
}
else if(r == 0)
{
ret = -2;
break;
}
}
if(rc_out != NULL)
{
*rc_out = rc;
}
return ret;
}
int write_wrap(const int fd, const void *ptr, size_t *wc_out, const size_t wt)
{
int ret = 0;
ssize_t w;
size_t wc;
assert(wt > 0);
assert(ptr != NULL);
for(wc = 0; wc < wt; wc += w)
{
if((w = write(fd, ((const uint8_t *)ptr)+wc, wt-wc)) < 0)
{
if(errno == EINTR)
{
w = 0;
continue;
}
ret = -1;
break;
}
}
if(wc_out != NULL)
{
*wc_out = wc;
}
return ret;
}
/*
* mkdir_wrap
*
* iteratively call mkdir until the full path has been created
*/
int mkdir_wrap(const char *path, mode_t mode)
{
char *d = NULL;
char *ptr;
/* ensure there is actually a path to create ... */
if(path[0] == '\0' || (path[0] == '/' && path[1] == '\0'))
{
return 0;
}
/* make a duplicate copy of the path that we are going to create */
if((d = strdup(path)) == NULL)
{
goto err;
}
ptr = d;
/* don't need to create the root directory ... */
if(ptr[0] == '/') ptr++;
while(ptr[0] != '\0')
{
if(ptr[0] == '/')
{
/* temporarily replace the path delimeter */
ptr[0] = '\0';
if(mkdir(d, mode) != 0 && errno != EEXIST)
{
goto err;
}
ptr[0] = '/';
}
ptr++;
}
/* create the last directory in the path */
if(ptr[-1] != '/')
{
if(mkdir(d, mode) != 0 && errno != EEXIST)
{
goto err;
}
}
free(d);
return 0;
err:
if(d != NULL) free(d);
return -1;
}
/*
* fstat_mtime
*
* simple utility function that gets the mtime field from
* a fstat call as a time_t.
*/
int fstat_mtime(int fd, time_t *mtime)
{
struct stat sb;
if(fstat(fd, &sb) != 0)
{
return -1;
}
*mtime = sb.st_mtime;
return 0;
}
/*
* stat_mtime
*
* simple utility function that gets the mtime field from
* a stat call as a time_t.
*/
int stat_mtime(const char *filename, time_t *mtime)
{
struct stat sb;
if(stat(filename, &sb) != 0)
{
return -1;
}
*mtime = sb.st_mtime;
return 0;
}
#if !defined(__sun__)
int sysctl_wrap(int *mib, u_int len, void **buf, size_t *size)
{
if(sysctl(mib, len, NULL, size, NULL, 0) != 0)
{
return -1;
}
if(*size == 0)
{
*buf = NULL;
return 0;
}
if((*buf = malloc(*size)) == NULL)
{
return -1;
}
if(sysctl(mib, len, *buf, size, NULL, 0) != 0)
{
free(*buf);
return -1;
}
return 0;
}
#endif
uint16_t in_cksum(const void *buf, const size_t len)
{
uint16_t *w = (uint16_t *)buf;
size_t l = len;
int sum = 0;
while(l > 1)
{
sum += *w++;
l -= 2;
}
if(l != 0)
{
sum += ((uint8_t *)w)[0];
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
static void uuencode_3(uint8_t *out, uint8_t a, uint8_t b, uint8_t c)
{
uint8_t t;
out[0] = (t = ((a >> 2) & 0x3f)) != 0 ? t + 32 : '`';
out[1] = (t = (((a << 4) | ((b >> 4) & 0xf)) & 0x3f)) != 0 ? t + 32 : '`';
out[2] = (t = (((b << 2) | ((c >> 6) & 0x3)) & 0x3f)) != 0 ? t + 32 : '`';
out[3] = (t = (c & 0x3f)) != 0 ? t + 32 : '`';
return;
}
int uuencode(const uint8_t *in, size_t ilen, uint8_t **out, size_t *olen)
{
uint8_t *ptr;
size_t len;
size_t complete_lines;
size_t leftover_bytes;
size_t i, j;
if(ilen == 0)
{
return -1;
}
/*
* figure out how many complete lines there are,
* and then how many leftover bytes there are
*/
complete_lines = ilen / 45;
leftover_bytes = ilen % 45;
/*
* an input line of 45 characters is transformed into an 60 character
* sequence, with the length encoded as a character at the start, and
* a new-line character at the end of the line
*/
len = (complete_lines * 62);
/*
* if there are leftover bytes, then each group of three characters
* will take four output bytes. if the number of leftover bytes is not
* a multiple of three, then they are encoded in a 4 character sequence.
* finally, there's a length character at the start and a new line at the
* end.
*/
if(leftover_bytes != 0)
{
len += ((leftover_bytes / 3) * 4);
if((leftover_bytes % 3) > 0)
{
len += 4;
}
len += 2;
}
/* allocate the end-of-data bytes */
len += 2;
/* allocate memory to encode the data to */
if((ptr = malloc(len)) == NULL)
{
return -1;
}
*out = ptr;
*olen = len;
/* encode all complete lines */
for(i=0; i<complete_lines; i++)
{
*ptr = (0x20 + 45); ptr++;
for(j=0; j<15; j++)
{
uuencode_3(ptr, in[0], in[1], in[2]);
in += 3;
ptr += 4;
}
*ptr = '\n'; ptr++;
}
/* encode the last line */
if(leftover_bytes != 0)
{
/* encode groups of 3 input bytes */
*ptr = (0x20 + leftover_bytes); ptr++;
for(j=0; j<leftover_bytes/3; j++)
{
uuencode_3(ptr, in[0], in[1], in[2]);
in += 3;
ptr += 4;
}
/* if there are one or two straggling bytes left, encode those */
if((leftover_bytes % 3) > 0)
{
uuencode_3(ptr, in[0], (leftover_bytes % 3) == 2 ? in[1] : 0, 0);
ptr += 4;
}
*ptr = '\n'; ptr++;
}
/* this line has no data -- uuencode EOF */
*ptr = '`'; ptr++;
*ptr = '\n';
return 0;
}
uint16_t byteswap16(const uint16_t word)
{
#if defined(HAVE_BSWAP16)
return bswap(word);
#elif defined(HAVE___BSWAP16)
return __bswap16(word);
#elif defined(HAVE___BSWAP_16)
return __bswap_16(word);
#elif defined(HAVE_OSSWAPINT16)
return OSSwapInt16(word);
#else
return (((word & 0xff00) >> 8) | ((word & 0xff) << 8));
#endif
}
/*
* uname_wrap
*
* do some basic parsing on the output from uname
*/
scamper_osinfo_t *uname_wrap(void)
{
struct utsname utsname;
scamper_osinfo_t *osinfo;
int i;
char *str;
/* call uname to get the information */
if(uname(&utsname) != 0)
{
return NULL;
}
/* allocate our wrapping struct */
if((osinfo = malloc(sizeof(scamper_osinfo_t))) == NULL)
{
return NULL;
}
memset(osinfo, 0, sizeof(scamper_osinfo_t));
/* copy sysname in */
if((osinfo->os = strdup(utsname.sysname)) == NULL)
{
goto err;
}
/* parse the OS name */
if(strcasecmp(osinfo->os, "FreeBSD") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_FREEBSD;
else if(strcasecmp(osinfo->os, "OpenBSD") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_OPENBSD;
else if(strcasecmp(osinfo->os, "NetBSD") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_NETBSD;
else if(strcasecmp(osinfo->os, "SunOS") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_SUNOS;
else if(strcasecmp(osinfo->os, "Linux") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_LINUX;
else if(strcasecmp(osinfo->os, "Darwin") == 0)
osinfo->os_id = SCAMPER_OSINFO_OS_DARWIN;
/* parse the release integer string */
i = 0; str = utsname.release;
while(*str != '\0')
{
if(*str == '.')
{
*str = '\0';
osinfo->os_rel_dots++;
}
else if(isdigit((int)*str) == 0)
{
*str = '\0';
break;
}
str++;
}
if((osinfo->os_rel = malloc(osinfo->os_rel_dots * sizeof(int))) == NULL)
{
goto err;
}
i = 0; str = utsname.release;
while(i < osinfo->os_rel_dots)
{
if(string_tolong(str, &osinfo->os_rel[i]) != 0)
{
goto err;
}
while(*str != '\0') str++;
str++;
i++;
}
return osinfo;
err:
if(osinfo->os != NULL) free(osinfo->os);
if(osinfo->os_rel != NULL) free(osinfo->os_rel);
free(osinfo);
return NULL;
}
void scamper_osinfo_free(scamper_osinfo_t *osinfo)
{
if(osinfo != NULL)
{
if(osinfo->os != NULL) free(osinfo->os);
if(osinfo->os_rel != NULL) free(osinfo->os_rel);
free(osinfo);
}
return;
}
syntax highlighted by Code2HTML, v. 0.9.1