Created
December 27, 2014 04:51
-
-
Save benwills/1a2d9bb07444e5442fdc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * $Id: simple-tcp-proxy.c,v 1.6 2002/11/27 00:40:31 wessels Exp $ | |
| */ | |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <stdlib.h> | |
| #include <errno.h> | |
| #include <netdb.h> | |
| #include <string.h> | |
| #include <signal.h> | |
| #include <assert.h> | |
| #include <syslog.h> | |
| #include <sys/types.h> | |
| #include <sys/select.h> | |
| #include <sys/file.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/param.h> | |
| #include <sys/socket.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <sys/wait.h> | |
| #include <netinet/in.h> | |
| #include <arpa/ftp.h> | |
| #include <arpa/inet.h> | |
| #include <arpa/telnet.h> | |
| #define BUF_SIZE 4096 | |
| extern int sys_nerr, errno; | |
| char client_hostname[64]; | |
| void | |
| cleanup(int sig) | |
| { | |
| syslog(LOG_INFO, "Cleaning up..."); | |
| exit(0); | |
| } | |
| void | |
| sigreap(int sig) | |
| { | |
| int status; | |
| pid_t p; | |
| while ((p = waitpid(-1, &status, WNOHANG)) > 0) { | |
| syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status); | |
| } | |
| /* doh! */ | |
| signal(SIGCHLD, sigreap); | |
| } | |
| void | |
| set_nonblock(int fd) | |
| { | |
| int fl; | |
| int x; | |
| x = fcntl(fd, F_GETFL, &fl); | |
| if (x < 0) { | |
| syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno)); | |
| exit(1); | |
| } | |
| fl |= O_NONBLOCK; | |
| x = fcntl(fd, F_SETFL, &fl); | |
| if (x < 0) { | |
| syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno)); | |
| exit(1); | |
| } | |
| } | |
| int | |
| create_server_sock(char *addr, int port) | |
| { | |
| int addrlen, s, on = 1, x; | |
| static struct sockaddr_in client_addr; | |
| s = socket(AF_INET, SOCK_STREAM, 0); | |
| if (s < 0) | |
| perror("socket"), exit(1); | |
| addrlen = sizeof(client_addr); | |
| memset(&client_addr, '\0', addrlen); | |
| client_addr.sin_family = AF_INET; | |
| client_addr.sin_addr.s_addr = inet_addr(addr); | |
| client_addr.sin_port = htons(port); | |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4); | |
| x = bind(s, (struct sockaddr *) &client_addr, addrlen); | |
| if (x < 0) | |
| perror("bind"), exit(1); | |
| x = listen(s, 5); | |
| if (x < 0) | |
| perror("listen"), exit(1); | |
| return s; | |
| } | |
| int | |
| open_remote_host(char *host, int port) | |
| { | |
| struct sockaddr_in rem_addr; | |
| int len, s, x; | |
| struct hostent *H; | |
| int on = 1; | |
| H = gethostbyname(host); | |
| if (!H) | |
| return (-2); | |
| len = sizeof(rem_addr); | |
| s = socket(AF_INET, SOCK_STREAM, 0); | |
| if (s < 0) | |
| return s; | |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4); | |
| len = sizeof(rem_addr); | |
| memset(&rem_addr, '\0', len); | |
| rem_addr.sin_family = AF_INET; | |
| memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length); | |
| rem_addr.sin_port = htons(port); | |
| x = connect(s, (struct sockaddr *) &rem_addr, len); | |
| if (x < 0) { | |
| close(s); | |
| return x; | |
| } | |
| set_nonblock(s); | |
| return s; | |
| } | |
| int | |
| get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn) | |
| { | |
| struct hostent *hostinfo; | |
| hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET); | |
| if (!hostinfo) { | |
| sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr)); | |
| return 0; | |
| } | |
| if (hostinfo && fqdn) | |
| sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr)); | |
| return 0; | |
| } | |
| int | |
| wait_for_connection(int s) | |
| { | |
| static int newsock, len; | |
| static struct sockaddr_in peer; | |
| len = sizeof(struct sockaddr); | |
| newsock = accept(s, (struct sockaddr *) &peer, &len); | |
| /* dump_sockaddr (peer, len); */ | |
| if (newsock < 0) { | |
| if (errno != EINTR) | |
| perror("accept"); | |
| } | |
| get_hinfo_from_sockaddr(peer, len, client_hostname); | |
| set_nonblock(newsock); | |
| return (newsock); | |
| } | |
| int | |
| mywrite(int fd, char *buf, int *len) | |
| { | |
| int x = write(fd, buf, *len); | |
| if (x < 0) | |
| return x; | |
| if (x == 0) | |
| return x; | |
| if (x != *len) | |
| memmove(buf, buf+x, (*len)-x); | |
| *len -= x; | |
| return x; | |
| } | |
| void | |
| service_client(int cfd, int sfd) | |
| { | |
| int maxfd; | |
| char *sbuf; | |
| char *cbuf; | |
| int x, n; | |
| int cbo = 0; | |
| int sbo = 0; | |
| fd_set R; | |
| sbuf = malloc(BUF_SIZE); | |
| cbuf = malloc(BUF_SIZE); | |
| maxfd = cfd > sfd ? cfd : sfd; | |
| maxfd++; | |
| while (1) { | |
| struct timeval to; | |
| if (cbo) { | |
| if (mywrite(sfd, cbuf, &cbo) < 0 && errno != EWOULDBLOCK) { | |
| syslog(LOG_ERR, "write %d: %s", sfd, strerror(errno)); | |
| exit(1); | |
| } | |
| } | |
| if (sbo) { | |
| if (mywrite(cfd, sbuf, &sbo) < 0 && errno != EWOULDBLOCK) { | |
| syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno)); | |
| exit(1); | |
| } | |
| } | |
| FD_ZERO(&R); | |
| if (cbo < BUF_SIZE) | |
| FD_SET(cfd, &R); | |
| if (sbo < BUF_SIZE) | |
| FD_SET(sfd, &R); | |
| to.tv_sec = 0; | |
| to.tv_usec = 1000; | |
| x = select(maxfd+1, &R, 0, 0, &to); | |
| if (x > 0) { | |
| if (FD_ISSET(cfd, &R)) { | |
| n = read(cfd, cbuf+cbo, BUF_SIZE-cbo); | |
| syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd); | |
| if (n > 0) { | |
| cbo += n; | |
| } else { | |
| close(cfd); | |
| close(sfd); | |
| _exit(0); | |
| } | |
| } | |
| if (FD_ISSET(sfd, &R)) { | |
| n = read(sfd, sbuf+sbo, BUF_SIZE-sbo); | |
| syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd); | |
| if (n > 0) { | |
| sbo += n; | |
| } else { | |
| close(sfd); | |
| close(cfd); | |
| _exit(0); | |
| } | |
| } | |
| } else if (x < 0 && errno != EINTR) { | |
| close(sfd); | |
| close(cfd); | |
| _exit(0); | |
| } | |
| } | |
| } | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| char *localaddr = strdup(argv[1]); | |
| int localport = atoi(argv[2]); | |
| char *remoteaddr = strdup(argv[3]); | |
| int remoteport = atoi(argv[4]); | |
| int client, server; | |
| int master_sock; | |
| if (5 != argc) { | |
| fprintf(stderr, "usage: %s laddr lport rhost rport\n", argv[0]); | |
| exit(1); | |
| } | |
| assert(localaddr); | |
| assert(localport > 0); | |
| assert(remoteaddr); | |
| assert(remoteport > 0); | |
| openlog(argv[0], LOG_PID, LOG_LOCAL4); | |
| signal(SIGINT, cleanup); | |
| signal(SIGCHLD, sigreap); | |
| master_sock = create_server_sock(localaddr, localport); | |
| for (;;) { | |
| if ((client = wait_for_connection(master_sock)) < 0) | |
| continue; | |
| if ((server = open_remote_host(remoteaddr, remoteport)) < 0) | |
| continue; | |
| if (!fork()) { | |
| syslog(LOG_NOTICE, "connection from %s fd=%d\n", client_hostname, client); | |
| syslog(LOG_INFO, "connected to %s:%d fd=%d\n", remoteaddr, remoteport, server); | |
| service_client(client, server); | |
| } | |
| close(client); | |
| close(server); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment