3083 lines
70 KiB
C

/*
* Copyright conserver.com, 2000
*
* Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com)
*/
#include <compat.h>
#include <cutil.h>
#include <version.h>
#include <net/if.h>
#if USE_IPV6
# include <ifaddrs.h>
#endif
#if HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#if HAVE_OPENSSL
# include <openssl/ssl.h>
#endif
int fVerbose = 0, fErrorPrinted = 0;
int isMultiProc = 0;
char *progname = "conserver package";
pid_t thepid = 0;
int fDebug = 0;
STRING *allStrings = (STRING *)0;
int stringCount = 0; /* count of allStrings list */
#if !USE_IPV6
struct in_addr *myAddrs = (struct in_addr *)0;
#endif
char myHostname[MAXHOSTNAME]; /* staff.cc.purdue.edu */
fd_set rinit;
fd_set winit;
int maxfd = 0;
int debugLineNo = 0;
char *debugFileName = (char *)0;
int isMaster = 1;
/* in the routines below (the init code) we can bomb if malloc fails (ksb)
*/
void
OutOfMem(void)
{
static char acNoMem[] = ": out of memory\n";
write(2, progname, strlen(progname));
write(2, acNoMem, sizeof(acNoMem) - 1);
exit(EX_UNAVAILABLE);
}
/* do a general cleanup and exit */
void
Bye(int status)
{
DestroyDataStructures();
#if HAVE_OPENSSL
# if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_free_strings();
# endif
#endif
exit(status);
}
/* This returns a string with the current time in ascii form.
* (same as ctime() but without the \n)
* optionally returns the time in time_t form (pass in NULL if you don't care).
* It's overwritten each time, so use it and forget it.
*/
const char *
StrTime(time_t *ltime)
{
static char curtime[40]; /* just in case ctime() varies */
time_t tyme;
tyme = time((time_t *)0);
StrCpy(curtime, ctime(&tyme), sizeof(curtime));
curtime[24] = '\000'; /* might need to adjust this at some point */
if (ltime != NULL)
*ltime = tyme;
return (const char *)curtime;
}
#define STRING_ALLOC_SIZE 64
char *
BuildStringChar(const char ch, STRING *msg)
{
if (msg->used + 1 >= msg->allocated) {
if (0 == msg->allocated) {
msg->allocated = STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)calloc(1, msg->allocated);
} else {
msg->allocated += STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)realloc(msg->string, msg->allocated);
}
CONDDEBUG((3,
"BuildStringChar(): 0x%lx tried allocating %lu bytes",
(void *)msg, msg->allocated));
if (msg->string == (char *)0)
OutOfMem();
}
if (msg->used) {
msg->string[msg->used - 1] = ch; /* overwrite NULL and */
msg->string[msg->used++] = '\000'; /* increment by one */
CONDDEBUG((3, "BuildStringChar(): 0x%lx added 1 char (%d/%d now)",
(void *)msg, msg->used, msg->allocated));
} else {
msg->string[msg->used++] = ch; /* no NULL, so store stuff */
msg->string[msg->used++] = '\000'; /* and increment by two */
CONDDEBUG((3, "BuildStringChar(): 0x%lx added 2 chars (%d/%d now)",
(void *)msg, msg->used, msg->allocated));
}
return msg->string;
}
char *
BuildString(const char *str, STRING *msg)
{
int len;
if ((char *)0 == str) {
msg->used = 0;
if (msg->string != (char *)0)
msg->string[0] = '\000';
CONDDEBUG((3, "BuildString(): 0x%lx reset", (void *)msg));
return msg->string;
}
if (msg->used) /* string or string + null? */
len = strlen(str);
else
len = strlen(str) + 1;
if (msg->used + len >= msg->allocated) {
if (0 == msg->allocated) {
msg->allocated =
(len / STRING_ALLOC_SIZE +
1) * STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)calloc(1, msg->allocated);
} else {
msg->allocated +=
((msg->used + len - msg->allocated) / STRING_ALLOC_SIZE +
1) * STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)realloc(msg->string, msg->allocated);
}
CONDDEBUG((3, "BuildString(): 0x%lx tried allocating %lu bytes",
(void *)msg, msg->allocated));
if (msg->string == (char *)0)
OutOfMem();
}
/* if msg->used, then len = strlen(), so we need to copy len + 1 to
* get the NULL which we overwrote with the copy */
#if HAVE_MEMCPY
if (msg->used)
memcpy(msg->string + msg->used - 1, str, len + 1);
else
memcpy(msg->string, str, len);
#else
if (msg->used)
bcopy(str, msg->string + msg->used - 1, len + 1);
else
bcopy(str, msg->string, len);
#endif
msg->used += len;
CONDDEBUG((3, "BuildString(): 0x%lx added %d chars (%d/%d now)",
(void *)msg, len, msg->used, msg->allocated));
return msg->string;
}
char *
BuildStringN(const char *str, int n, STRING *msg)
{
int len;
if ((char *)0 == str) {
msg->used = 0;
if (msg->string != (char *)0)
msg->string[0] = '\000';
CONDDEBUG((3, "BuildStringN(): 0x%lx reset", (void *)msg));
return msg->string;
}
if (n <= 0)
return msg->string;
if (msg->used)
len = n;
else
len = n + 1;
if (msg->used + len >= msg->allocated) {
if (0 == msg->allocated) {
msg->allocated =
(len / STRING_ALLOC_SIZE +
1) * STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)calloc(1, msg->allocated);
} else {
msg->allocated +=
((msg->used + len - msg->allocated) / STRING_ALLOC_SIZE +
1) * STRING_ALLOC_SIZE * sizeof(char);
msg->string = (char *)realloc(msg->string, msg->allocated);
}
CONDDEBUG((3, "BuildStringN(): 0x%lx tried allocating %lu bytes",
(void *)msg, msg->allocated));
if (msg->string == (char *)0)
OutOfMem();
}
#if HAVE_MEMCPY
memcpy(msg->string + (msg->used ? msg->used - 1 : 0), str, n);
#else
bcopy(str, msg->string + (msg->used ? msg->used - 1 : 0), n);
#endif
/* add a NULL */
msg->string[(msg->used ? msg->used - 1 : 0) + n] = '\000';
msg->used += len;
CONDDEBUG((3, "BuildStringN(): 0x%lx added %d chars (%d/%d now)",
(void *)msg, len, msg->used, msg->allocated));
return msg->string;
}
void *
MemMove(void *dest, void *src, size_t n)
{
#if HAVE_MEMMOVE
return memmove(dest, src, n);
#else
char *s = src;
char *d = dest;
if (s < d) {
/* Moving from low mem to hi mem; start at end. */
for (s += n, d += n; n > 0; --n)
*--d = *--s;
} else if (s != d) {
/* Moving from hi mem to low mem; start at beginning. */
for (; n > 0; --n)
*d++ = *s++;
}
return dest;
#endif
}
char *
ShiftString(STRING *msg, int n)
{
if (msg == (STRING *)0 || n <= 0 || n > msg->used - 1)
return (char *)0;
MemMove(msg->string, msg->string + n, msg->used - n);
msg->used -= n;
return msg->string;
}
void
InitString(STRING *msg)
{
msg->string = (char *)0;
msg->used = msg->allocated = 0;
}
void
DestroyString(STRING *msg)
{
if (msg->prev == (STRING *)0 && msg->next == (STRING *)0 &&
allStrings != msg) {
CONDDEBUG((1, "DestroyString(): 0x%lx non-pooled string destroyed",
(void *)msg, stringCount));
} else {
if (msg->prev != (STRING *)0)
msg->prev->next = msg->next;
if (msg->next != (STRING *)0)
msg->next->prev = msg->prev;
if (msg == allStrings) {
allStrings = msg->next;
}
stringCount--;
CONDDEBUG((1,
"DestroyString(): 0x%lx string destroyed (count==%d)",
(void *)msg, stringCount));
}
if (msg->allocated)
free(msg->string);
free(msg);
}
STRING *
AllocString(void)
{
STRING *s;
if ((s = (STRING *)calloc(1, sizeof(STRING)))
== (STRING *)0)
OutOfMem();
if (allStrings != (STRING *)0) {
allStrings->prev = s;
s->next = allStrings;
}
allStrings = s;
InitString(s);
stringCount++;
CONDDEBUG((1, "AllocString(): 0x%lx created string #%d", (void *)s,
stringCount));
return s;
}
void
DestroyStrings(void)
{
while (allStrings != (STRING *)0) {
DestroyString(allStrings);
}
}
static STRING *mymsg = (STRING *)0;
char *
BuildTmpString(const char *str)
{
if (mymsg == (STRING *)0)
mymsg = AllocString();
return BuildString(str, mymsg);
}
char *
BuildTmpStringChar(const char c)
{
if (mymsg == (STRING *)0)
mymsg = AllocString();
return BuildStringChar(c, mymsg);
}
char *
ReadLine(FILE *fp, STRING *save, int *iLine)
{
static char buf[1024];
char *wholeline = (char *)0;
char *ret = (char *)0;
int i, buflen, peek, commentCheck = 1, comment = 0;
static STRING *bufstr = (STRING *)0;
static STRING *wholestr = (STRING *)0;
if (bufstr == (STRING *)0)
bufstr = AllocString();
if (wholestr == (STRING *)0)
wholestr = AllocString();
peek = 0;
wholeline = (char *)0;
BuildString((char *)0, bufstr);
BuildString((char *)0, wholestr);
while (save->used || ((ret = fgets(buf, sizeof(buf), fp)) != (char *)0)
|| peek) {
/* If we have a previously saved line, use it instead */
if (save->used) {
StrCpy(buf, save->string, sizeof(buf));
BuildString((char *)0, save);
}
if (peek) {
/* End of file? Never mind. */
if (ret == (char *)0)
break;
/* If we don't have a line continuation and we've seen
* some worthy data
*/
if (!isspace((int)buf[0]) && (wholeline != (char *)0)) {
BuildString((char *)0, save);
BuildString(buf, save);
break;
}
peek = 0;
}
if (commentCheck) {
for (i = 0; buf[i] != '\000'; i++)
if (!isspace((int)buf[i]))
break;
if (buf[i] == '#') {
comment = 1;
commentCheck = 0;
} else if (buf[i] != '\000') {
commentCheck = 0;
}
}
/* Check for EOL */
buflen = strlen(buf);
if ((buflen >= 1) && (buf[buflen - 1] == '\n')) {
(*iLine)++; /* Finally have a whole line */
if (comment == 0 && commentCheck == 0) {
/* Finish off the chunk without the \n */
buf[buflen - 1] = '\000';
BuildString(buf, bufstr);
wholeline = BuildString(bufstr->string, wholestr);
}
peek = 1;
comment = 0;
commentCheck = 1;
BuildString((char *)0, bufstr);
} else {
/* Save off the partial chunk */
BuildString(buf, bufstr);
}
}
/* If we hit the EOF and weren't peeking ahead
* and it's not a comment
*/
if (!peek && (ret == (char *)0) && (comment == 0) &&
(commentCheck == 0)) {
(*iLine)++;
wholeline = BuildString(bufstr->string, wholestr);
}
CONDDEBUG((1, "ReadLine(): returning <%s>",
(wholeline != (char *)0) ? wholeline : "<NULL>"));
return wholeline;
}
/* show a character as a string so the user cannot mistake it for (ksb)
* another
*/
char *
FmtCtl(int ci, STRING *pcIn)
{
unsigned char c;
BuildString((char *)0, pcIn);
c = ci & 0xff;
if (c > 127) {
c -= 128;
BuildString("M-", pcIn);
}
if (c < ' ' || c == '\177') {
BuildStringChar('^', pcIn);
BuildStringChar(c ^ 0100, pcIn);
} else if (c == ' ') {
BuildString("<space>", pcIn);
} else if (c == '^') {
BuildString("<circumflex>", pcIn);
} else if (c == '\\') {
BuildString("<backslash>", pcIn);
} else {
BuildStringChar(c, pcIn);
}
return pcIn->string;
}
void
FmtCtlStr(char *pcIn, int len, STRING *pcOut)
{
unsigned char c;
BuildString((char *)0, pcOut);
if (pcIn == (char *)0)
return;
if (len < 0)
len = strlen(pcIn);
for (; len; len--, pcIn++) {
c = *pcIn & 0xff;
if (c > 127) {
c -= 128;
BuildString("M-", pcOut);
}
if (c < ' ' || c == '\177') {
BuildStringChar('^', pcOut);
BuildStringChar(c ^ 0100, pcOut);
} else {
BuildStringChar(c, pcOut);
}
}
}
void
Debug(int level, char *fmt, ...)
{
va_list ap;
if (fDebug < level)
return;
va_start(ap, fmt);
if (isMultiProc)
fprintf(stderr, "[%s] %s (%lu): DEBUG: [%s:%d] ",
StrTime((time_t *)0), progname, (unsigned long)thepid,
debugFileName, debugLineNo);
else
fprintf(stderr, "%s: DEBUG: [%s:%d] ", progname, debugFileName,
debugLineNo);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
void
Error(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (isMultiProc)
fprintf(stderr, "[%s] %s (%lu): ERROR: ", StrTime((time_t *)0),
progname, (unsigned long)thepid);
else
fprintf(stderr, "%s: ", progname);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
fErrorPrinted = 1;
}
void
Msg(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (isMultiProc)
fprintf(stdout, "[%s] %s (%lu): ", StrTime((time_t *)0), progname,
(unsigned long)thepid);
else
fprintf(stdout, "%s: ", progname);
vfprintf(stdout, fmt, ap);
fprintf(stdout, "\n");
va_end(ap);
}
void
Verbose(char *fmt, ...)
{
va_list ap;
if (!fVerbose)
return;
va_start(ap, fmt);
if (isMultiProc)
fprintf(stdout, "[%s] %s (%lu): INFO: ", StrTime((time_t *)0),
progname, (unsigned long)thepid);
else
fprintf(stdout, "%s: ", progname);
vfprintf(stdout, fmt, ap);
fprintf(stdout, "\n");
va_end(ap);
}
void
SimpleSignal(int sig, RETSIGTYPE(*disp) (int))
{
#if HAVE_SIGACTION
struct sigaction sa;
sa.sa_handler = disp;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(sig, &sa, NULL);
#else
signal(sig, disp);
#endif
}
int
GetMaxFiles(void)
{
int mf;
#if HAVE_SYSCONF
mf = sysconf(_SC_OPEN_MAX);
#else
# if HAVE_GETRLIMIT
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
mf = rl.rlim_cur;
# else
# if HAVE_GETDTABLESIZE
mf = getdtablesize();
# else
# ifndef OPEN_MAX
# define OPEN_MAX 64
# endif /* !OPEN_MAX */
mf = OPEN_MAX;
# endif /* HAVE_GETDTABLESIZE */
# endif /* HAVE_GETRLIMIT */
#endif /* HAVE_SYSCONF */
#ifdef FD_SETSIZE
if (FD_SETSIZE <= mf) {
mf = (FD_SETSIZE - 1);
}
#endif
CONDDEBUG((1, "GetMaxFiles(): maxfiles=%d", mf));
return mf;
}
/* Routines for the generic I/O stuff for conserver. This will handle
* all open(), close(), read(), and write() calls.
*/
/* This encapsulates a regular file descriptor in a CONSFILE
* object. Returns a CONSFILE pointer to that object.
*/
CONSFILE *
FileOpenFD(int fd, enum consFileType type)
{
CONSFILE *cfp;
if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE)))
== (CONSFILE *)0)
OutOfMem();
cfp->ftype = type;
cfp->fd = fd;
cfp->wbuf = AllocString();
#if HAVE_OPENSSL
cfp->ssl = (SSL *)0;
cfp->waitForRead = cfp->waitForWrite = FLAGFALSE;
#endif
#if DEBUG_CONSFILE_IO
{
char buf[1024];
sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname,
(unsigned long)thepid, fd);
if ((cfp->debugwfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugwfd, buf, strlen(buf));
}
sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname,
(unsigned long)thepid, fd);
if ((cfp->debugrfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugrfd, buf, strlen(buf));
}
}
#endif
CONDDEBUG((2, "FileOpenFD(): encapsulated fd %d type %d", fd, type));
return cfp;
}
/* This encapsulates a pipe pair in a CONSFILE
* object. Returns a CONSFILE pointer to that object.
*/
CONSFILE *
FileOpenPipe(int fd, int fdout)
{
CONSFILE *cfp;
if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE)))
== (CONSFILE *)0)
OutOfMem();
cfp->ftype = simplePipe;
cfp->fd = fd;
cfp->fdout = fdout;
cfp->wbuf = AllocString();
#if HAVE_OPENSSL
cfp->ssl = (SSL *)0;
cfp->waitForRead = cfp->waitForWrite = FLAGFALSE;
#endif
#if DEBUG_CONSFILE_IO
{
char buf[1024];
sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname,
(unsigned long)thepid, fdout);
if ((cfp->debugwfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugwfd, buf, strlen(buf));
}
sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname,
(unsigned long)thepid, fd);
if ((cfp->debugrfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugrfd, buf, strlen(buf));
}
}
#endif
CONDDEBUG((2, "FileOpenPipe(): encapsulated pipe pair fd %d and fd %d",
fd, fdout));
return cfp;
}
/* This is to "unencapsulate" the file descriptor */
int
FileUnopen(CONSFILE *cfp)
{
int retval = 0;
if (cfp == (CONSFILE *)0)
return 0;
switch (cfp->ftype) {
case simpleFile:
retval = cfp->fd;
break;
case simplePipe:
retval = cfp->fd;
break;
case simpleSocket:
retval = cfp->fd;
break;
#if HAVE_OPENSSL
case SSLSocket:
retval = -1;
break;
#endif
default:
retval = -1;
break;
}
CONDDEBUG((2, "FileUnopen(): unopened fd %d", cfp->fd));
DestroyString(cfp->wbuf);
#if DEBUG_CONSFILE_IO
if (cfp->debugwfd != -1)
close(cfp->debugwfd);
if (cfp->debugrfd != -1)
close(cfp->debugrfd);
#endif
free(cfp);
return retval;
}
/* This opens a file like open(2). Returns a CONSFILE pointer
* or a (CONSFILE *)0 on error
*/
CONSFILE *
FileOpen(const char *path, int flag, int mode)
{
CONSFILE *cfp;
int fd;
if (-1 == (fd = open(path, flag, mode))) {
CONDDEBUG((2, "FileOpen(): failed to open `%s'", path));
return (CONSFILE *)0;
}
if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE)))
== (CONSFILE *)0)
OutOfMem();
cfp->ftype = simpleFile;
cfp->fd = fd;
cfp->wbuf = AllocString();
#if HAVE_OPENSSL
cfp->ssl = (SSL *)0;
cfp->waitForRead = cfp->waitForWrite = FLAGFALSE;
#endif
#if DEBUG_CONSFILE_IO
{
char buf[1024];
sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname,
(unsigned long)thepid, fd);
if ((cfp->debugwfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugwfd, buf, strlen(buf));
}
sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname,
(unsigned long)thepid, fd);
if ((cfp->debugrfd =
open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) {
sprintf(buf, "[---- STARTED - %s ----]\n",
StrTime((time_t *)0));
write(cfp->debugrfd, buf, strlen(buf));
}
}
#endif
CONDDEBUG((2, "FileOpen(): opened `%s' as fd %d", path, fd));
return cfp;
}
/* Unless otherwise stated, returns the same values as close(2).
* The CONSFILE object passed in *CANNOT* be used once calling
* this function - even if there was an error.
*/
int
FileClose(CONSFILE **pcfp)
{
CONSFILE *cfp;
int retval = 0;
#if defined(__CYGWIN__)
struct linger lingeropt;
#endif
cfp = *pcfp;
if (cfp == (CONSFILE *)0)
return 0;
switch (cfp->ftype) {
case simpleFile:
do {
retval = close(cfp->fd);
} while (retval == -1 && errno == EINTR);
break;
case simplePipe:
do {
retval = close(cfp->fd);
} while (retval == -1 && errno == EINTR);
do {
retval = close(cfp->fdout);
} while (retval == -1 && errno == EINTR);
break;
case simpleSocket:
#if defined(__CYGWIN__)
/* flush out the client socket - set it to blocking,
* then write to it
*/
SetFlags(cfp->fd, 0, O_NONBLOCK);
/* sent it a byte - guaranteed to block - ensure delivery
* of prior data yeah - this is a bit paranoid - try
* without this at first
*/
/* write(cfp->fd, "\n", 1); */
/* this is the guts of the workaround for Winsock close bug */
shutdown(cfp->fd, 1);
/* enable lingering */
lingeropt.l_onoff = 1;
lingeropt.l_linger = 15;
setsockopt(cfp->fd, SOL_SOCKET, SO_LINGER, &lingeropt,
sizeof(lingeropt));
#endif
do {
retval = close(cfp->fd);
} while (retval == -1 && errno == EINTR);
break;
#if HAVE_OPENSSL
case SSLSocket:
CONDDEBUG((2,
"FileClose(): performing a SSL_shutdown() on fd %d",
cfp->fd));
SSL_shutdown(cfp->ssl);
CONDDEBUG((2, "FileClose(): performing a SSL_free() on fd %d",
cfp->fd));
SSL_free(cfp->ssl);
/* set the sucker back to a simpleSocket and recall so we
* do all that special stuff we oh so love...and make sure
* we return so we don't try and free(0). -bryan
*/
cfp->ftype = simpleSocket;
return FileClose(pcfp);
#endif
default:
retval = -1;
break;
}
if (cfp->ftype == simplePipe) {
CONDDEBUG((2, "FileClose(): closed fd %d/%d", cfp->fd,
cfp->fdout));
} else {
CONDDEBUG((2, "FileClose(): closed fd %d", cfp->fd));
}
DestroyString(cfp->wbuf);
#if DEBUG_CONSFILE_IO
if (cfp->debugwfd != -1)
close(cfp->debugwfd);
if (cfp->debugrfd != -1)
close(cfp->debugrfd);
#endif
free(cfp);
*pcfp = (CONSFILE *)0;
return retval;
}
/* returns: -1 on error or eof, >= 0 for valid reads */
int
FileRead(CONSFILE *cfp, void *buf, int len)
{
int retval = -1;
if (cfp->errored == FLAGTRUE)
return -1;
switch (cfp->ftype) {
case simpleFile:
case simplePipe:
case simpleSocket:
while (retval < 0) {
if ((retval = read(cfp->fd, buf, len)) <= 0) {
if (retval == 0) {
retval = -1;
break;
}
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
/* must be non-blocking - so stop */
retval = 0;
break;
}
Error("FileRead(): fd %d: %s", cfp->fd,
strerror(errno));
retval = -1;
break;
}
#if DEBUG_CONSFILE_IO
if (cfp->debugrfd != -1)
write(cfp->debugrfd, buf, retval);
#endif
}
break;
#if HAVE_OPENSSL
case SSLSocket:
if (cfp->waitForWrite == FLAGTRUE) {
cfp->waitForWrite = FLAGFALSE;
if (cfp->wbuf->used <= 1)
FD_CLR(cfp->fd, &winit);
}
retval = SSL_read(cfp->ssl, buf, len);
switch (SSL_get_error(cfp->ssl, retval)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
retval = 0;
break;
case SSL_ERROR_WANT_WRITE:
cfp->waitForWrite = FLAGTRUE;
FD_SET(cfp->fd, &winit);
retval = 0;
break;
default:
Error("FileRead(): SSL error on fd %d", cfp->fd);
/* fall through */
case SSL_ERROR_ZERO_RETURN:
retval = -1;
CONDDEBUG((2,
"FileRead(): performing a SSL_shutdown() on fd %d",
cfp->fd));
SSL_shutdown(cfp->ssl);
CONDDEBUG((2,
"FileRead(): performing a SSL_free() on fd %d",
cfp->fd));
SSL_free(cfp->ssl);
cfp->ssl = (SSL *)0;
cfp->ftype = simpleSocket;
break;
}
# if DEBUG_CONSFILE_IO
if (cfp->debugrfd != -1)
write(cfp->debugrfd, buf, retval);
# endif
break;
#endif
default:
retval = -1;
break;
}
if (retval >= 0) {
CONDDEBUG((2, "FileRead(): read %d byte%s from fd %d", retval,
(retval == 1) ? "" : "s", cfp->fd));
if (fDebug && buf != (char *)0) {
static STRING *tmpString = (STRING *)0;
if (tmpString == (STRING *)0)
tmpString = AllocString();
BuildString((char *)0, tmpString);
if (retval > 30) {
FmtCtlStr(buf, 30, tmpString);
CONDDEBUG((2, "FileRead(): read `%s'... from fd %d",
tmpString->string, cfp->fd));
} else {
FmtCtlStr(buf, retval, tmpString);
CONDDEBUG((2, "FileRead(): read `%s' from fd %d",
tmpString->string, cfp->fd));
}
}
} else {
CONDDEBUG((2,
"FileRead(): failed attempted read of %d byte%s from fd %d",
len, (len == 1) ? "" : "s", cfp->fd));
}
if (retval < 0)
cfp->errored = FLAGTRUE;
return retval;
}
/* returns: -1 on error or eof, >= 0 for valid reads */
int
FileWrite(CONSFILE *cfp, FLAG bufferonly, char *buf, int len)
{
int len_orig = len;
int len_out = 0;
int retval = 0;
int fdout = 0;
if (cfp->ftype == simplePipe)
fdout = cfp->fdout;
else
fdout = cfp->fd;
if (cfp->errored == FLAGTRUE) {
if (cfp->wbuf->used > 1)
BuildString((char *)0, cfp->wbuf);
FD_CLR(fdout, &winit);
return -1;
}
if (len < 0 && buf != (char *)0)
len = strlen(buf);
if (fDebug && len > 0 && buf != (char *)0) {
static STRING *tmpString = (STRING *)0;
if (tmpString == (STRING *)0)
tmpString = AllocString();
BuildString((char *)0, tmpString);
if (len > 30) {
FmtCtlStr(buf, 30, tmpString);
CONDDEBUG((2, "FileWrite(): sending `%s'... to fd %d",
tmpString->string, fdout));
} else {
FmtCtlStr(buf, len, tmpString);
CONDDEBUG((2, "FileWrite(): sending `%s' to fd %d",
tmpString->string, fdout));
}
}
/* save the data */
if (len > 0 && buf != (char *)0) {
if (cfp->quoteiac == FLAGTRUE) {
int l, o;
for (o = l = 0; l < len; l++) {
if (buf[l] == (char)OB_IAC) {
BuildStringN(buf + o, l + 1 - o, cfp->wbuf);
BuildStringChar((char)OB_IAC, cfp->wbuf);
o = l + 1;
}
}
if (o < len)
BuildStringN(buf + o, len - o, cfp->wbuf);
} else
BuildStringN(buf, len, cfp->wbuf);
}
if (bufferonly == FLAGTRUE)
return 0;
/* point at the local data */
buf = cfp->wbuf->string;
len = cfp->wbuf->used - 1;
/* if we don't have any, forget it */
if (buf == (char *)0 || len <= 0)
return 0;
/* so, we could be blocking or non-blocking. since we may be able
* to block, we'll just keep trying to write while we have data and
* stop when we hit an error or flush all the data.
*/
switch (cfp->ftype) {
case simplePipe:
case simpleFile:
case simpleSocket:
while (len > 0) {
if ((retval = write(fdout, buf, len)) < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
/* must be non-blocking - so stop */
retval = 0;
break;
}
retval = -1;
/* i believe, as of 8.0.8, we need to just ignore
* this and actually produce the error message
* below. perhaps we'll have a lot of extra
* FileWrite() errors, perhaps not. things shouldn't
* just close down and cause errors in normal cases,
* right?!? -bryan
* maybe not right now, actually. i'm going to check
* the return code of FileWrite() on the "important"
* things and let the others silently fail and have
* the FileRead() catch problems - like it has been
* doing. i really should be checking all the return
* codes...and i'm sure i'll get there eventually.
*/
if (errno == EPIPE)
break;
Error("FileWrite(): fd %d: %s", fdout,
strerror(errno));
break;
}
#if DEBUG_CONSFILE_IO
if (cfp->debugwfd != -1)
write(cfp->debugwfd, buf, retval);
#endif
buf += retval;
len -= retval;
len_out += retval;
}
break;
#if HAVE_OPENSSL
case SSLSocket:
if (cfp->waitForRead == FLAGTRUE)
cfp->waitForRead = FLAGFALSE;
while (len > 0) {
/* in theory, SSL_write always returns 'len' on success
* so the while() loop is a noop. but, just in case i
* read something wrong, we treat SSL_write like write().
*/
retval = SSL_write(cfp->ssl, buf, len);
switch (SSL_get_error(cfp->ssl, retval)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
cfp->waitForRead = FLAGTRUE;
retval = len_out = 0;
break;
case SSL_ERROR_WANT_WRITE:
retval = len_out = 0;
break;
default:
Error("FileWrite(): SSL error on fd %d", cfp->fd);
/* fall through */
case SSL_ERROR_ZERO_RETURN:
retval = -1;
CONDDEBUG((2,
"FileWrite(): performing a SSL_shutdown() on fd %d",
cfp->fd));
SSL_shutdown(cfp->ssl);
CONDDEBUG((2,
"FileWrite(): performing a SSL_free() on fd %d",
cfp->fd));
SSL_free(cfp->ssl);
cfp->ssl = (SSL *)0;
cfp->ftype = simpleSocket;
break;
}
if (retval <= 0)
break;
# if DEBUG_CONSFILE_IO
if (cfp->debugwfd != -1)
write(cfp->debugwfd, buf, retval);
# endif
buf += retval;
len -= retval;
len_out += retval;
}
break;
#endif
default:
retval = -1;
break;
}
/* so, if we saw an error, just bail...all is done anyway */
if (retval >= 0) {
if (len_out > 0) {
/* save the rest for later */
if (len > 0) {
ShiftString(cfp->wbuf, len_out);
} else {
BuildString((char *)0, cfp->wbuf);
}
}
retval = len_out;
}
if (retval < 0) {
if (cfp->wbuf->used > 1)
BuildString((char *)0, cfp->wbuf);
cfp->errored = FLAGTRUE;
}
if (cfp->wbuf->used <= 1)
FD_CLR(fdout, &winit);
else {
FD_SET(fdout, &winit);
CONDDEBUG((2, "FileWrite(): buffered %d byte%s for fd %d",
(cfp->wbuf->used <= 1) ? 0 : cfp->wbuf->used,
(cfp->wbuf->used <= 1) ? "" : "s", fdout));
}
if (retval >= 0) {
CONDDEBUG((2, "FileWrite(): wrote %d byte%s to fd %d", retval,
(retval == 1) ? "" : "s", fdout));
} else {
CONDDEBUG((2, "FileWrite(): write of %d byte%s to fd %d: %s",
len_orig, (retval == -1) ? "" : "s", fdout,
strerror(errno)));
}
return retval;
}
int
FileCanRead(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd)
{
#if HAVE_OPENSSL
int fdout;
#endif
if (cfp == (CONSFILE *)0)
return 0;
#if HAVE_OPENSSL
if (cfp->ftype == simplePipe)
fdout = cfp->fdout;
else
fdout = cfp->fd;
#endif
return ((FD_ISSET(cfp->fd, prfd)
#if HAVE_OPENSSL
&& cfp->waitForRead != FLAGTRUE) || (fdout >= 0 &&
FD_ISSET(fdout, pwfd)
&& cfp->waitForWrite ==
FLAGTRUE
#endif
));
}
int
FileCanWrite(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd)
{
int fdout;
if (cfp == (CONSFILE *)0)
return 0;
if (cfp->ftype == simplePipe)
fdout = cfp->fdout;
else
fdout = cfp->fd;
return ((FD_ISSET(fdout, pwfd)
#if HAVE_OPENSSL
&& cfp->waitForWrite != FLAGTRUE) || (FD_ISSET(cfp->fd, prfd)
&& cfp->waitForRead ==
FLAGTRUE
#endif
));
}
int
FileBufEmpty(CONSFILE *cfp)
{
if (cfp == (CONSFILE *)0)
return 1;
return (cfp->wbuf->used <= 1);
}
void
VWrite(CONSFILE *cfp, FLAG bufferonly, STRING *str, char *fmt, va_list ap)
{
int s, l, e;
char c;
int fmtlen = 0;
int fmtpre = 0;
short padzero = 0;
short sawdot = 0;
static STRING *msg = (STRING *)0;
static STRING *output = (STRING *)0;
short flong = 0, fneg = 0, fminus = 0;
if (fmt == (char *)0 || (cfp == (CONSFILE *)0 && str == (STRING *)0))
return;
if (msg == (STRING *)0)
msg = AllocString();
if (output == (STRING *)0)
output = AllocString();
BuildString((char *)0, output);
for (e = s = l = 0; (c = fmt[s + l]) != '\000'; l++) {
if (e == 0 && c == '%') {
e = 1;
BuildStringN(fmt + s, l, output);
s += l;
l = 0;
continue;
}
if (e) {
unsigned long i;
int u;
char *p;
char cc;
if (c >= '0' && c <= '9') {
if (sawdot == 0) {
if (c == '0' && fmtlen == 0)
padzero = 1;
fmtlen = fmtlen * 10 + (c - '0');
} else {
fmtpre = fmtpre * 10 + (c - '0');
}
} else {
switch (c) {
case '.':
sawdot = 1;
continue;
case '-':
fminus = 1;
continue;
case 'h':
/* noop since shorts are promoted to int in va_arg */
continue;
case 'l':
flong = 1;
continue;
case '%':
BuildStringChar('%', output);
break;
case 'c':
cc = (char)va_arg(ap, int);
BuildStringChar(cc, output);
break;
case 's':
p = va_arg(ap, char *);
if (p == (char *)0)
p = "(null)";
{
int l = strlen(p);
int c;
if (fmtpre > 0 && fmtpre < l)
l = fmtpre;
if (fminus != 0)
BuildStringN(p, l, output);
for (c = l; c < fmtlen; c++)
BuildStringChar(' ', output);
if (fminus == 0)
BuildStringN(p, l, output);
}
break;
case 'd':
i = (flong ? va_arg(ap, long) : (long)
va_arg(ap, int));
if ((long)i < 0) {
fneg = 1;
i = -i;
}
goto number;
case 'u':
i = (flong ? va_arg(ap, unsigned long)
: (unsigned long)va_arg(ap, unsigned int));
number:
BuildString((char *)0, msg);
while (i >= 10) {
BuildStringChar((i % 10) + '0', msg);
i /= 10;
}
BuildStringChar(i + '0', msg);
if (fneg)
BuildStringChar('-', msg);
if (fmtpre > 0) {
padzero = 0;
if (fmtpre > fmtlen)
fmtlen = fmtpre;
while (msg->used - 1 < fmtpre)
BuildStringChar('0', msg);
}
/* reverse the text to put it in forward order
*/
u = msg->used - 1;
for (i = 0; i < u / 2; i++) {
char temp;
temp = msg->string[i];
msg->string[i]
= msg->string[u - i - 1];
msg->string[u - i - 1] = temp;
}
{
int l = msg->used - 1;
if (fminus != 0)
BuildString(msg->string, output);
for (; l < fmtlen; l++) {
if (padzero == 0 || fminus != 0)
BuildStringChar(' ', output);
else
BuildStringChar('0', output);
}
if (fminus == 0)
BuildString(msg->string, output);
}
break;
case 'X':
case 'x':
i = (flong ? va_arg(ap, unsigned long)
: (unsigned long)va_arg(ap, unsigned int));
BuildString((char *)0, msg);
while (i >= 16) {
if (i % 16 >= 10)
BuildStringChar((i % 16) - 10 +
(c == 'x' ? 'a' : 'A'),
msg);
else
BuildStringChar((i % 16) + '0', msg);
i /= 16;
}
if (i >= 10)
BuildStringChar(i - 10 +
(c == 'x' ? 'a' : 'A'), msg);
else
BuildStringChar(i + '0', msg);
if (fmtpre > 0) {
padzero = 0;
if (fmtpre > fmtlen)
fmtlen = fmtpre;
while (msg->used - 1 < fmtpre)
BuildStringChar('0', msg);
}
/* reverse the text to put it in forward order
*/
u = msg->used - 1;
for (i = 0; i < u / 2; i++) {
char temp;
temp = msg->string[i];
msg->string[i]
= msg->string[u - i - 1];
msg->string[u - i - 1] = temp;
}
{
int l = msg->used - 1;
if (fminus != 0)
BuildString(msg->string, output);
for (; l < fmtlen; l++) {
if (padzero == 0 || fminus != 0)
BuildStringChar(' ', output);
else
BuildStringChar('0', output);
}
if (fminus == 0)
BuildString(msg->string, output);
}
break;
default:
Error
("VWrite(): unknown conversion character `%c' in `%s'",
c, fmt);
break;
}
s += l + 1;
l = -1;
e = flong = fneg = fminus = 0;
fmtlen = fmtpre = sawdot = padzero = 0;
}
}
}
if (l)
BuildStringN(fmt + s, l, output);
if (str != (STRING *)0)
BuildString((char *)0, str);
if (output->used > 1) {
if (str != (STRING *)0)
BuildStringN(output->string, output->used - 1, str);
if (cfp != (CONSFILE *)0)
FileWrite(cfp, bufferonly, output->string, output->used - 1);
}
}
char *
BuildStringPrint(STRING *str, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
VWrite((CONSFILE *)0, FLAGFALSE, str, fmt, ap);
va_end(ap);
if (str == (STRING *)0)
return (char *)0;
else
return str->string;
}
char *
BuildTmpStringPrint(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (mymsg == (STRING *)0)
mymsg = AllocString();
VWrite((CONSFILE *)0, FLAGFALSE, mymsg, fmt, ap);
va_end(ap);
return mymsg->string;
}
void
FileVWrite(CONSFILE *cfp, FLAG bufferonly, char *fmt, va_list ap)
{
VWrite(cfp, bufferonly, (STRING *)0, fmt, ap);
}
void
FilePrint(CONSFILE *cfp, FLAG bufferonly, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
FileVWrite(cfp, bufferonly, fmt, ap);
va_end(ap);
}
/* Unless otherwise stated, returns the same values as fstat(2) */
int
FileStat(CONSFILE *cfp, struct stat *buf)
{
int retval = 0;
if (cfp->errored == FLAGTRUE)
return -1;
switch (cfp->ftype) {
case simpleFile:
retval = fstat(cfp->fd, buf);
break;
case simplePipe:
retval = -1;
break;
case simpleSocket:
retval = fstat(cfp->fd, buf);
break;
#if HAVE_OPENSSL
case SSLSocket:
retval = -1;
break;
#endif
default:
retval = -1;
break;
}
if (retval < 0)
cfp->errored = FLAGTRUE;
return retval;
}
/* Unless otherwise stated, returns the same values as lseek(2) */
int
FileSeek(CONSFILE *cfp, off_t offset, int whence)
{
int retval = 0;
if (cfp->errored == FLAGTRUE)
return -1;
switch (cfp->ftype) {
case simpleFile:
retval = lseek(cfp->fd, offset, whence);
break;
case simplePipe:
retval = -1;
break;
case simpleSocket:
retval = lseek(cfp->fd, offset, whence);
break;
#if HAVE_OPENSSL
case SSLSocket:
retval = -1;
break;
#endif
default:
retval = -1;
break;
}
if (retval < 0)
cfp->errored = FLAGTRUE;
return retval;
}
/* Returns the file descriptor number of the underlying file */
int
FileFDNum(CONSFILE *cfp)
{
int retval = 0;
if (cfp == (CONSFILE *)0)
return -1;
switch (cfp->ftype) {
case simpleFile:
retval = cfp->fd;
break;
case simplePipe:
retval = cfp->fd;
break;
case simpleSocket:
retval = cfp->fd;
break;
#if HAVE_OPENSSL
case SSLSocket:
retval = cfp->fd;
break;
#endif
default:
retval = cfp->fd;
break;
}
return retval;
}
/* Returns the file descriptor number of the underlying file */
int
FileFDOutNum(CONSFILE *cfp)
{
if (cfp == (CONSFILE *)0 || cfp->ftype != simplePipe)
return -1;
return cfp->fdout;
}
/* Returns the file type */
enum consFileType
FileGetType(CONSFILE *cfp)
{
switch (cfp->ftype) {
case simpleFile:
return simpleFile;
case simplePipe:
return simplePipe;
case simpleSocket:
return simpleSocket;
#if HAVE_OPENSSL
case SSLSocket:
return SSLSocket;
#endif
default:
return nothing;
}
}
/* Sets the file type */
void
FileSetType(CONSFILE *cfp, enum consFileType type)
{
cfp->ftype = type;
}
/* Sets the file quoting method */
void
FileSetQuoteIAC(CONSFILE *cfp, FLAG flag)
{
cfp->quoteiac = flag;
}
FLAG
FileSawQuoteSusp(CONSFILE *cfp)
{
FLAG r = cfp->sawiacsusp;
cfp->sawiacsusp = FLAGFALSE;
return r;
}
FLAG
FileSawQuoteExec(CONSFILE *cfp)
{
FLAG r = cfp->sawiacexec;
cfp->sawiacexec = FLAGFALSE;
return r;
}
FLAG
FileSawQuoteAbrt(CONSFILE *cfp)
{
FLAG r = cfp->sawiacabrt;
cfp->sawiacabrt = FLAGFALSE;
return r;
}
FLAG
FileSawQuoteGoto(CONSFILE *cfp)
{
FLAG r = cfp->sawiacgoto;
cfp->sawiacgoto = FLAGFALSE;
return r;
}
#if HAVE_OPENSSL
/* Get the SSL instance */
SSL *
FileGetSSL(CONSFILE *cfp)
{
return cfp->ssl;
}
/* Sets the SSL instance */
void
FileSetSSL(CONSFILE *cfp, SSL *ssl)
{
cfp->ssl = ssl;
}
/* return -1 on error, 0 for "wait" state, 1 for success */
int
FileCanSSLAccept(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd)
{
if (cfp == (CONSFILE *)0)
return 0;
return ((FD_ISSET(cfp->fd, prfd) && cfp->waitForRead == FLAGTRUE) ||
(FD_ISSET(cfp->fd, pwfd) && cfp->waitForWrite == FLAGTRUE) ||
(cfp->waitForRead != FLAGTRUE &&
cfp->waitForWrite != FLAGTRUE));
}
/* return -1 on error, 0 for "wait" state, 1 for success */
int
FileSSLAccept(CONSFILE *cfp)
{
int retval;
if (cfp->waitForWrite == FLAGTRUE) {
cfp->waitForWrite = FLAGFALSE;
if (cfp->wbuf->used <= 1)
FD_CLR(cfp->fd, &winit);
}
cfp->waitForRead = FLAGFALSE;
CONDDEBUG((1, "FileSSLAccept(): about to SSL_accept() for fd %d",
cfp->fd));
retval = SSL_accept(cfp->ssl);
switch (SSL_get_error(cfp->ssl, retval)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
cfp->waitForRead = FLAGTRUE;
return 0;
case SSL_ERROR_WANT_WRITE:
cfp->waitForWrite = FLAGTRUE;
FD_SET(cfp->fd, &winit);
return 0;
default:
Error("FileSSLAccept(): SSL error on fd %d", cfp->fd);
/* fall through */
case SSL_ERROR_ZERO_RETURN:
SSL_free(cfp->ssl);
cfp->ssl = (SSL *)0;
cfp->ftype = simpleSocket;
return -1;
}
cfp->ftype = SSLSocket;
CONDDEBUG((1, "FileSSLAccept(): SSL Connection: %s :: %s",
SSL_get_cipher_version(cfp->ssl),
SSL_get_cipher_name(cfp->ssl)));
return 1;
}
#endif
/* Unless otherwise stated, returns the same values as send(2) */
int
FileSend(CONSFILE *cfp, const void *msg, size_t len, int flags)
{
int retval = 0;
int fdout;
if (cfp->ftype == simplePipe)
fdout = cfp->fdout;
else
fdout = cfp->fd;
if (cfp->errored == FLAGTRUE) {
FD_CLR(fdout, &winit);
return -1;
}
switch (cfp->ftype) {
case simpleFile:
retval = send(fdout, msg, len, flags);
break;
case simplePipe:
retval = send(fdout, msg, len, flags);
break;
case simpleSocket:
retval = send(fdout, msg, len, flags);
break;
#if HAVE_OPENSSL
case SSLSocket:
retval = send(fdout, msg, len, flags);
break;
#endif
default:
retval = -1;
break;
}
if (retval < 0) {
cfp->errored = FLAGTRUE;
FD_CLR(fdout, &winit);
}
return retval;
}
/* replace trailing space with '\000' in a string and return
* a pointer to the start of the non-space part
*/
char *
PruneSpace(char *string)
{
char *p;
char *head = (char *)0;
char *tail = (char *)0;
/* Don't do much if it's crap */
if (string == (char *)0 || *string == '\000')
return string;
/* Now for the tricky part - search the string */
for (p = string; *p != '\000'; p++) {
if (isspace((int)(*p))) {
if (tail == (char *)0)
tail = p; /* possible end of string */
} else {
if (head == (char *)0)
head = p; /* found the start */
tail = (char *)0; /* reset tail */
}
}
if (tail != (char *)0)
*tail = '\000';
if (head != (char *)0)
return head;
else
return string;
}
#if !USE_IPV6
/* fills the myAddrs array with host interface addresses */
void
ProbeInterfaces(in_addr_t bindAddr)
{
# ifdef SIOCGIFCONF
struct ifconf ifc;
struct ifreq *ifr;
# ifdef SIOCGIFFLAGS
struct ifreq ifrcopy;
# endif
# ifdef SIOCGIFNUM
int nifr;
# endif
int sock;
int r = 0, m = 0;
int bufsize = 2048;
int count = 0;
/* if we use -M, just fill the array with that interface */
if (bindAddr != INADDR_ANY) {
myAddrs = (struct in_addr *)calloc(2, sizeof(struct in_addr));
if (myAddrs == (struct in_addr *)0)
OutOfMem();
# if HAVE_MEMCPY
memcpy(&(myAddrs[0].s_addr), &bindAddr, sizeof(in_addr_t));
# else
bcopy(&bindAddr, &(myAddrs[0].s_addr), sizeof(in_addr_t));
# endif
Verbose("interface address %s (-M option)", inet_ntoa(myAddrs[0]));
return;
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
Error("ProbeInterfaces(): socket(): %s", strerror(errno));
Bye(EX_OSERR);
}
# ifdef SIOCGIFNUM
if (ioctl(sock, SIOCGIFNUM, &nifr) == 0)
bufsize = nifr * sizeof(struct ifreq) + 512;
# endif
while (bufsize) {
ifc.ifc_len = bufsize;
ifc.ifc_req = (struct ifreq *)malloc(ifc.ifc_len);
if (ifc.ifc_req == (struct ifreq *)0)
OutOfMem();
if (ioctl(sock, SIOCGIFCONF, &ifc) != 0 && errno != EINVAL) {
free(ifc.ifc_req);
close(sock);
Error("ProbeInterfaces(): ioctl(SIOCGIFCONF): %s",
strerror(errno));
Bye(EX_OSERR);
}
/* if the return size plus a 512 byte "buffer zone" is less than
* the buffer we passed in (bufsize), we're done. otherwise
* allocate a bigger buffer and try again. with a too-small
* buffer, some implementations (freebsd) will fill the buffer
* best it can (leaving a gap - returning <=bufsize) and others
* (linux) will return a buffer length the same size as passed
* in (==bufsize). so, we'll assume a 512 byte gap would have
* been big enough to put one more record and as long as we have
* that "buffer zone", we should have all the interfaces.
* so, solaris returns EINVAL if it's too small, so we catch that
* above and since if_len is bufsize, it'll loop again.
*/
if (ifc.ifc_len + 512 < bufsize)
break;
free(ifc.ifc_req);
bufsize += 2048;
}
/* this is probably way overkill, but better to kill a few bytes
* than loop through looking for valid interfaces that are up
* twice, huh?
*/
count = ifc.ifc_len / sizeof(*ifr);
CONDDEBUG((1, "ProbeInterfaces(): ifc_len==%d max_count==%d",
ifc.ifc_len, count));
/* set up myAddrs array */
if (myAddrs != (struct in_addr *)0)
free(myAddrs);
myAddrs = (struct in_addr *)0;
if (count == 0) {
free(ifc.ifc_req);
close(sock);
return;
}
myAddrs = (struct in_addr *)calloc(count + 1, sizeof(struct in_addr));
if (myAddrs == (struct in_addr *)0)
OutOfMem();
for (m = r = 0; r < ifc.ifc_len;) {
struct sockaddr *sa;
ifr = (struct ifreq *)&ifc.ifc_buf[r];
sa = (struct sockaddr *)&ifr->ifr_addr;
/* don't use less than a ifreq sized chunk */
if ((ifc.ifc_len - r) < sizeof(*ifr))
break;
# ifdef HAVE_SA_LEN
if (sa->sa_len > sizeof(ifr->ifr_ifru))
r += sizeof(ifr->ifr_name) + sa->sa_len;
else
# endif
r += sizeof(*ifr);
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
/* make sure the address isn't 0.0.0.0, which is how we
* signal the end of our list
*/
if (
# if HAVE_MEMCMP
memcmp(&(myAddrs[m]), &(sin->sin_addr),
sizeof(struct in_addr))
# else
bcmp(&(myAddrs[m]), &(sin->sin_addr),
sizeof(struct in_addr))
# endif
== 0)
continue;
# ifdef SIOCGIFFLAGS
/* make sure the interface is up */
ifrcopy = *ifr;
if ((ioctl(sock, SIOCGIFFLAGS, &ifrcopy) == 0) &&
((ifrcopy.ifr_flags & IFF_UP) == 0))
continue;
# endif
CONDDEBUG((1, "ProbeInterfaces(): name=%s addr=%s",
ifr->ifr_name, inet_ntoa(sin->sin_addr)));
# if HAVE_MEMCPY
memcpy(&myAddrs[m], &(sin->sin_addr), sizeof(struct in_addr));
# else
bcopy(&(sin->sin_addr), &myAddrs[m], sizeof(struct in_addr));
# endif
Verbose("interface address %s (%s)", inet_ntoa(myAddrs[m]),
ifr->ifr_name);
m++;
}
}
if (m == 0) {
free(myAddrs);
myAddrs = (struct in_addr *)0;
}
close(sock);
free(ifc.ifc_req);
# else /* use the hostname like the old code did (but use all addresses!) */
int count;
struct hostent *he;
/* if we use -M, just fill the array with that interface */
if (bindAddr != INADDR_ANY) {
myAddrs = (struct in_addr *)calloc(2, sizeof(struct in_addr));
if (myAddrs == (struct in_addr *)0)
OutOfMem();
# if HAVE_MEMCPY
memcpy(&(myAddrs[0].s_addr), &bindAddr, sizeof(in_addr_t));
# else
bcopy(&bindAddr, &(myAddrs[0].s_addr), sizeof(in_addr_t));
# endif
Verbose("interface address %s (-M option)", inet_ntoa(myAddrs[0]));
return;
}
Verbose("using hostname for interface addresses");
if ((struct hostent *)0 == (he = gethostbyname(myHostname))) {
Error("ProbeInterfaces(): gethostbyname(%s): %s", myHostname,
hstrerror(h_errno));
return;
}
if (4 != he->h_length || AF_INET != he->h_addrtype) {
Error
("ProbeInterfaces(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)",
myHostname, he->h_length, AF_INET, he->h_addrtype);
return;
}
for (count = 0; he->h_addr_list[count] != (char *)0; count++);
if (myAddrs != (struct in_addr *)0)
free(myAddrs);
myAddrs = (struct in_addr *)0;
if (count == 0)
return;
myAddrs = (struct in_addr *)calloc(count + 1, sizeof(struct in_addr));
if (myAddrs == (struct in_addr *)0)
OutOfMem();
for (count--; count >= 0; count--) {
# if HAVE_MEMCPY
memcpy(&(myAddrs[count].s_addr), he->h_addr_list[count],
he->h_length);
# else
bcopy(he->h_addr_list[count], &(myAddrs[count].s_addr),
he->h_length);
# endif
Verbose("interface address %s (hostname address)",
inet_ntoa(myAddrs[count]));
}
# endif
}
#endif /* USE_IPV6 */
int
IsMe(char *id)
{
#if USE_IPV6
int ret = 0;
int error;
struct addrinfo hints;
struct addrinfo *res, *rp;
struct ifaddrs *myAddrs, *ifa;
void *a, *b;
size_t len;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
/* get IP based on hostname */
error = getaddrinfo(id, NULL, &hints, &res);
if (error) {
perror(gai_strerror(error));
return 0;
}
/* get list of all addresses on system */
error = getifaddrs(&myAddrs);
if (error) {
perror("getifaddrs failed");
return 0;
}
/* try to find a match */
for (ifa = myAddrs; ifa != NULL; ifa = ifa->ifa_next) {
/* skip interfaces without address or in down state */
if (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP))
continue;
for (rp = res; rp != NULL; rp = rp->ai_next) {
if (ifa->ifa_addr->sa_family == rp->ai_addr->sa_family) {
/* I really don't like to hardcode it but we have to */
if (ifa->ifa_addr->sa_family == AF_INET) { /* IPv4 */
a = &(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
b = &(((struct sockaddr_in *)rp->ai_addr)->sin_addr);
len = sizeof(struct in_addr);
} else { /* IPv6 */
a = &(((struct sockaddr_in6 *)ifa->
ifa_addr)->sin6_addr);
b = &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr);
len = sizeof(struct in6_addr);
}
if (
# if HAVE_MEMCMP
memcmp(a, b, len)
# else
bcmp(a, b, len)
# endif
== 0) {
ret = 1;
goto done;
}
}
}
}
done:
freeaddrinfo(res);
freeifaddrs(myAddrs);
CONDDEBUG((1, "IsMe: ret %d id %s", ret, id));
return ret;
#else
int j, i;
struct hostent *he;
in_addr_t addr;
# if HAVE_INET_ATON
struct in_addr inetaddr;
# endif
/* check for ip address match */
# if HAVE_INET_ATON
if (inet_aton(id, &inetaddr) != 0) {
addr = inetaddr.s_addr;
# else
addr = inet_addr(id);
if (addr != (in_addr_t) (-1)) {
# endif
for (i = 0;
myAddrs != (struct in_addr *)0 &&
myAddrs[i].s_addr != (in_addr_t) 0; i++) {
if (
# if HAVE_MEMCMP
memcmp(&(myAddrs[i].s_addr), &addr, sizeof(addr))
# else
bcmp(&(myAddrs[i].s_addr), &addr, sizeof(addr))
# endif
== 0)
return 1;
}
return 0;
}
/* check for ip match of hostname */
if ((struct hostent *)0 == (he = gethostbyname(id))) {
Error("IsMe(): gethostbyname(%s): %s", id, hstrerror(h_errno));
return 0;
}
if (4 != he->h_length || AF_INET != he->h_addrtype) {
Error
("IsMe(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)",
id, he->h_length, AF_INET, he->h_addrtype);
return 0;
}
for (j = 0; he->h_addr_list[j] != (char *)0; j++) {
for (i = 0;
myAddrs != (struct in_addr *)0 &&
myAddrs[i].s_addr != (in_addr_t) 0; i++) {
if (
# if HAVE_MEMCMP
memcmp(&(myAddrs[i].s_addr), he->h_addr_list[j],
he->h_length)
# else
bcmp(&(myAddrs[i].s_addr), he->h_addr_list[j],
he->h_length)
# endif
== 0)
return 1;
}
}
return 0;
#endif /* USE_IPV6 */
}
#if HAVE_OPENSSL
/* Unless otherwise stated, returns the same values as send(2) */
int
SSLVerifyCallback(int ok, X509_STORE_CTX *store)
{
char data[256];
if (ok) {
if (fDebug) {
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
CONDDEBUG((1,
"SSLVerifyCallback(): info of certificate at depth: %d",
depth));
X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
CONDDEBUG((1, "SSLVerifyCallback(): issuer = %s", data));
X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
CONDDEBUG((1, "SSLVerifyCallback(): subject = %s", data));
}
} else {
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
Error("SSLVerifyCallback(): error with certificate at depth: %d",
depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
Error("SSLVerifyCallback(): issuer = %s", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
Error("SSLVerifyCallback(): subject = %s", data);
Error("SSLVerifyCallback(): error #%d: %s", err,
X509_verify_cert_error_string(err));
}
return ok;
}
#endif
int
SetFlags(int fd, int s, int c)
{
int flags;
if ((flags = fcntl(fd, F_GETFL)) >= 0) {
flags |= s;
flags &= ~c;
if (fcntl(fd, F_SETFL, flags) < 0) {
Error("SetFlags(): fcntl(%u,F_SETFL): %s", fd,
strerror(errno));
return 0;
}
} else {
Error("SetFlags(): fcntl(%u,F_GETFL): %s", fd, strerror(errno));
return 0;
}
return 1;
}
char *
StrDup(const char *msg)
{
int len;
char *buf;
if (msg == (char *)0)
return (char *)0;
len = strlen(msg) + 1;
buf = malloc(len);
if (buf == (char *)0)
return (char *)0;
#if HAVE_MEMCPY
memcpy(buf, msg, len);
#else
bcopy(msg, buf, len);
#endif
return buf;
}
char *
StringChar(STRING *msg, int offset, char c)
{
int o;
if (msg == (STRING *)0 || msg->used <= 1 || offset < 0 ||
offset > msg->used)
return (char *)0;
for (o = offset; o != msg->used; o++) {
if (msg->string[o] == c)
return &(msg->string[o]);
}
return (char *)0;
}
/* this takes a buffer, and returns the number of characters to use,
* which goes up to the first OB_IAC character sequence (that isn't
* OB_IAC/OB_IAC). if it is an OB_IAC sequence, it sets the flag and
* returns zero. if it's invalid args, we return -1.
* so <0 == no data, 0 == check flags, >0 number of chars to use
* this *WILL* modify the buffer (OB_IAC sequences get extracted/shrunk)
*/
int
ParseIACBuf(CONSFILE *cfp, void *msg, int *len)
{
int l = 0;
unsigned char *b = msg;
if (*len <= 0)
return -1;
if (cfp->quoteiac != FLAGTRUE)
return *len;
/* split OB_IAC/char pair OR OB_IAC at start */
if (cfp->sawiac == FLAGTRUE || b[0] == OB_IAC) {
int i = 1;
if (cfp->sawiac == FLAGTRUE) {
i = 0;
cfp->sawiac = FLAGFALSE;
}
if (i == *len) { /* only thing is OB_IAC */
cfp->sawiac = FLAGTRUE;
return -1;
}
if (b[i] == OB_SUSP)
cfp->sawiacsusp = FLAGTRUE;
else if (b[i] == OB_EXEC)
cfp->sawiacexec = FLAGTRUE;
else if (b[i] == OB_ABRT)
cfp->sawiacabrt = FLAGTRUE;
else if (b[i] == OB_GOTO)
cfp->sawiacgoto = FLAGTRUE;
else {
if (b[i] != OB_IAC)
Error
("ParseIACBuf(): fd %d: unrecognized quoted-OB_IAC char",
cfp->fd, strerror(errno));
l = 1;
}
*len = *len - i - 1 + l;
MemMove(b, b + i + 1 - l, *len);
if (l == 0)
return 0;
}
for (; l < *len; l++) {
if (b[l] == OB_IAC) {
if (l + 1 == *len)
return l;
if (b[l + 1] == OB_IAC) {
--(*len);
MemMove(b + l, b + l + 1, *len - l);
} else {
return l;
}
}
}
return l;
}
/* the format of the file should be as follows
*
* <section keyword> [section name] {
* <item keyword> [item value];
* .
* .
* }
*
* whitespace gets retained in [section name], and [item value]
* values. for example,
*
* users bryan todd ;
*
* will give users the value of 'bryan todd'. the leading and
* trailing whitespace is nuked, but the middle stuff isn't.
*
* a little note about the 'state' var...
* START = before <section keyword>
* NAME = before [section name]
* LEFTB = before left curly brace
* KEY = before <item keyword>
* VALUE = before [item value]
* SEMI = before semi-colon
*/
typedef enum states {
START,
NAME,
LEFTB,
KEY,
VALUE,
SEMI
} STATES;
typedef enum tokens {
DONE,
LEFTBRACE,
RIGHTBRACE,
SEMICOLON,
WORD,
INCLUDE
} TOKEN;
int line = 1; /* current line number */
char *file = (char *)0;
TOKEN
GetWord(FILE *fp, int *line, short spaceok, STRING *word)
{
int c;
short backslash = 0;
short quote = 0;
short comment = 0;
short sawQuote = 0;
short quotedBackslash = 0;
char *include = "include";
short checkInc = -1;
/* checkInc == -3, saw #include
* == -2, saw nothin'
* == -1, saw \n or start of file
* == 0, saw "\n#"
*/
BuildString((char *)0, word);
while ((c = fgetc(fp)) != EOF) {
if (c == '\n') {
(*line)++;
if (checkInc == -2)
checkInc = -1;
}
if (comment) {
if (c == '\n')
comment = 0;
if (checkInc >= 0) {
if (include[checkInc] == '\000') {
if (isspace(c))
checkInc = -3;
} else if (c == include[checkInc])
checkInc++;
else
checkInc = -2;
} else if (checkInc == -3) {
static STRING *fname = (STRING *)0;
if (fname == (STRING *)0)
fname = AllocString();
if (fname->used != 0 || !isspace(c)) {
if (c == '\n') {
if (fname->used > 0) {
while (fname->used > 1 && isspace((int)
(fname->
string
[fname->
used -
2])))
fname->used--;
if (fname->used > 0)
fname->string[fname->used - 1] = '\000';
}
checkInc = -2;
if (fname->used > 0) {
BuildString((char *)0, word);
BuildString(fname->string, word);
BuildString((char *)0, fname);
return INCLUDE;
}
} else
BuildStringChar(c, fname);
}
}
continue;
}
if (backslash) {
BuildStringChar(c, word);
backslash = 0;
continue;
}
if (quote) {
if (c == '"') {
if (quotedBackslash) {
BuildStringChar(c, word);
quotedBackslash = 0;
} else
quote = 0;
} else {
if (quotedBackslash) {
BuildStringChar('\\', word);
quotedBackslash = 0;
}
if (c == '\\')
quotedBackslash = 1;
else
BuildStringChar(c, word);
}
continue;
}
if (c == '\\') {
backslash = 1;
} else if (c == '#') {
comment = 1;
if (checkInc == -1)
checkInc = 0;
} else if (c == '"') {
quote = 1;
sawQuote = 1;
} else if (isspace(c)) {
if (word->used <= 1)
continue;
if (spaceok) {
BuildStringChar(c, word);
continue;
}
gotword:
while (word->used > 1 &&
isspace((int)(word->string[word->used - 2])))
word->used--;
if (word->used > 0)
word->string[word->used - 1] = '\000';
return WORD;
} else if (c == '{') {
if (word->used <= 1 && !sawQuote) {
BuildStringChar(c, word);
return LEFTBRACE;
} else {
ungetc(c, fp);
goto gotword;
}
} else if (c == '}') {
if (word->used <= 1 && !sawQuote) {
BuildStringChar(c, word);
return RIGHTBRACE;
} else {
ungetc(c, fp);
goto gotword;
}
} else if (c == ';') {
if (word->used <= 1 && !sawQuote) {
BuildStringChar(c, word);
return SEMICOLON;
} else {
ungetc(c, fp);
goto gotword;
}
} else {
BuildStringChar(c, word);
}
}
/* this should only happen in rare cases */
if (quotedBackslash) {
BuildStringChar('\\', word);
quotedBackslash = 0;
}
/* if we saw "valid" data, it's a word */
if (word->used > 1 || sawQuote)
goto gotword;
return DONE;
}
void
ParseFile(char *filename, FILE *fp, int level)
{
/* things that should be used between recursions */
static STATES state = START;
static STRING *word = (STRING *)0;
static short spaceok = 0;
static int secIndex = 0;
static int keyIndex = 0;
/* other stuff that's local to each recursion */
char *p;
TOKEN token = DONE;
int nextline = 1; /* "next" line number */
if (level >= 10) {
if (isMaster)
Error("ParseFile(): nesting too deep, not parsing `%s'",
filename);
return;
}
/* set some globals */
line = 1;
file = filename;
/* if we're parsing the base file, set static vars */
if (level == 0) {
state = START;
spaceok = 0;
secIndex = 0;
keyIndex = 0;
}
/* initialize local things */
if (word == (STRING *)0)
word = AllocString();
while ((token = GetWord(fp, &nextline, spaceok, word)) != DONE) {
if (token == INCLUDE) {
FILE *lfp;
if ((FILE *)0 == (lfp = fopen(word->string, "r"))) {
if (isMaster)
Error("ParseFile(): fopen(%s): %s", word->string,
strerror(errno));
} else {
char *fname;
/* word gets destroyed, so save the name */
fname = StrDup(word->string);
ParseFile(fname, lfp, level + 1);
fclose(lfp);
free(fname);
}
} else {
switch (state) {
case START:
switch (token) {
case WORD:
for (secIndex = 0;
(p = sections[secIndex].id) != (char *)0;
secIndex++) {
if (strcasecmp(word->string, p) == 0) {
CONDDEBUG((1,
"ReadCfg(): got keyword '%s' [%s:%d]",
word->string, file, line));
state = NAME;
break;
}
}
if (state == START) {
if (isMaster)
Error("invalid keyword '%s' [%s:%d]",
word->string, file, line);
}
break;
case LEFTBRACE:
case RIGHTBRACE:
case SEMICOLON:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
case NAME:
switch (token) {
case WORD:
(*sections[secIndex].begin) (word->string);
state = LEFTB;
break;
case RIGHTBRACE:
if (isMaster)
Error("premature token '%s' [%s:%d]",
word->string, file, line);
state = START;
break;
case LEFTBRACE:
case SEMICOLON:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
case LEFTB:
switch (token) {
case LEFTBRACE:
state = KEY;
break;
case RIGHTBRACE:
if (isMaster)
Error("premature token '%s' [%s:%d]",
word->string, file, line);
(*sections[secIndex].abort) ();
state = START;
break;
case SEMICOLON:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case WORD:
if (isMaster)
Error("invalid word '%s' [%s:%d]",
word->string, file, line);
break;
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
case KEY:
switch (token) {
case WORD:
for (keyIndex = 0;
(p =
sections[secIndex].items[keyIndex].id) !=
(char *)0; keyIndex++) {
if (strcasecmp(word->string, p) == 0) {
CONDDEBUG((1,
"got keyword '%s' [%s:%d]",
word->string, file, line));
state = VALUE;
break;
}
}
if (state == KEY) {
if (isMaster)
Error("invalid keyword '%s' [%s:%d]",
word->string, file, line);
}
break;
case RIGHTBRACE:
(*sections[secIndex].end) ();
state = START;
break;
case LEFTBRACE:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case SEMICOLON:
if (isMaster)
Error("premature token '%s' [%s:%d]",
word->string, file, line);
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
case VALUE:
switch (token) {
case WORD:
(*sections[secIndex].items[keyIndex].
reg) (word->string);
state = SEMI;
break;
case SEMICOLON:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
state = KEY;
break;
case RIGHTBRACE:
if (isMaster)
Error("premature token '%s' [%s:%d]",
word->string, file, line);
(*sections[secIndex].abort) ();
state = START;
break;
case LEFTBRACE:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
case SEMI:
switch (token) {
case SEMICOLON:
state = KEY;
break;
case RIGHTBRACE:
if (isMaster)
Error("premature token '%s' [%s:%d]",
word->string, file, line);
(*sections[secIndex].abort) ();
state = START;
break;
case LEFTBRACE:
if (isMaster)
Error("invalid token '%s' [%s:%d]",
word->string, file, line);
break;
case WORD:
if (isMaster)
Error("invalid word '%s' [%s:%d]",
word->string, file, line);
break;
case DONE: /* just shutting up gcc */
case INCLUDE: /* just shutting up gcc */
break;
}
break;
}
switch (state) {
case NAME:
case VALUE:
spaceok = 1;
break;
case KEY:
case LEFTB:
case START:
case SEMI:
spaceok = 0;
break;
}
}
line = nextline;
}
if (level == 0) {
int i;
/* check for proper ending of file and do any cleanup */
switch (state) {
case START:
break;
case KEY:
case LEFTB:
case VALUE:
case SEMI:
(*sections[secIndex].abort) ();
/* fall through */
case NAME:
if (isMaster)
Error("premature EOF seen [%s:%d]", file, line);
break;
}
/* now clean up all the temporary space used */
for (i = 0; sections[i].id != (char *)0; i++) {
(*sections[i].destroy) ();
}
}
}
void
ProcessSubst(SUBST *s, char **repl, char **str, char *name, char *id)
{
/*
* (CONSENT *pCE) and (char **repl) are used when a replacement is to
* actually happen...repl is the string to munch, pCE holds the data.
*
* (char **str) is used to store a copy of (char *id), if it passes
* the format check.
*
* the idea is that this is first called when the config file is read,
* putting the result in (char **str). then we call it again, near
* the end, permuting (char **repl) with values from (CONSENT *pCE) with
* the saved string now coming in as (char *id). got it?
*
* you could pass all arguments in...then both types of actions occur.
*/
char *p;
char *repfmt[256];
unsigned short repnum;
int i;
enum repstate {
REP_BEGIN,
REP_LTR,
REP_EQ,
REP_INT,
REP_END
} state;
if (s == (SUBST *)0) {
Error("ProcessSubst(): WTF? No substitute support structure?!?!");
Bye(EX_SOFTWARE);
}
if (str != (char **)0) {
if (*str != (char *)0) {
free(*str);
*str = (char *)0;
}
}
if ((id == (char *)0) || (*id == '\000'))
return;
repnum = 0;
state = REP_BEGIN;
for (i = 0; i < 256; i++)
repfmt[i] = (char *)0;
for (p = id; *p != '\000'; p++) {
switch (state) {
case REP_BEGIN:
/* must be printable */
if (*p == ',' || !isgraph((int)(*p)))
goto subst_err;
/* make sure we haven't seen this replacement char yet */
repnum = (unsigned short)(*p);
if (repfmt[repnum] != (char *)0) {
if (isMaster)
Error
("substitution characters of `%s' option are the same [%s:%d]",
name, file, line);
return;
}
state = REP_LTR;
break;
case REP_LTR:
if (*p != '=')
goto subst_err;
state = REP_EQ;
break;
case REP_EQ:
repfmt[repnum] = p;
if (s->token(*(repfmt[repnum])) != ISNOTHING)
state = REP_INT;
else
goto subst_err;
break;
case REP_INT:
if (*p == 'd' || *p == 'x' || *p == 'X' || *p == 'a' ||
*p == 'A') {
if (s->token(*(repfmt[repnum])) != ISNUMBER)
goto subst_err;
state = REP_END;
} else if (*p == 's') {
if (s->token(*(repfmt[repnum])) != ISSTRING)
goto subst_err;
state = REP_END;
} else if (!isdigit((int)(*p)))
goto subst_err;
break;
case REP_END:
if (*p != ',')
goto subst_err;
state = REP_BEGIN;
break;
}
}
if (state != REP_END) {
subst_err:
if (isMaster)
Error
("invalid `%s' specification `%s' (char #%d: `%c') [%s:%d]",
name, id, (p - id) + 1, *p, file, line);
return;
}
if (str != (char **)0) {
if ((*str = StrDup(id)) == (char *)0)
OutOfMem();
}
if (s != (SUBST *)0 && repl != (char **)0 && *repl != (char *)0) {
static STRING *result = (STRING *)0;
if (result == (STRING *)0)
result = AllocString();
BuildString((char *)0, result);
for (p = *repl; *p != '\000'; p++) {
if (repfmt[(unsigned short)(*p)] != (char *)0) {
char *r = repfmt[(unsigned short)(*p)];
int plen = 0;
char *c = (char *)0;
int o = 0;
if (s->token(*r) == ISSTRING) {
/* check the pattern for a length */
if (isdigit((int)(*(r + 1))))
plen = atoi(r + 1);
/* this should never return zero, but just in case */
if ((*s->value) (*r, &c, (int *)0) == 0)
c = "";
plen -= strlen(c);
/* pad it out, if necessary */
for (i = 0; i < plen; i++)
BuildStringChar(' ', result);
/* throw in the string */
BuildString(c, result);
} else {
int i = 0;
unsigned short port = 0;
unsigned short base = 0;
int padzero = 0;
static STRING *num = (STRING *)0;
if (num == (STRING *)0)
num = AllocString();
BuildString((char *)0, num);
/* this should never return zero, but just in case */
if ((*s->value) (*r, (char **)0, &i) == 0)
port = 0;
else
port = (unsigned short)i;
/* check the pattern for a length and padding */
for (c = r + 1; *c != '\000'; c++)
if (!isdigit((int)(*c)))
break;
if (c != r + 1) {
plen = atoi(r + 1);
padzero = (r[1] == '0');
}
/* check for base */
switch (*c) {
case 'd':
base = 10;
break;
case 'x':
case 'X':
base = 16;
break;
case 'a':
case 'A':
base = 36;
break;
default:
return;
}
while (port >= base) {
if (port % base >= 10)
BuildStringChar((port % base) - 10 +
((*c == 'x' ||
*c == 'a') ? 'a' : 'A'),
num);
else
BuildStringChar((port % base) + '0', num);
port /= base;
}
if (port >= 10)
BuildStringChar(port - 10 +
((*c == 'x' ||
*c == 'a') ? 'a' : 'A'), num);
else
BuildStringChar(port + '0', num);
/* if we're supposed to be a certain length, pad it */
while (num->used - 1 < plen) {
if (padzero == 0)
BuildStringChar(' ', num);
else
BuildStringChar('0', num);
}
/* reverse the text to put it in forward order */
o = num->used - 1;
for (i = 0; i < o / 2; i++) {
char temp;
temp = num->string[i];
num->string[i]
= num->string[o - i - 1];
num->string[o - i - 1] = temp;
}
BuildStringN(num->string, o, result);
}
} else
BuildStringChar(*p, result);
}
free(*repl);
if ((*repl = StrDup(result->string)) == (char *)0)
OutOfMem();
}
return;
}
char *
MyVersion(void)
{
static STRING *version = (STRING *)0;
if (version != (STRING *)0)
return version->string;
version = AllocString();
BuildStringPrint(version, "%s %d.%d.%d", VERSION_TEXT, VERSION_MAJOR,
VERSION_MINOR, VERSION_REV);
return version->string;
}
unsigned int
AtoU(char *str)
{
unsigned int v;
int i;
v = 0;
for (i = 0; isdigit((int)str[i]); i++) {
v *= 10;
v += str[i] - '0';
}
return v;
}
void
StrCpy(char *dst, const char *src, unsigned int size)
{
#ifdef HAVE_STRLCPY
strlcpy(dst, src, size);
#else
strcpy(dst, src);
#endif
}