/* jumpgate -- jumpgate.c
* Patroklos G. Argyroudis <argp@cs.tcd.ie>
*
* $Id: jumpgate.c,v 1.26 2002/10/17 08:59:47 argp Exp $
*/
#include "jumpgate.h"
int
main(int argc, char *argv[])
{
int lfd, lport, c;
const int val = 1; /* flag for REUSEADDR */
pid_t chldpid;
socklen_t clen;
in_addr_t bind_addr;
struct hostent *lhost;
struct hostent *h_ent;
struct servent *s_ent;
struct jump rec;
struct sockaddr_in laddr, caddr;
int syslog_flag = 0; /* default is not to use syslog */
int interactive = 0; /* default is non-interactive mode */
int backlog = BACKLOG; /* maximum backlog queue */
rec.latency_flag = rec.log_flag = rec.rport = lport = 0;
rec.hostname = NULL;
bind_addr = htonl(INADDR_ANY);
opterr = 0;
while((c = getopt(argc, argv, "hsvil:r:a:b:f:c:L:")) != -1)
{
switch(c)
{
case 'h':
help(argv[0]);
exit(EXIT_SUCCESS);
case 's':
syslog_flag = 1;
break;
case 'v':
fprintf(stderr, "jumpgate v%s by Patroklos G. Argyroudis <argp@cs.tcd.ie>\n", VERSION);
exit(EXIT_SUCCESS);
case 'i':
interactive = 1;
break;
case 'l':
if((s_ent = getservbyname(optarg, "tcp")))
{
lport = ntohs(s_ent->s_port);
}
else
{
char *end_ptr;
lport = strtol(optarg, &end_ptr, 10);
if(!*optarg || *end_ptr || lport < 1 || lport > 65535)
{
error_exit(0, "error: unknown or invalid local port");
}
}
if(lport <= 1024 && geteuid() != 0)
{
error_exit(0, "error: you're trying to bind a well-known port without having root privileges");
}
break;
case 'r':
if((s_ent = getservbyname(optarg, "tcp")))
{
rec.rport = ntohs(s_ent->s_port);
}
else
{
char *end_ptr;
rec.rport = strtol(optarg, &end_ptr, 10);
if(!*optarg || *end_ptr || rec.rport < 1 || rec.rport > 65535)
{
error_exit(0, "error: unknown or invalid remote port");
}
}
break;
case 'a':
rec.hostname = strdup(optarg);
if((rec.hostaddr = inet_addr(optarg)) == INADDR_NONE)
{
if((h_ent = gethostbyname(optarg)) != NULL)
{
memcpy(&rec.hostaddr, h_ent->h_addr, sizeof(in_addr_t));
}
else
{
error_exit(0, "error: error resolving remote hostname");
}
}
break;
case 'b':
if((bind_addr = inet_addr(optarg)) == INADDR_NONE)
{
if((h_ent = gethostbyname(optarg)) != NULL)
{
memcpy(&bind_addr, h_ent->h_addr, sizeof(in_addr_t));
}
else
{
error_exit(0, "error: error resolving hostname for local binding");
}
}
break;
case 'f':
rec.log_flag = 1;
rec.logfile = optarg;
break;
case 'c':
backlog = atoi(optarg);
break;
case 'L':
rec.latency = (useconds_t)atoi(optarg);
if(rec.latency < 0 || rec.latency > 999999)
{
error_exit(0, "error: invalid latency");
}
rec.latency_flag = 1;
break;
default:
usage(argv[0]);
exit(EXIT_SUCCESS);
}
}
/* check the users input */
if(lport == 0 && rec.rport == 0 && rec.hostname == NULL && interactive == 0)
{
usage(argv[0]);
exit(EXIT_SUCCESS);
}
if(lport == 0)
{
error_exit(0, "error: you specified no local port");
}
if(rec.rport == 0 && interactive == 0)
{
error_exit(0, "error: you specified no remote port");
}
if(rec.hostname == NULL && interactive == 0)
{
error_exit(0, "error: you specified no remote host");
}
if(daemonize(".") == -1)
{
error_exit(1, "fork error");
}
openlog("jumpgate", LOG_PID, LOG_USER);
if((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
error_exit(1, "socket error");
}
bzero(&laddr, sizeof(laddr));
laddr.sin_family = AF_INET;
laddr.sin_addr.s_addr = bind_addr;
laddr.sin_port = htons(lport);
/* if you use linux beware of the SO_REUSEADDR vulnerability,
* although I think new linux versions fix this problem
*/
if(setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
{
error_exit(1, "setsockopt error");
}
if(bind(lfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0)
{
error_exit(1, "bind error");
}
if(listen(lfd, backlog) < 0)
{
error_exit(1, "listen error");
}
#ifdef SOLARIS
if(sigset(SIGCHLD, chld_reap) == SIG_ERR)
#else
if(signal(SIGCHLD, chld_reap) == SIG_ERR)
#endif /* SOLARIS */
{
error_exit(1, "signal error");
}
for(; ;)
{
clen = sizeof(caddr);
if((rec.cfd = accept(lfd, (struct sockaddr *)&caddr, &clen)) < 0)
{
if(errno == EINTR || errno == ECONNABORTED)
{
continue;
}
else
{
error_log(1, 1, LOG_ERR, "accept error");
}
}
if((chldpid = fork()) == -1)
{
error_log(1, 1, LOG_ERR, "fork error");
}
if(chldpid == 0)
{
if((close(lfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(interactive == 1)
{
query(&rec);
}
tcp_jumpgate(&rec);
if(syslog_flag == 1)
{
syslog(LOG_NOTICE, "%s jumped to %s:%d\n", inet_ntoa(caddr.sin_addr), rec.hostname, rec.rport);
}
exit(EXIT_SUCCESS);
}
if((close(rec.cfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
}
return(EXIT_SUCCESS);
}
void
usage(char *name)
{
fprintf(stderr, "usage: %s [-hsvi] [-b <local host or IP address>] [-l <local port #>]\n", name);
fprintf(stderr, " [-r <remote port #>] [-a <remote host or IP address>] [-f <filename>]\n");
fprintf(stderr, " [-c <number of connections>] [-L <microseconds>]\n");
}
void
help(char *name)
{
fprintf(stderr, "\njumpgate v%s by Patroklos G. Argyroudis <argp@cs.tcd.ie>\n\n", VERSION);
fprintf(stderr, "usage: %s [-hsvi] [-b <local host or IP address>] [-l <local port #>]\n", name);
fprintf(stderr, " [-r <remote port #>] [-a <remote host or IP address>] [-f <filename>]\n");
fprintf(stderr, " [-c <number of connections>] [-L <microseconds>]\n\n");
fprintf(stderr, "-h this help message\n");
fprintf(stderr, "-s log jumpgate use via syslog(3)\n");
fprintf(stderr, "-v display version number and exit\n");
fprintf(stderr, "-i interactively ask the user where to forward\n");
fprintf(stderr, "-b specify the local IP to bind to\n");
fprintf(stderr, "-l specify the local listening port\n");
fprintf(stderr, "-r specify the remote port to forward the connection\n");
fprintf(stderr, "-a specify the host to forward the connection\n");
fprintf(stderr, "-f specify the filename to log the connection\n");
fprintf(stderr, "-c specify the maximum number of the connections backlog\n");
fprintf(stderr, "-L specify the latency in microseconds\n\n");
}
/* taken from "UNIX Network Programming" by W. Richard Stevens */
void
error_exit(int errno_flag, char *msg)
{
int errno_save;
char buffer[MAXLINE];
errno_save = errno;
snprintf(buffer, sizeof(buffer), "%s", msg);
if(errno_flag)
{
snprintf(buffer+strlen(buffer), sizeof(buffer)-strlen(buffer), ": %s", strerror(errno_save));
}
strncat(buffer, "\n", 2);
fflush(stdout);
fputs(buffer, stderr);
fflush(NULL);
exit(EXIT_FAILURE);
}
void
chld_reap(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
/* do nothing */;
}
return;
}
int
daemonize(const char *path)
{
pid_t pid;
if((signal(SIGTTOU, SIG_IGN)) == SIG_ERR)
{
error_exit(1, "signal error");
}
if((signal(SIGTTIN, SIG_IGN)) == SIG_ERR)
{
error_exit(1, "signal error");
}
if((signal(SIGTSTP, SIG_IGN)) == SIG_ERR)
{
error_exit(1, "signal error");
}
if((signal(SIGHUP, SIG_IGN)) == SIG_ERR)
{
error_exit(1, "signal error");
}
if((pid = fork()) < 0)
{
return(-1);
}
else if(pid != 0)
{
exit(EXIT_SUCCESS);
}
if(setsid() == -1)
{
error_exit(1, "setsid error");
}
if(chdir(path) == -1)
{
error_exit(1, "chdir error");
}
umask(0);
return(0);
}
void
query(struct jump *jmp)
{
u_char p[MAXLINE];
u_char hname[MAXLINE];
u_char msg[MAXLINE];
u_char reply[MAXLINE];
struct hostent *h_ent;
struct servent *s_ent;
snprintf(msg, sizeof(msg), "<hostname or IP address> <port> (e.g.: bsd.gr 80): ");
if((write(jmp->cfd, msg, strlen(msg))) == -1)
{
error_log(1, 1, LOG_ERR, "write error");
}
if((read(jmp->cfd, reply, (sizeof(reply) - 1))) == -1)
{
error_log(1, 1, LOG_ERR, "read error");
}
sscanf(reply, "%s %s", hname, p);
if((s_ent = getservbyname(p, "tcp")))
{
jmp->rport = ntohs(s_ent->s_port);
}
else
{
char *end_ptr;
jmp->rport = strtol(p, &end_ptr, 10);
if(!*optarg || *end_ptr || jmp->rport < 1 || jmp->rport > 65535)
{
error_exit(0, "error: unknown or invalid port");
}
}
jmp->hostname = strdup(hname);
if((jmp->hostaddr = inet_addr(hname)) == INADDR_NONE)
{
if((h_ent = gethostbyname(hname)) != NULL)
{
memcpy(&jmp->hostaddr, h_ent->h_addr, sizeof(in_addr_t));
}
else
{
snprintf(msg, sizeof(msg), "error resolving %s\n", hname);
if((write(jmp->cfd, msg, strlen(msg))) == -1)
{
error_log(1, 1, LOG_ERR, "write error");
}
exit(EXIT_FAILURE);
}
}
return;
}
void
tcp_jumpgate(struct jump *jmp)
{
FILE *fp = NULL;
int n, rfd;
char buf[BUFSIZE];
fd_set fdsr, fdse;
struct sockaddr_in raddr;
if(jmp->log_flag == 1)
{
if((fp = fopen(jmp->logfile, "a+")) == NULL)
{
error_log(1, 1, LOG_ERR, "fopen error");
}
}
if((rfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
error_log(1, 1, LOG_ERR, "socket error");
}
raddr.sin_family = AF_INET;
raddr.sin_port = htons(jmp->rport);
memcpy(&raddr.sin_addr, &jmp->hostaddr, sizeof(in_addr_t));
if((connect(rfd, (struct sockaddr *)&raddr, sizeof(raddr))) < 0)
{
error_log(1, 1, LOG_ERR, "connect error");
}
if(jmp->log_flag == 1)
{
fprintf(fp, "--[ host: %s port: %d ]--\n", jmp->hostname, jmp->rport);
}
for(; ;)
{
FD_ZERO(&fdsr);
FD_ZERO(&fdse);
FD_SET(jmp->cfd, &fdsr);
FD_SET(jmp->cfd, &fdse);
FD_SET(rfd, &fdsr);
FD_SET(rfd, &fdse);
if(select(MYMAXFDS, &fdsr, NULL, &fdse, NULL) == -1)
{
if((shutdown(rfd, 2)) == -1)
{
error_log(1, 1, LOG_ERR, "shutdown error");
}
if((close(rfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(jmp->log_flag == 1)
{
fixfile(jmp->logfile, fp);
}
return;
}
if(FD_ISSET(jmp->cfd, &fdsr) || FD_ISSET(jmp->cfd, &fdse))
{
if((n = read(jmp->cfd, buf, BUFSIZE)) <= 0)
{
if((shutdown(rfd, 2)) == -1)
{
error_log(1, 1, LOG_ERR, "shutdown error");
}
if((close(rfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(jmp->log_flag == 1)
{
fixfile(jmp->logfile, fp);
}
return;
}
else if(jmp->log_flag == 1)
{
fprintf(fp, "%s\n", buf);
}
if((write(rfd, buf, n)) <= 0)
{
if((shutdown(rfd, 2)) == -1)
{
error_log(1, 1, LOG_ERR, "shutdown error");
}
if((close(rfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(jmp->log_flag == 1)
{
fixfile(jmp->logfile, fp);
}
return;
}
else if(jmp->latency_flag == 1)
{
#ifdef LINUX
usleep(jmp->latency);
#else /* {Free,Net,Open}BSD and Solaris */
if((usleep(jmp->latency)) == -1)
{
error_log(1, 1, LOG_ERR, "usleep error");
}
#endif /* LINUX */
}
}
else if(FD_ISSET(rfd, &fdsr) || FD_ISSET(rfd, &fdse))
{
if((n = read(rfd, buf, BUFSIZE)) < 0)
{
if((shutdown(rfd, 2)) == -1)
{
error_log(1, 1, LOG_ERR, "shutdown error");
}
if((close(rfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(jmp->log_flag == 1)
{
fixfile(jmp->logfile, fp);
}
return;
}
if((write(jmp->cfd, buf, n)) <= 0)
{
if((shutdown(rfd, 2)) == -1)
{
error_log(1, 1, LOG_ERR, "shutdown error");
}
if((close(rfd)) == -1)
{
error_log(1, 1, LOG_ERR, "close error");
}
if(jmp->log_flag == 1)
{
fixfile(jmp->logfile, fp);
}
return;
}
else if(jmp->latency_flag == 1)
{
#ifdef LINUX
usleep(jmp->latency);
#else /* {Free,Net,Open}BSD and Solaris */
if((usleep(jmp->latency)) == -1)
{
error_log(1, 1, LOG_ERR, "usleep error");
}
#endif /* LINUX */
}
}
}
return;
}
void
fixfile(char *file, FILE *pf)
{
if((chmod(file, (S_IRUSR | S_IWUSR))) == -1)
{
error_log(1, 1, LOG_ERR, "chmod error");
}
if((fclose(pf)) == EOF)
{
error_log(1, 1, LOG_ERR, "fclose error");
}
}
/* taken from "UNIX Network Programming" by W. Richard Stevens */
void
error_log(int errno_flag, int exit_flag, int priority, char *msg)
{
int errno_save;
char buffer[MAXLINE];
errno_save = errno;
snprintf(buffer, sizeof(buffer), "%s", msg);
if(errno_flag)
{
snprintf(buffer+strlen(buffer), sizeof(buffer)-strlen(buffer), ": %s", strerror(errno_save));
}
strncat(buffer, "\n", 2);
syslog(priority, "%s", buffer);
if(exit_flag)
{
exit(EXIT_FAILURE);
}
else
{
return;
}
}
/* EOF */
syntax highlighted by Code2HTML, v. 0.9.1