/*-
* Copyright (c) 2002, 2003 Peter Pentchev
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "urelay.h"
#include "ur_err.h"
__RINGID("$Ringlet: c/net/urelay/urelay.c,v 1.3 2003/07/16 10:21:04 roam Exp $");
int verbose, quiet;
char ur_verstr[64] = "urelay";
char *msgsrc, *msgdst;
char *ignsrc, *igndst;
static ur_err_t ur_init(int, char *[]);
static ur_err_t ur_close(void);
static ur_err_t ur_doit(int, char *[]);
static void usage(void);
static ur_err_t version(void);
/*
* Function:
* ur_makeversion() - generate version string
* Inputs:
* none
* Returns:
* ur_err_t
* Modifies:
* ur_verstr
*/
static ur_err_t
ur_makeversion(void) {
snprintf(ur_verstr, sizeof(ur_verstr), "urelay v%d.%d"
#if UR_VER_PRE
"-pre%d"
#endif /* UR_VER_PRE */
#if UR_VER_PATCH
"p%d"
#endif /* UR_VER_PATCH */
,
UR_VER_MAJ, UR_VER_MIN
#if UR_VER_PRE
, UR_VER_PRE
#endif /* UR_VER_PRE */
#if UR_VER_PATCH
, UR_VER_PATCH
#endif /* UR_VER_PATCH */
);
return (UR_ERR_NONE);
}
static ur_err_t
ur_nonblock(int fd) {
int flags;
if (fcntl(fd, F_GETFL, &flags) == -1)
return (UR_ERR_FCNTL_GET);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
return (UR_ERR_FCNTL_SET);
return (UR_ERR_NONE);
}
/*
* Function:
* ur_init - general-purpose init function
* Inputs:
* argc, argv - main() args for option processing
* Returns:
* ur_err_t
* CMDLINE
* Modifies:
* verbose, quiet
*/
static ur_err_t
ur_init(int argc, char *argv[]) {
int ch, helpq, versq;
ur_err_t err;
helpq = versq = 0;
ur_makeversion();
/* Process cmdline options */
opterr = 0;
while (ch = getopt(argc, argv, UR_OPTSTR), ch != EOF)
switch (ch) {
case 'h':
helpq = 1;
break;
case 'M':
msgsrc = optarg;
break;
case 'm':
msgdst = optarg;
break;
case 'S':
ignsrc = optarg;
break;
case 's':
igndst = optarg;
break;
case 'q':
quiet++;
break;
case 'V':
versq = 1;
break;
case 'v':
verbose++;
break;
case '?':
default:
usage();
/* NOTREACHED */
}
argc -= optind; argv += optind;
if (versq)
version();
if (helpq)
usage();
if (versq)
/* no need for "|| helpq": usage() never returns */
exit(UR_ERR_NONE);
if ((err = ur_nonblock(0)) != UR_ERR_NONE)
return (err);
if ((err = ur_nonblock(6)) != UR_ERR_NONE)
return (err);
return (UR_ERR_NONE);
}
/*
* Function:
* ur_close - general-purpose shutdown function
* Inputs:
* none
* Returns:
* ur_err_t
* nothing for the present (FIXME)
* Modifies:
* nothing for the present (FIXME)
*/
static ur_err_t
ur_close(void) {
return (UR_ERR_NONE);
}
/*
* Function:
* usage - startup help info
* Inputs:
* none
* Returns:
* nothing
* Modifies:
* nothing, writes to stdout and exits
*/
void
usage(void) {
static const char *msg[] = {
"usage: urelay [-hqVv]",
"\t-h\tprint this help text and exit;",
"\t-q\tquiet operation; multiple -q's make it even more quiet;",
"\t-V\tprint version information and exit;",
"\t-v\tverbose operation; multiple -v's increase verbosity level.",
NULL
};
unsigned i;
for (i = 0; msg[i] != NULL; i++)
fprintf(stderr, "%s\n", msg[i]);
exit(UR_ERR_CMDLINE);
}
/*
* function:
* ur_version - output version info
* Inputs:
* none
* Returns:
* UR_ERR_NONE
* Modifies:
* nothing, writes to stdout
*/
ur_err_t
version(void) {
printf("%s\n", ur_verstr);
if (verbose) {
printf("%s\n", "Built on " __DATE__ ", " __TIME__);
#ifdef __GNUC__
if (verbose > 1)
printf("%s\n", "Compiler: GNU C " __VERSION__
" on " UR_OS " " UR_OSREL ": " UR_OSHOST);
#endif /* __GNUC__ */
}
return (UR_ERR_NONE);
}
static ur_err_t
ur_write(int fd, char *buf, size_t len) {
int n;
while (len > 0) {
if ((n = write(fd, buf, len)) == -1)
return (UR_ERR_WRITE);
len -= n;
buf += n;
}
return (UR_ERR_NONE);
}
static ur_err_t
ur_xfer(int in, int out) {
char buf[512];
int n;
ur_err_t err;
RDBG(("xfer, in = %d, out = %d\n", in, out));
while ((n = read(in, buf, sizeof(buf))) > 0) {
RDBG(("xfer: read %d\n", n));
if ((err = ur_write(out, buf, n)) != UR_ERR_NONE)
return (err);
}
RDBG(("xfer: out of the loop, n = %d\n", n));
if (n == -1) {
if (errno == EAGAIN)
return (UR_ERR_NONE);
return (UR_ERR_READ);
}
if (n == 0)
return (UR_ERR_NOREAD);
return (UR_ERR_NONE);
}
static ur_err_t
ur_swallow(int fd, char *str, size_t len) {
char *buf;
int n;
if ((buf = malloc(len)) == NULL)
return (UR_ERR_NOMEM);
while (len > 0) {
if ((n = read(fd, buf, len)) == -1) {
if (errno == EAGAIN)
continue;
free(buf);
return (UR_ERR_READ);
}
if (n == 0)
continue;
if (memcmp(str, buf, n)) {
free(buf);
return (UR_ERR_DIFF);
}
str += n;
len -= n;
}
free(buf);
return (UR_ERR_NONE);
}
/*
* Function:
* ur_selloop - do the actual connection proxying.
* Inputs:
* src - the source file descriptor array
* dst - the destination file desc array
* Returns:
* ur_err_t
* UR_ERR_SELECT - select() error
* Error codes from ur_swallow(), ur_write(), ur_xfer().
* Modifies:
* nothing by itself; does a select() on the file descriptors and
* invokes ur_swallow(), ur_write(), or ur_xfer() as needed.
*/
static ur_err_t
ur_selloop(int src[2], int dst[2]) {
fd_set fdr;
int maxfd, left, n;
ur_err_t err;
RDBG(("selloop, src=(%d, %d), dst = (%d, %d)\n",
src[0], src[1], dst[0], dst[1]));
if ((msgsrc != NULL) &&
((err = ur_write(src[1], msgsrc, strlen(msgsrc))) != UR_ERR_NONE))
return (err);
if ((msgdst != NULL) &&
((err = ur_write(dst[1], msgdst, strlen(msgdst))) != UR_ERR_NONE))
return (err);
if ((ignsrc != NULL) &&
((err = ur_swallow(src[0], ignsrc, strlen(ignsrc))) != UR_ERR_NONE))
return (err);
if ((igndst != NULL) &&
((err = ur_swallow(dst[0], igndst, strlen(igndst))) != UR_ERR_NONE))
return (err);
left = 2;
maxfd = src[0];
if (src[1] > maxfd)
maxfd = src[1];
if (dst[0] > maxfd)
maxfd = dst[0];
if (dst[1] > maxfd)
maxfd = dst[1];
while (left > 0) {
RDBG(("selloop: loop, left = %d\n", left));
FD_ZERO(&fdr);
FD_SET(src[0], &fdr);
FD_SET(dst[0], &fdr);
if ((n = select(maxfd, &fdr, NULL, NULL, NULL)) == -1)
return (UR_ERR_SELECT);
RDBG(("selloop: select() returned %d\n", n));
if (n == 0)
continue;
if (FD_ISSET(src[0], &fdr))
if ((err = ur_xfer(src[0], dst[1])) != UR_ERR_NONE) {
if (err == UR_ERR_NOREAD) {
shutdown(dst[1], SHUT_WR);
left--;
} else {
return (err);
}
}
if (FD_ISSET(dst[0], &fdr))
if ((err = ur_xfer(dst[0], src[1])) != UR_ERR_NONE) {
if (err == UR_ERR_NOREAD) {
shutdown(src[1], SHUT_WR);
left--;
} else {
return (err);
}
}
}
return (UR_ERR_NONE);
}
/*
* Function:
* ur_doit - perform the actual work
* Inputs:
* argc, argv - main() args
* Returns:
* The error code from ur_selloop().
* Modifies:
* nothing by itself; invokes ur_selloop().
*/
static ur_err_t
ur_doit(int argc __unused, char *argv[] __unused) {
int fdin[2] = {0, 1}, fdout[2] = {6, 7};
ur_err_t err;
if ((err = ur_selloop(fdin, fdout)) != UR_ERR_NONE)
return (err);
return (UR_ERR_NONE);
}
/*
* M A I N F U N C T I O N
*/
int
main(int argc, char *argv[]) {
ur_err_t r, cr;
if (r = ur_init(argc, argv), r)
return (ur_prerror("init", r));
argc -= optind;
argv += optind;
if (r = ur_doit(argc, argv), r)
ur_prerror("doit", r);
if (cr = ur_close(), cr)
ur_prerror("close", cr);
return (r? r: cr);
}
syntax highlighted by Code2HTML, v. 0.9.1