#include #include #include #include #include #include #include #include #include #include #include #define LISTEN_ADDR "0.0.0.0" #define LISTEN_PORT 4000 #define CONNECT_ADDR "10.1.1.1" #define CONNECT_PORT 4001 #define PIPESIZE 65536 #ifndef __NR_splice /* x86_32 only ! */ #define __NR_splice 313 #define SPLICE_F_MOVE (0x01) /* move pages instead of copying */ #define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */ #define SPLICE_F_MORE (0x04) /* expect more data */ #define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */ _syscall6(int, splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_out, size_t, len, unsigned long, flags) #endif #define MAX(a, b) (((a) > (b)) ? (a) : (b)) static char buffer[32768]; void die(const char *msg) { if (msg) fprintf(stderr, "%s\n", msg); exit(1); } int main(int argc, char **argv) { int one = 1; int zero = 0; int pipes[2]; fd_set rfd, wfd; struct sockaddr_in listen_addr; struct sockaddr_in connect_addr; int listen_fd; int accept_fd; int connect_fd; long len0, len1; int bytes, pipefull; /* prepare ourselves to accept an incoming connection */ listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = inet_addr(LISTEN_ADDR); listen_addr.sin_port = htons(LISTEN_PORT); listen_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_fd < 0) die("listen_fd"); setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0) die("bind"); if (listen(listen_fd, 10) < 0) die("listen"); accept_fd = accept(listen_fd, NULL, 0); if (accept_fd < 0) die("accept"); /* now connect to the remote host */ connect_addr.sin_family = AF_INET; connect_addr.sin_addr.s_addr = inet_addr(CONNECT_ADDR); connect_addr.sin_port = htons(CONNECT_PORT); connect_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (connect_fd < 0) die("connect_fd"); if (connect(connect_fd, (struct sockaddr *)&connect_addr, sizeof(connect_addr)) < 0) die("connect"); /* OK we're connected */ fcntl(accept_fd, F_SETFL, O_NONBLOCK); fcntl(connect_fd, F_SETFL, O_NONBLOCK); pipe(pipes); bytes = 0; pipefull = 0; while (1) { FD_ZERO(&rfd); FD_ZERO(&wfd); if (/*!bytes*/!pipefull) FD_SET(connect_fd, &rfd); if (bytes) FD_SET(accept_fd, &wfd); select(MAX(accept_fd, connect_fd) + 1, &rfd, &wfd, NULL, NULL); if (FD_ISSET(connect_fd, &rfd)) { len0 = splice(connect_fd, NULL, pipes[1], NULL, PIPESIZE, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); if (len0 == 0) { // connection closed if (bytes) pipefull = 1; else break; } if (len0 == -1) { if (errno == EAGAIN) { // we can get EAGAIN on end of read, pipe full or close. if (bytes) { pipefull = 1; } else { // the pipe is empty and we cannot write, the connection closed. break; } } else break; } else { bytes += len0; } } if (FD_ISSET(accept_fd, &wfd)) { len1 = splice(pipes[0], NULL, accept_fd, NULL, bytes, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); if (len1 == 0) break; if (len1 == -1) { if (errno != EAGAIN) break; } else { bytes -= len1; pipefull = 0; } } } close(connect_fd); close(accept_fd); return 0; }