#include #include #include #include #include #include /* for gethostbyname */ #include #include #include #include #include #include extern int errno; #define VERSION "2.5.1" #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff /* -1 return */ #endif /* INADDR_NONE */ #define KEEPALIVE_TIMEOUT 900 /* 15 min. */ int tty; int timeout_sec = KEEPALIVE_TIMEOUT; int timeout_flag; int crlf_flag = 0; void timeout(int sig) { timeout_flag++; } int xread(int fd, char* buff, int n) { if(timeout_sec > 0) { timeout_flag = 0; alarm(timeout_sec); timeout_flag = 0; if((n = read(fd, buff, n)) < 0) { if(timeout_flag && errno == EINTR) { write(2, "TIMEOUT\n", 8); exit(0); } else return n; } alarm(0); } else n = read(fd, buff, n); return n; } #ifdef KEEPALIVE_TIMEOUT #define read(fd, buff, n) xread(fd, buff, n) #endif /* KEEPALIVE_TIMEOUT */ #define SWAP(a, b, tmp) tmp=a; a=b; b=tmp void swap_endian(char* x, int size) { char tmp; switch(size) { case 2: SWAP(x[0], x[1], tmp); break; case 4: SWAP(x[0], x[3], tmp); SWAP(x[1], x[2], tmp); break; } } /* Return internet IP address in network byte order */ unsigned int host_ip_address(const char* address) { unsigned int addr; struct hostent *hp; if((addr = inet_addr(address)) != INADDR_NONE) return addr; if((hp = gethostbyname(address)) == NULL) { fprintf(stderr, "Unknown host name: %s\n", address); exit(1); } memcpy(&addr, hp->h_addr, hp->h_length); return addr; } int bind_sock(int sfd, unsigned short port, unsigned int addr) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_port = htons(port); in.sin_family = AF_INET; in.sin_addr.s_addr = addr; #ifdef SO_REUSEADDR { int on=1; setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&on, sizeof(on)); } #endif /* SO_REUSEADDR */ return bind(sfd, (struct sockaddr *)&in, sizeof(in)); } int connect_to_server(const char* hostname, unsigned short tcp_port, unsigned short local_port) { int fd, len; struct sockaddr_in in; unsigned int addr; if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(1); } memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; in.sin_port = htons(tcp_port); addr = host_ip_address(hostname); memcpy(&in.sin_addr, &addr, 4); if(local_port > 0) if(bind_sock(fd, local_port, 0) < 0) { perror("bind"); exit(1); } if(tty) { struct hostent *hp; hp = gethostbyaddr((unsigned char *)&in.sin_addr, 4, AF_INET); if(hp != NULL) fprintf(stderr, "Try to connect to %s (addr=%s, port=%d)\n", hp->h_name, inet_ntoa(in.sin_addr), tcp_port); else fprintf(stderr, "Try to connect to %s (port=%d)\n", inet_ntoa(in.sin_addr), tcp_port); } alarm(2 * 60); if(connect(fd, (struct sockaddr *)&in, sizeof(in)) < 0) { perror("connect"); exit(1); } alarm(0); len = sizeof(in); if(getsockname(fd, (struct sockaddr *)&in, &len) < 0) perror("getsockname"); else if(tty) fprintf(stderr, "Local Port: %d\n", ntohs(in.sin_port)); return fd; } int open_server_socket(unsigned short port) { int sfd; if((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("sokcet"); exit(1); } if(bind_sock(sfd, port, INADDR_ANY) < 0) { perror("bind"); exit(1); } /* Set it up to wait for connections. */ if(listen(sfd, SOMAXCONN) < 0) { perror("listen"); close(sfd); exit(1); } return sfd; } int wait_connection(int fd) { int cnt, n; fd_set rmask; char buff[BUFSIZ]; FD_ZERO(&rmask); FD_SET(fd, &rmask); FD_SET(0, &rmask); select_proc: cnt = select(fd + 1, &rmask, NULL, NULL, NULL); if(cnt < 0) { perror("select"); exit(1); } if(cnt == 0) { sleep(1); goto select_proc; } if(FD_ISSET(0, &rmask)) { n = read(0, buff, BUFSIZ); if(n < 0) { perror("read"); return -1; } if(n == 0) return 0; write(1, buff, n); } if(FD_ISSET(fd, &rmask)) return 1; sleep(1); goto select_proc; } unsigned short service_port(const char* s) { unsigned short port; if('0' <= *s && *s <= '9') port = atoi(s); else { struct servent* sp; sp = getservbyname(s, "tcp"); if(sp == NULL) { fprintf(stderr, "Unknown service `%s'\n", s); exit(1); } port = ntohs(sp->s_port); } return port; } int xwrite(int fd, char *buff, int size) { int ret; if(crlf_flag == 0 || size == 0) { return write(fd, buff, size); } ret = 0; while(size > 0) { char *lfpt, backch; int n; if((lfpt = strchr(buff, '\n')) == NULL) { ret += write(fd, buff, size); return ret; } n = lfpt - buff; backch = lfpt[1]; lfpt[0] = '\r'; lfpt[1] = '\n'; write(fd, buff, n + 2); n++; buff += n; size -= n; buff[0] = backch; } return size; } char* progname; void usage(void) { fprintf(stderr, "Usage: %s [opts] hostname {tcp_port|service} [{tcp_port|service}]\n", progname); fprintf(stderr, " %s [opts] -pasv {tcp_port|service}\n", progname); fprintf(stderr, "Opts:\n"); fprintf(stderr, "-h help\n"); fprintf(stderr, "-timeout time out (default is 900)\n"); fprintf(stderr, " '-timeout -' means no time out\n"); fprintf(stderr, "-V print version.\n"); fprintf(stderr, "-bufsiz buffer size (default is %d)\n", BUFSIZ); fprintf(stderr, "-crlf map to \n"); } int main(int argc, char** argv) { int fd = -1, sfd = -1; int pasv_flag = 0; char* buff = NULL; int buff_size = 0; unsigned short tcp_port = 0; unsigned short local_port = 0; char* hostname = NULL; fd_set rmask; int cnt; int n, i; progname = argv[0]; tty = isatty(1) && isatty(0); /* parse args */ for(i = 1; i < argc; i++) { if(!strcmp(argv[i], "-pasv")) { pasv_flag = 1; } else if(!strcmp(argv[i], "-timeout")) { if(++i == argc) { usage(); return 0; } if(argv[i][0] == '-') timeout_sec = 0; else timeout_sec = atoi(argv[i]); } else if(!strcmp(argv[i], "-bufsiz")) { if(++i == argc) { usage(); return 0; } buff_size = atoi(argv[i]); } else if(!strcmp(argv[i], "-h")) { usage(); return 0; } else if(!strcmp(argv[i], "-crlf")) { crlf_flag = 1; } else if(!strcmp(argv[i], "-V")) { fprintf(stderr, "%s Version %s\n", progname, VERSION); return 0; } else if(argv[i][0] == '-') { usage(); return 1; } else break; } if(pasv_flag) { if(i == argc) { usage(); return 1; } tcp_port = service_port(argv[i]); } else { if(i + 2 != argc && i + 3 != argc) { usage(); return 1; } hostname = argv[i]; tcp_port = service_port(argv[i + 1]); if(i + 3 == argc) local_port = service_port(argv[i + 2]); } if(buff_size <= 0) buff_size = BUFSIZ; if((buff = (char *)malloc(buff_size + 1)) == NULL) { perror("malloc"); return 1; } signal(SIGALRM, timeout); signal(SIGPIPE, SIG_IGN); if(pasv_flag) /* passive open */ sfd = open_server_socket(tcp_port); else /* active open */ fd = connect_to_server(hostname, tcp_port, local_port); do { if(pasv_flag) { struct sockaddr_in client_data; int len; if(wait_connection(sfd) != 1) break; len = sizeof(client_data); if((fd = accept(sfd, (struct sockaddr *)&client_data, &len)) < 0) { perror("accest"); break; } if(tty) { struct hostent *hp; struct in_addr addr; memcpy(&addr, &client_data.sin_addr, 4); hp = gethostbyaddr((unsigned char *)&addr, 4, AF_INET); fprintf(stderr, "Client %s (Port=%u) is ready!\n", (hp && hp->h_name ? hp->h_name : inet_ntoa(addr)), client_data.sin_port); } } while(1) { FD_ZERO(&rmask); FD_SET(fd, &rmask); FD_SET(0, &rmask); cnt = select(fd + 1, &rmask, NULL, NULL, NULL); if(cnt < 0) { perror("select"); return 1; } if(FD_ISSET(0, &rmask)) { n = read(0, buff, buff_size); if(n < 0) { perror("read"); close(fd); return 1; } if(n == 0) { shutdown(fd, 1); while((n = read(fd, buff, buff_size)) > 0) write(1, buff, n); if(n < 0) perror("read"); shutdown(fd, 2); close(fd); break; } xwrite(fd, buff, n); cnt--; } if(cnt > 0 && FD_ISSET(fd, &rmask)) { n = read(fd, buff, buff_size); if(n < 0) { perror("read"); close(fd); return 1; } if(n == 0) break; write(1, buff, n); } } if(tty) fprintf(stderr, "Connection is closed.\n"); if(pasv_flag) shutdown(fd, 2); close(fd); } while(pasv_flag); if(pasv_flag) shutdown(sfd, 2); return 0; }