/*
* util.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "util.h"
#include <termios.h>
#include <paths.h>
#include <libutil.h>
#include <netdb.h>
#include <tcpd.h>
#include <sys/limits.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <net/route.h>
#include <netinet/if_ether.h>
#include <net/ethernet.h>
#include <osreldate.h>
/*
* DEFINITIONS
*/
#define MAX_FILENAME 1000
#define MAX_LINE_ARGS 50
#define BIG_LINE_SIZE 1000
#define MAX_OPEN_DELAY 2
#define MAX_LOCK_ATTEMPTS 30
/*
* INTERNAL VARIABLES
*/
#ifndef USE_NG_PRED1
static const u_int16_t Crc16Table[256] = {
/* 00 */ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
/* 08 */ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
/* 10 */ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
/* 18 */ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
/* 20 */ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
/* 28 */ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
/* 30 */ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
/* 38 */ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
/* 40 */ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
/* 48 */ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
/* 50 */ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
/* 58 */ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
/* 60 */ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
/* 68 */ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
/* 70 */ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
/* 78 */ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
/* 80 */ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
/* 88 */ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
/* 90 */ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
/* 98 */ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
/* a0 */ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
/* a8 */ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
/* b0 */ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
/* b8 */ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
/* c0 */ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
/* c8 */ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
/* d0 */ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
/* d8 */ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
/* e0 */ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
/* e8 */ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
/* f0 */ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
/* f8 */ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
#endif
static FILE *lockFp = NULL;
/*
* INTERNAL FUNCTIONS
*/
static void Escape(char *line);
static char *ReadLine(FILE *fp, int *lineNum, char *result, size_t resultsize);
static char HexVal(char c);
static void IndexConfFile(FILE *fp, struct configfile **cf);
static struct configfiles *ConfigFilesIndex=NULL;
#undef isspace
#define isspace(c) (((c)==' '||(c)=='\t'||(c)=='\n'||(c)=='\r')?1:0)
/*
* LengthenArray()
*/
void
LengthenArray(void *array, size_t esize, int *alenp, const char *type)
{
void **const arrayp = (void **)array;
void *newa;
newa = Malloc(type, (*alenp + 1) * esize);
if (*arrayp != NULL) {
memcpy(newa, *arrayp, *alenp * esize);
Freee(*arrayp);
}
*arrayp = newa;
(*alenp)++;
}
/*
* ExecCmd()
*/
int
ExecCmd(int log, const char *label, const char *fmt, ...)
{
int rtn;
char cmd[LINE_MAX];
char cmdn[LINE_MAX];
va_list ap;
va_start(ap, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, ap);
va_end(ap);
strcpy(cmdn, cmd);
/* Log command on the console */
Log(log, ("[%s] system: %s", label, cmd));
/* Hide any stdout output of command */
snprintf(cmdn + strlen(cmdn), sizeof(cmdn) - strlen(cmdn), " >%s 2>&1", _PATH_DEVNULL);
/* Do command */
if ((rtn = system(cmdn)))
Log(log|LG_ERR, ("[%s] system: command \"%s\" returned %d", label, cmd, rtn));
/* Return command's return value */
return(rtn);
}
/*
* ExecCmdNosh()
*/
int
ExecCmdNosh(int log, const char *label, const char *fmt, ...)
{
int rtn;
char cmd[LINE_MAX];
char *cmdp = &(cmd[0]);
char *argv[256];
char **arg;
va_list ap;
pid_t pid, savedpid;
int pstat;
struct sigaction ign, intact, quitact;
sigset_t newsigblock, oldsigblock;
va_start(ap, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, ap);
va_end(ap);
/* Log command on the console */
Log(log, ("[%s] exec: %s", label, cmd));
/* Parce args */
for (arg = &argv[0]; (*arg = strsep(&cmdp, " \t")) != NULL;) {
if (**arg != '\0') {
if (++arg >= &argv[255])
break;
}
}
*arg = NULL;
/* Do command */
/*
* Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
* existing signal dispositions.
*/
ign.sa_handler = SIG_IGN;
(void)sigemptyset(&ign.sa_mask);
ign.sa_flags = 0;
(void)sigaction(SIGINT, &ign, &intact);
(void)sigaction(SIGQUIT, &ign, &quitact);
(void)sigemptyset(&newsigblock);
(void)sigaddset(&newsigblock, SIGCHLD);
(void)sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
switch(pid = fork()) {
case -1: /* error */
break;
case 0: /* child */
/*
* Restore original signal dispositions and exec the command.
*/
(void)sigaction(SIGINT, &intact, NULL);
(void)sigaction(SIGQUIT, &quitact, NULL);
(void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
close(1);
open(_PATH_DEVNULL, O_WRONLY);
close(2);
open(_PATH_DEVNULL, O_WRONLY);
execv(argv[0], argv);
exit(127);
default: /* parent */
savedpid = pid;
do {
pid = wait4(savedpid, &pstat, 0, (struct rusage *)0);
} while (pid == -1 && errno == EINTR);
break;
}
(void)sigaction(SIGINT, &intact, NULL);
(void)sigaction(SIGQUIT, &quitact, NULL);
(void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
rtn = (pid == -1 ? -1 : pstat);
if (rtn)
Log(log|LG_ERR, ("[%s] system: command \"%s\" returned %d", label, cmd, rtn));
/* Return command's return value */
return(rtn);
}
/*
* ParseLine()
*
* Parse arguments, respecting double quotes and backslash escapes.
* Returns number of arguments, at most "max_args". This destroys
* the original line. The arguments returned are Malloc()'d strings
* which must be freed by the caller using FreeArgs().
*/
int
ParseLine(char *line, char *av[], int max_args, int copy)
{
int ac;
char *s, *arg;
/* Get args one at a time */
for (ac = 0; ac < max_args; ac++)
{
/* Skip white space */
while (*line && isspace(*line))
line++;
/* Done? */
if (*line == 0)
break;
/* Get normal or quoted arg */
if (*line == '"')
{
/* Stop only upon matching quote or NUL */
for (arg = ++line; *line; line++)
if (*line == '"')
{
*line++ = 0;
break;
}
else if (*line == '\\' && line[1] != 0)
{
strcpy(line, line + 1);
Escape(line);
}
}
else
{
/* NUL terminate this argument at first white space */
for (arg = line; *line && !isspace(*line); line++);
if (*line)
*line++ = 0;
/* Convert characters */
for (s = arg; *s; s++)
if (*s == '\\')
{
strcpy(s, s + 1);
Escape(s);
}
}
/* Make a copy of this arg */
if (copy) {
strcpy(av[ac] = Malloc(MB_CMD, strlen(arg) + 1), arg);
}
else
av[ac] = arg;
}
#if 0
{
int k;
printf("ParseLine: %d args:\n", ac);
for (k = 0; k < ac; k++)
printf(" [%2d] \"%s\"\n", k, av[k]);
}
#endif
return(ac);
}
/*
* FreeArgs()
*/
void
FreeArgs(int ac, char *av[])
{
while (ac > 0)
Freee(av[--ac]);
}
/*
* Escape()
*
* Give a string, interpret the beginning characters as an escape
* code and return with that code converted.
*/
static void
Escape(char *line)
{
int x, k;
char *s = line;
switch (*line)
{
case 't': *s = '\t'; return;
case 'n': *s = '\n'; return;
case 'r': *s = '\r'; return;
case 's': *s = ' '; return;
case '"': *s = '"'; return;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
for (x = k = 0; k < 3 && *s >= '0' && *s <= '7'; s++)
x = (x << 3) + (*s - '0');
*--s = x;
break;
case 'x':
for (s++, x = k = 0; k < 2 && isxdigit(*s); s++)
x = (x << 4) + (isdigit(*s) ? (*s - '0') : (tolower(*s) - 'a' + 10));
*--s = x;
break;
default:
return;
}
strcpy(line, s);
}
/*
* ReadFile()
*
* Read the commands specified for the target in the specified
* file, which can be found in the PATH_CONF_DIR directory.
* Returns negative if the file or target was not found.
*/
int
ReadFile(const char *filename, const char *target,
int (*func)(Context ctx, int ac, const char *const av[], const char *file, int line), Context ctx)
{
FILE *fp;
int ac;
char *av[MAX_LINE_ARGS];
char *line;
char buf[BIG_LINE_SIZE];
struct configfile *cf;
int lineNum;
/* Open file */
if ((fp = OpenConfFile(filename, &cf)) == NULL)
return(-2);
/* Find label */
if (SeekToLabel(fp, target, &lineNum, cf) < 0) {
fclose(fp);
return(-1);
}
/* Execute command list */
while ((line = ReadFullLine(fp, &lineNum, buf, sizeof(buf))) != NULL)
{
if (!isspace(*line))
{
break;
}
ac = ParseLine(line, av, sizeof(av) / sizeof(*av), 0);
(*func)(ctx, ac, (const char *const *)av, filename, lineNum);
}
/* Done */
fclose(fp);
return(0);
}
/*
* IndexConfFile()
*
* Scan config file for labels
*/
static void
IndexConfFile(FILE *fp, struct configfile **cf)
{
char *s, *line;
char buf[BIG_LINE_SIZE];
struct configfile **tmp;
int lineNum;
/* Start at beginning */
rewind(fp);
lineNum = 0;
tmp=cf;
/* Find label */
while ((line = ReadFullLine(fp, &lineNum, buf, sizeof(buf))) != NULL)
{
if (isspace(*line))
continue;
if ((s = strtok(line, " \t\f:"))) {
(*tmp)=Malloc(MB_CMDL, sizeof(struct configfile));
(*tmp)->label=strcpy(Malloc(MB_CMDL, strlen(s)+1),s);
(*tmp)->linenum=lineNum;
(*tmp)->seek=ftello(fp);
tmp=&((*tmp)->next);
}
}
}
/*
* SeekToLabel()
*
* Find a label in file and position file pointer just after it
*/
int
SeekToLabel(FILE *fp, const char *label, int *lineNum, struct configfile *cf)
{
char *s, *line;
char buf[BIG_LINE_SIZE];
struct configfile *tmp;
if (cf) { /* Trying to use index */
tmp=cf;
while (tmp && strcmp(tmp->label,label)) {
tmp=tmp->next;
}
if (tmp) {
fseeko(fp,tmp->seek, SEEK_SET);
if (lineNum)
*lineNum=tmp->linenum;
return(0);
}
} else { /* There are no index */
/* Start at beginning */
rewind(fp);
if (lineNum)
*lineNum = 0;
/* Find label */
while ((line = ReadFullLine(fp, lineNum, buf, sizeof(buf))) != NULL)
{
if (isspace(*line))
continue;
if ((s = strtok(line, " \t\f:")) && !strcmp(s, label))
return(0);
}
}
/* Not found */
Log(LG_ERR, ("Label '%s' not found", label));
return(-1);
}
/*
* OpenConfFile()
*
* Open a configuration file
*/
FILE *
OpenConfFile(const char *name, struct configfile **cf)
{
char pathname[MAX_FILENAME];
FILE *fp;
struct configfiles **tmp;
/* Build full pathname */
if (name[0] == '/')
snprintf(pathname, sizeof(pathname), "%s", name);
else
snprintf(pathname, sizeof(pathname), "%s/%s", gConfDirectory, name);
/* Open file */
if ((fp = fopen(pathname, "r")) == NULL)
{
Perror("%s: Can't open file '%s'", __FUNCTION__, pathname);
return(NULL);
}
(void) fcntl(fileno(fp), F_SETFD, 1);
if (cf) {
tmp=&ConfigFilesIndex;
while ((*tmp) && strcmp((*tmp)->filename,name)) {
tmp=&((*tmp)->next);
}
if (!(*tmp)) {
(*tmp) = Malloc(MB_CMD, sizeof(struct configfiles));
(*tmp)->filename = strcpy(Malloc(MB_CMD, strlen(name)+1),name);
(*tmp)->sections = NULL;
(*tmp)->next = NULL;
IndexConfFile(fp, &((*tmp)->sections));
}
*cf=(*tmp)->sections;
}
return(fp);
}
/*
* ReadFullLine()
*
* Read a full line, respecting backslash continuations.
* Returns pointer to Malloc'd storage, which must be Freee'd
*/
char *
ReadFullLine(FILE *fp, int *lineNum, char *result, int resultsize)
{
int len, resultlinesize, continuation;
unsigned linelen;
char line[BIG_LINE_SIZE];
char real_line[BIG_LINE_SIZE];
char *resultline;
if (result!=NULL && resultsize>0) {
resultline=result;
resultlinesize=resultsize;
} else {
resultline=line;
resultlinesize=sizeof(line);
}
resultline[0] = 0;
linelen = 0;
continuation = TRUE;
while ( continuation )
{
/* Get next real line */
if (ReadLine(fp, lineNum, real_line, sizeof(real_line)) == NULL) {
if (*resultline)
break;
else
return(NULL);
}
/* Strip trailing white space, detect backslash */
for (len = strlen(real_line);
len > 0 && isspace(real_line[len - 1]);
len--) {};
real_line[len] = 0;
if ((continuation = (len && real_line[len - 1] == '\\')))
real_line[len - 1] = ' ';
/* Append real line to what we've got so far */
strlcpy(resultline + linelen, real_line, resultlinesize - linelen);
linelen += len;
if (linelen > sizeof(line) - 1)
linelen = sizeof(line) - 1;
}
/* Report any overflow */
if (linelen >= sizeof(line) - 1)
Log(LG_ERR, ("warning: line too long, truncated"));
/* Copy line and return */
if (result!=NULL && resultsize>0)
return resultline;
else
return strcpy(Malloc(MB_CMD, linelen + 1), resultline);
}
/*
* ReadLine()
*
* Read a line, skipping blank lines & comments. A comment
* is a line whose first non-white-space character is a hash.
*/
static char *
ReadLine(FILE *fp, int *lineNum, char *result, size_t resultsize)
{
int empty;
char *s;
int ch;
if ((!result) || (resultsize <= 0))
return (NULL);
/* Get first non-empty, non-commented line */
empty = TRUE;
while ( empty ) {
/* Read next line from file */
if ((fgets(result, resultsize, fp)) == NULL)
return(NULL);
if (lineNum)
(*lineNum)++;
/* Truncate long lines */
if (strlen(result) > (resultsize - 2)) {
Log(LG_ERR, ("warning: line too long, truncated"));
while ((ch = getc(fp)) != EOF && ch != '\n');
}
/* Ignore comments */
s = result + strspn(result, " \t");
if (*s == '#') {
*s = 0;
} else {
/* Is this line empty? */
for ( ; *s; s++) {
if (!isspace(*s)) {
empty = FALSE;
break;
}
}
}
}
return(result);
}
#ifdef PHYSTYPE_MODEM
/*
* OpenSerialDevice()
*
* Open and configure a serial device. Call ExclusiveCloseDevice()
* to close a file descriptor returned by this function.
*/
int
OpenSerialDevice(const char *label, const char *path, int baudrate)
{
struct termios attr;
int fd;
/* Open & lock serial port */
if ((fd = ExclusiveOpenDevice(label, path)) < 0)
return(-1);
/* Set non-blocking I/O */
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
Perror("[%s] can't set \"%s\" to non-blocking", label, path);
goto failed;
}
/* Set serial port raw mode, baud rate, hardware flow control, etc. */
if (tcgetattr(fd, &attr) < 0)
{
Perror("[%s] can't tcgetattr \"%s\"", label, path);
goto failed;
}
cfmakeraw(&attr);
attr.c_cflag &= ~(CSIZE|PARENB|PARODD);
attr.c_cflag |= (CS8|CREAD|CLOCAL|HUPCL|CCTS_OFLOW|CRTS_IFLOW);
attr.c_iflag &= ~(IXANY|IMAXBEL|ISTRIP|IXON|IXOFF|BRKINT|ICRNL|INLCR);
attr.c_iflag |= (IGNBRK|IGNPAR);
attr.c_oflag &= ~OPOST;
attr.c_lflag = 0;
cfsetspeed(&attr, (speed_t) baudrate);
if (tcsetattr(fd, TCSANOW, &attr) < 0)
{
Perror("[%s] can't tcsetattr \"%s\"", label, path);
failed:
ExclusiveCloseDevice(label, fd, path);
return(-1);
}
/* OK */
return(fd);
}
/*
* ExclusiveOpenDevice()
*/
int
ExclusiveOpenDevice(const char *label, const char *pathname)
{
int fd, locked = FALSE;
const char *ttyname = NULL;
time_t startTime;
/* Lock device UUCP style, if it resides in /dev */
if (!strncmp(pathname, _PATH_DEV, 5))
{
int res;
ttyname = pathname + 5;
if ((res = uu_lock(ttyname)) != UU_LOCK_OK) {
Log(LG_ERR, ("[%s] uu_lock(%s): %s", label, ttyname, uu_lockerr(res)));
return(-1);
}
locked = TRUE;
}
/* Open it, but give up after so many interruptions */
for (startTime = time(NULL);
(fd = open(pathname, O_RDWR, 0)) < 0
&& time(NULL) < startTime + MAX_OPEN_DELAY; )
if (errno != EINTR)
{
Perror("[%s] can't open %s", label, pathname);
if (locked)
uu_unlock(ttyname);
return(-1);
}
/* Did we succeed? */
if (fd < 0)
{
Log(LG_ERR, ("[%s] can't open %s after %d secs",
label, pathname, MAX_OPEN_DELAY));
if (locked)
uu_unlock(ttyname);
return(-1);
}
(void) fcntl(fd, F_SETFD, 1);
/* Done */
return(fd);
}
/*
* ExclusiveCloseDevice()
*/
void
ExclusiveCloseDevice(const char *label, int fd, const char *pathname)
{
int rtn = -1;
const char *ttyname;
time_t startTime;
/* Close file(s) */
for (startTime = time(NULL);
time(NULL) < startTime + MAX_OPEN_DELAY && (rtn = close(fd)) < 0; )
if (errno != EINTR)
{
Perror("[%s] can't close %s", label, pathname);
break;
}
/* Did we succeed? */
if ((rtn < 0) && (errno == EINTR))
{
Log(LG_ERR, ("[%s] can't close %s after %d secs",
label, pathname, MAX_OPEN_DELAY));
DoExit(EX_ERRDEAD);
}
/* Remove lock */
if (!strncmp(pathname, _PATH_DEV, 5))
{
ttyname = pathname + 5;
if (uu_unlock(ttyname) < 0)
Perror("[%s] can't unlock %s", label, ttyname);
}
}
#endif /* PHYSTYPE_MODEM */
/*
* GenerateMagic()
*
* Generate random number which will be used as magic number.
* This could be made a little more "random"...
*/
u_long
GenerateMagic(void)
{
time_t now;
struct timeval tval;
time(&now);
gettimeofday(&tval, NULL);
now += (tval.tv_sec ^ tval.tv_usec) + getppid();
now *= gPid;
return(now);
}
/*
* PIDCheck()
*
* See if process is already running and deal with PID file.
*/
int
PIDCheck(const char *filename, int killem)
{
int n_tries;
struct pidfh *pfh = NULL;
/* Sanity */
assert(!lockFp);
/* Atomically open and lock file */
for (n_tries = 0; n_tries < MAX_LOCK_ATTEMPTS; n_tries++)
{
pid_t old_pid;
pfh = pidfile_open(filename, 0644, &old_pid);
if (pfh == NULL) {
if (errno == EEXIST) {
if (!killem) {
Log(LG_ERR, ("already running as process %d", old_pid));
return(-1);
}
if (kill(old_pid, SIGTERM) < 0)
switch (errno) {
case ESRCH:
Log(LG_ERR, ("process %d no longer exists", old_pid));
break;
default:
Perror("%s: kill(%d)", __FUNCTION__, old_pid);
return(-1);
}
/* Wait and try again */
Log(LG_ERR, ("waiting for process %d to die...", old_pid));
sleep(1);
} else {
Perror("cannot open pid file");
return(-1);
}
} else {
pidfile_write(pfh);
break;
}
}
if (n_tries == MAX_LOCK_ATTEMPTS)
{
Log(LG_ERR, ("can't lock %s after %d attempts", filename, n_tries));
return(-1);
}
return(0);
}
/*
* GetInetSocket()
*
* Get a TCP socket and bind it to an address. Set SO_REUSEADDR on the socket.
*/
int
GetInetSocket(int type, struct u_addr *addr, in_port_t port, int block, char *ebuf, size_t len)
{
int sock;
static int one = 1;
struct sockaddr_storage sa;
u_addrtosockaddr(addr,port,&sa);
/* Get and bind non-blocking socket */
if ((sock = socket(sa.ss_family, socktype(type), type == SOCK_STREAM ? IPPROTO_TCP : 0)) < 0)
{
snprintf(ebuf, len, "socket: %s", strerror(errno));
return(-1);
}
(void) fcntl(sock, F_SETFD, 1);
if (!block)
{
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
{
snprintf(ebuf, len, "can't set socket non-blocking: %s", strerror(errno));
close(sock);
return(-1);
}
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
{
snprintf(ebuf, len, "setsockopt: %s", strerror(errno));
close(sock);
return(-1);
}
if (bind(sock, (struct sockaddr *) &sa, sa.ss_len) < 0)
{
snprintf(ebuf, len, "bind: %s", strerror(errno));
close(sock);
return(-1);
}
return(sock);
}
/*
* TcpGetListenPort()
*
* Get port for incoming telnet connections
*/
int
TcpGetListenPort(struct u_addr *addr, in_port_t port, int block)
{
char ebuf[100];
int sock;
int saverrno;
/* Get socket */
if ((sock = GetInetSocket(SOCK_STREAM, addr, port, block, ebuf, sizeof(ebuf))) < 0)
{
saverrno = errno;
Log(LG_ERR, ("%s", ebuf));
errno = saverrno;
return(-1);
}
/* Make socket available for connections */
if (listen(sock, INT_MAX) < 0)
{
Perror("%s: listen", __FUNCTION__);
(void) close(sock);
return(-1);
}
/* Done */
return(sock);
}
/*
* TcpAcceptConnection()
*
* Accept next connection on port
*/
int
TcpAcceptConnection(int sock, struct sockaddr_storage *addr, int block)
{
int new_sock;
socklen_t size=sizeof(struct sockaddr_storage);
/* Accept incoming connection */
memset(addr, 0, sizeof(*addr));
if ((new_sock = accept(sock, (struct sockaddr *) addr, &size)) < 0) {
Perror("%s: accept", __FUNCTION__);
return(-1);
}
#ifdef USE_WRAP
if (Enabled(&gGlobalConf.options, GLOBAL_CONF_TCPWRAPPER)) {
struct request_info req;
request_init(&req, RQ_DAEMON, "mpd", RQ_FILE, new_sock, NULL);
fromhost(&req);
if (!hosts_access(&req)) {
Log(LG_ERR, ("refused connection (tcp-wrapper) from %s",
eval_client(&req)));
close(new_sock);
return(-1);
}
}
#endif
if (!block)
{
(void) fcntl(new_sock, F_SETFD, 1);
if (fcntl(new_sock, F_SETFL, O_NONBLOCK) < 0) {
Perror("%s: fcntl", __FUNCTION__);
return(-1);
}
}
/* Done */
return(new_sock);
}
/*
* ShowMesg()
*/
void
ShowMesg(int log, const char *pref, const char *buf, int len)
{
char *s, mesg[256];
if (len > 0)
{
if (len > (int)(sizeof(mesg) - 1))
len = sizeof(mesg) - 1;
memcpy(mesg, buf, len);
mesg[len] = 0;
for (s = strtok(mesg, "\r\n"); s; s = strtok(NULL, "\r\n"))
Log(log, ("[%s] MESG: %s", pref, s));
}
}
/*
* Bin2Hex()
*/
char *
Bin2Hex(const unsigned char *bin, size_t len)
{
static char hexconvtab[] = "0123456789abcdef";
size_t i, j;
char *buf;
if (len > 0) {
buf = Malloc(MB_UTIL, len * 2 + 1);
for (i = j = 0; i < len; i++) {
buf[j++] = hexconvtab[bin[i] >> 4];
buf[j++] = hexconvtab[bin[i] & 15];
}
buf[j] = 0;
} else {
buf = Malloc(MB_UTIL, 3);
buf[0] = '0';
buf[1] = '0';
buf[2] = 0;
}
return buf;
}
/*
* Hex2Bin()
*/
u_char *
Hex2Bin(char *hexstr)
{
unsigned i;
u_char *binval;
binval = Malloc(MB_UTIL, strlen(hexstr) / 2);
for (i = 0; i < strlen(hexstr) / 2; i++) {
binval[i] = 16 * HexVal(hexstr[2*i]) + HexVal(hexstr[2*i+1]);
}
return binval;
}
static char
HexVal(char c)
{
if (c >= '0' && c <= '9') {
return (c - '0');
} else if (c >= 'a' && c <= 'z') {
return (c - 'a' + 10);
} else if (c >= 'A' && c <= 'Z') {
return (c - 'A' + 10);
} else {
return (-1);
}
}
#ifndef USE_NG_PRED1
/*
* Crc16()
*
* Compute the 16 bit frame check value, per RFC 1171 Appendix B,
* on an array of bytes.
*/
u_short
Crc16(u_short crc, u_char *cp, int len)
{
while (len--)
crc = (crc >> 8) ^ Crc16Table[(crc ^ *cp++) & 0xff];
return(crc);
}
#endif
/*
* GetAnyIpAddress()
*
* Get any non-loopback IP address owned by this machine
* Prefer addresses from non-point-to-point interfaces.
*/
int
GetAnyIpAddress(struct u_addr *ipaddr, const char *ifname)
{
int s, p2p = 0;
struct in_addr ipa = { 0 };
static struct in_addr nipa = { 0 };
static int have_nipa = 0;
struct ifreq *ifr, *ifend;
struct ifreq ifreq;
struct ifconf ifc;
unsigned int buffsize = IFCONF_BUFFSIZE;
/* use cached IP to reduce number of syscalls */
if (ifname == NULL && have_nipa) {
in_addrtou_addr(&nipa, ipaddr);
return(0);
}
/* Get socket */
if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
Perror("%s: Socket creation error", __FUNCTION__);
return(-1);
}
/* Try simple call for the first IP on interface */
if (ifname != NULL) {
strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name));
if (ioctl(s, SIOCGIFADDR, &ifreq) < 0) {
if (errno != ENXIO)
Perror("%s: ioctl(SIOCGIFADDR)", __FUNCTION__);
close(s);
return(-1);
}
ipa = ((struct sockaddr_in *)(void *)&ifreq.ifr_ifru.ifru_addr)->sin_addr;
if ((ntohl(ipa.s_addr)>>24) == 127)
ipa.s_addr = 0; /* We don't like 127.0.0.1 */
}
/* If simple is not enouth try complex call */
if (ipa.s_addr == 0) {
struct ifreq *ifs;
while (1) {
ifc.ifc_len = buffsize;
ifc.ifc_req = ifs = Malloc(MB_UTIL, ifc.ifc_len);
if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
Freee(ifs);
if (errno != ENXIO)
Perror("%s: ioctl(SIOCGIFCONF)", __FUNCTION__);
close(s);
return(-1);
}
/* if used size is too close to allocated size retry with a larger buffer */
if ((unsigned)ifc.ifc_len + 128 < buffsize)
break;
Freee(ifs);
if (buffsize >= IFCONF_BUFFMAXSIZE) {
Log(LG_ERR, ("%s: Max buffer size reached", __FUNCTION__));
close(s);
return(-1);
}
buffsize *= 2;
}
for (ifend = (struct ifreq *)(void *)(ifc.ifc_buf + ifc.ifc_len),
ifr = ifc.ifc_req;
ifr < ifend;
ifr = (struct ifreq *)(void *)((char *) &ifr->ifr_addr
+ MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) {
if (ifr->ifr_addr.sa_family == AF_INET) {
if (ifname!=NULL && strcmp(ifname,ifr->ifr_name))
continue;
/* Check that the interface is up; prefer non-p2p and non-loopback */
strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
continue;
if ((ifreq.ifr_flags & IFF_UP) != IFF_UP)
continue;
if ((ifreq.ifr_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) && ipa.s_addr)
continue;
if ((ntohl(((struct sockaddr_in *)(void *)&ifr->ifr_addr)->sin_addr.s_addr)>>24)==127)
continue;
/* Save IP address and interface name */
ipa = ((struct sockaddr_in *)(void *)&ifr->ifr_addr)->sin_addr;
p2p = (ifreq.ifr_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0;
if (!p2p) break;
}
}
Freee(ifs);
}
close(s);
/* Found? */
if (ipa.s_addr == 0)
return(-1);
if (ifname == NULL) {
nipa = ipa;
have_nipa = 1;
}
in_addrtou_addr(&ipa, ipaddr);
return(0);
}
/*
* GetEther()
*
* Get the hardware address of an interface on the the same subnet as addr.
* If addr == NULL, finds the address of any local ethernet interface.
*/
int
GetEther(struct u_addr *addr, struct sockaddr_dl *hwaddr)
{
int s;
struct ifreq *ifr, *bifr, *ifend, *ifp;
u_int32_t ina, mask, bmask;
struct ifreq ifreq;
struct ifconf ifc;
struct ifreq *ifs;
unsigned int buffsize = IFCONF_BUFFSIZE;
static struct sockaddr_dl nhwaddr;
static int have_nhwaddr = 0;
/* cache value to reduce number of syscalls */
if (addr == NULL && have_nhwaddr) {
memcpy(hwaddr, &nhwaddr,
sizeof(*hwaddr));
return(0);
}
/* Get interface list */
if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
Perror("%s: Socket creation error", __FUNCTION__);
return(-1);
}
while (1) {
ifc.ifc_len = buffsize;
ifc.ifc_req = ifs = Malloc(MB_UTIL, ifc.ifc_len);
if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
Freee(ifs);
Perror("%s: ioctl(SIOCGIFCONF)", __FUNCTION__);
close(s);
return(-1);
}
/* if used size is too close to allocated size retry with a larger buffer */
if ((unsigned)ifc.ifc_len + 128 < buffsize)
break;
Freee(ifs);
if (buffsize >= IFCONF_BUFFMAXSIZE) {
Log(LG_ERR, ("%s: Max buffer size reached", __FUNCTION__));
close(s);
return(-1);
}
buffsize *= 2;
}
/*
* Scan through looking for an interface with an IP
* address on same subnet as `addr'.
*/
bifr = NULL;
bmask = 0;
for (ifend = (struct ifreq *)(void *)(ifc.ifc_buf + ifc.ifc_len),
ifr = ifc.ifc_req;
ifr < ifend;
ifr = (struct ifreq *)(void *)((char *) &ifr->ifr_addr
+ MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) {
if (ifr->ifr_addr.sa_family == AF_INET) {
/* Save IP address and interface name */
ina = ((struct sockaddr_in *)(void *)&ifr->ifr_addr)->sin_addr.s_addr;
strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
ifreq.ifr_addr = ifr->ifr_addr;
/* Check that the interface is up, and not point-to-point or loopback */
if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) {
Log(LG_IFACE2, ("ioctl(SIOCGIFFLAGS, %s): %d", ifr->ifr_name, errno));
continue;
}
if ((ifreq.ifr_flags &
(IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
!= (IFF_UP|IFF_BROADCAST))
continue;
if (addr) {
/* Get its netmask and check that it's on the right subnet */
if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
continue;
mask = ((struct sockaddr_in *)(void *)&ifreq.ifr_addr)->sin_addr.s_addr;
if ((addr->u.ip4.s_addr & mask) != (ina & mask))
continue;
/* Is this the best match? */
if (mask >= bmask) {
bmask = mask;
bifr = ifr;
}
continue;
}
/* OK */
bifr = ifr;
break;
}
}
close(s);
/* Found? */
if (bifr == NULL) {
Freee(ifs);
return(-1);
}
/* Now scan again looking for a link-level address for this interface */
for (ifp = bifr, ifr = ifc.ifc_req; ifr < ifend; ) {
if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
&& ifr->ifr_addr.sa_family == AF_LINK) {
if (addr == NULL) {
memcpy(&nhwaddr, (struct sockaddr_dl *)(void *)&ifr->ifr_addr,
sizeof(*hwaddr));
have_nhwaddr = 1;
}
memcpy(hwaddr, (struct sockaddr_dl *)(void *)&ifr->ifr_addr,
sizeof(*hwaddr));
Freee(ifs);
return(0);
}
ifr = (struct ifreq *)(void *)((char *)&ifr->ifr_addr
+ MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
}
/* Not found! */
Freee(ifs);
return(-1);
}
int
GetPeerEther(struct u_addr *addr, struct sockaddr_dl *hwaddr)
{
int mib[6];
size_t needed;
char *lim, *buf, *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl;
int st, found_entry = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = addr->family;
mib[4] = NET_RT_FLAGS;
#ifdef RTF_LLINFO
mib[5] = RTF_LLINFO;
#else
mib[5] = 0;
#endif
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
Perror("route-sysctl-estimate");
return (0);
}
if (needed == 0) /* empty table */
return 0;
buf = NULL;
for (;;) {
if (buf)
Freee(buf);
buf = Malloc(MB_UTIL, needed);
st = sysctl(mib, 6, buf, &needed, NULL, 0);
if (st == 0 || errno != ENOMEM)
break;
needed += needed / 8;
}
if (st == -1) {
Log(LG_ERR, ("actual retrieval of routing table"));
Freee(buf);
return (0);
}
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
if (addr->u.ip4.s_addr == sin2->sin_addr.s_addr) {
sdl = (struct sockaddr_dl *)(void *)((char *)sin2 + SA_SIZE(sin2));
memcpy(hwaddr, sdl, sdl->sdl_len);
found_entry = 1;
break;
}
}
Freee(buf);
return (found_entry);
}
/*
* Decode ASCII message
*/
void
ppp_util_ascify(char *buf, size_t bsiz, const char *data, size_t len)
{
char *bp;
unsigned i;
for (bp = buf, i = 0; i < len; i++) {
const char ch = (char)data[i];
if (bsiz < 3)
break;
switch (ch) {
case '\t':
*bp++ = '\\';
*bp++ = 't';
bsiz -= 2;
break;
case '\n':
*bp++ = '\\';
*bp++ = 'n';
bsiz -= 2;
break;
case '\r':
*bp++ = '\\';
*bp++ = 'r';
bsiz -= 2;
break;
default:
if (isprint(ch & 0x7f)) {
*bp++ = ch;
bsiz--;
} else {
*bp++ = '^';
*bp++ = '@' + (ch & 0x1f);
bsiz -= 2;
}
break;
}
}
*bp = '\0';
}
#ifndef HAVE_NTOA_R
/*
* Convert a binary representation of an ethernet address to an ASCII string.
*/
char *
ether_ntoa_r(const struct ether_addr *n, char *a)
{
int i;
i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x", n->octet[0],
n->octet[1], n->octet[2], n->octet[3], n->octet[4], n->octet[5]);
if (i < 17)
return (NULL);
return (a);
}
#endif
int
IfaceSetFlag(const char *ifname, int value)
{
struct ifreq my_ifr;
int s;
int flags;
/* Get socket */
if ((s = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
Perror("Can't get socket to set flags");
return(-1);
}
memset(&my_ifr, 0, sizeof(my_ifr));
(void) strlcpy(my_ifr.ifr_name, ifname, sizeof(my_ifr.ifr_name));
if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) {
Perror("ioctl (SIOCGIFFLAGS)");
close(s);
return (-1);
}
flags = (my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16);
if (value < 0) {
value = -value;
flags &= ~value;
} else
flags |= value;
my_ifr.ifr_flags = flags & 0xffff;
my_ifr.ifr_flagshigh = flags >> 16;
if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) {
Perror("ioctl (SIOCSIFFLAGS)");
close(s);
return (-1);
}
close(s);
return (0);
}
/*
* Obtain some data, peer (source) and destination addresses of SOCK_DGRAM IPv4 UDP request.
*/
ssize_t GetDataAddrs(int sock, void *dbuf, size_t dbufsize,
struct sockaddr_storage *peer, socklen_t peer_len,
struct u_addr *addr)
{
struct {
struct msghdr msg;
struct iovec iov;
} b;
union { /* ensure correct alignment for space */
struct cmsghdr cm;
char space[CMSG_SPACE(sizeof(struct in_addr))];
} buf;
struct cmsghdr *p;
ssize_t size;
/* Sanity check */
if (addr->family != AF_INET) {
errno = EAFNOSUPPORT;
return (-1);
}
b.msg.msg_name = peer;
b.msg.msg_namelen = peer_len;
b.msg.msg_iov = &b.iov;
b.msg.msg_iovlen = 1;
b.msg.msg_control = &buf;
b.msg.msg_controllen = sizeof(buf);
b.msg.msg_flags = 0;
b.iov.iov_base = dbuf;
b.iov.iov_len = dbufsize;
if ((size = recvmsg(sock, &b.msg, 0)) < 0) {
Perror("%s: recvmsg: %s", __FUNCTION__, strerror(errno));
return (size);
}
p = CMSG_FIRSTHDR(&b.msg);
if (p && p->cmsg_level == IPPROTO_IP && p->cmsg_type == IP_RECVDSTADDR)
memcpy(&addr->u.ip4, CMSG_DATA(p), sizeof(addr->u.ip4));
return (size);
}
uint16_t GetSystemIfaceMTU(const char *ifname)
{
struct ifreq ifr;
static int sock = -1;
if (sock == -1 &&
(sock = socket(PF_INET, socktype(SOCK_DGRAM), 0)) == -1) {
Perror("[%s] %s: Socket creation error", ifname, __FUNCTION__);
return (0);
}
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) == -1) {
Perror("[%s] %s: SIOCGIFMTU failed", ifname, __FUNCTION__);
return (0);
}
/* Let _exit() close sock */
return (ifr.ifr_mtu);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>