/* jumpgate -- jumpgate.c * Patroklos G. Argyroudis * * $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 \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 ] [-l ]\n", name); fprintf(stderr, " [-r ] [-a ] [-f ]\n"); fprintf(stderr, " [-c ] [-L ]\n"); } void help(char *name) { fprintf(stderr, "\njumpgate v%s by Patroklos G. Argyroudis \n\n", VERSION); fprintf(stderr, "usage: %s [-hsvi] [-b ] [-l ]\n", name); fprintf(stderr, " [-r ] [-a ] [-f ]\n"); fprintf(stderr, " [-c ] [-L ]\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), " (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 */