/* ============================================================================ * Copyright (C) 1998 Angus Mackay. All rights reserved; * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ============================================================================ */ /* * $Id: tcpcat.c,v 1.24 2000/01/23 01:19:22 amackay Exp $ * * tcpcat is a simple program that is like `cat' but it works over tcp streams * to allow you to cat from one host to another. * * the host common way to use this program whould be something like this: * on host a: $ tcpcat -l 63255 | gzip -dc | tar xvf - * on host b: $ tcpcat -h hosta:63255 tcpcat-X.X.X.tar.gz * * this program has been tested under Linux 2.0 and Solaris 2.6. * */ #ifdef HAVE_CONFIG_H # include #endif #define HAVE_TERMIOS_H 1 #define HAVE_TCGETATTR 1 #define HAVE_TCSETATTR 1 #define HAVE_ON_EXIT 1 #ifdef HAVE_GETOPT_H # include #endif #include #include #include #include #if HAVE_FCNTL_H # include #endif #include #if HAVE_ARPA_INET_H # include #endif #include #include #if HAVE_SYS_UN_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TERMIOS_H # if !defined(HAVE_TCGETATTR) && !defined(HAVE_TCSETATTR) # undef HAVE_TERMIOS_H # endif #endif #ifdef HAVE_TERMIOS_H # include #else # ifdef HAVE_TERMIO_H # include # else # endif #endif #include "zlib.h" #ifndef HAVE_HERROR # define herror(x) fprintf(stderr, "%s: error\n", x) #endif #ifdef DEBUG #define dprintf(x) if( options & OPT_DEBUG ) \ { \ fprintf(stderr, "%s,%d: ", __FILE__, __LINE__); \ fprintf x; \ } #else # define dprintf(x) #endif #define EXIT_NO_HOST 2 #define MIN_BUFFER_SIZE 1 /**************************************************/ static char *program_name; static char *host = NULL; static char *listen_port = NULL; static char *connect_port = NULL; static char *connect_bind_port = NULL; static struct timeval *timeout = NULL; static char *echo_file = NULL; static volatile int server_sockfd; static volatile int client_sockfd; static volatile int client_sockfd2; static volatile int echo_fd; static FILE *echo_fp; static int buffersize = 64*1024; static int socketbuffersize = 0; static uLong g_cksum = 0; static int (*do_connect)(volatile int *sock, char *host, char *port); static int (*do_listen)(volatile int *serv_sock, char *host, char *port); static int (*do_accept)(volatile int *sock, volatile int serv_sock); static int options; static long bytes_copied; #define OPT_DEBUG 0x0001 #define OPT_LISTEN 0x0002 #define OPT_QUIET 0x0004 #define OPT_INPUT 0x0008 #define OPT_OUTPUT 0x0010 #define OPT_VERBOSE 0x0020 #define OPT_CONNECT 0x0040 #define OPT_AF_UNIX 0x0080 #define OPT_CONTINUOUS 0x0100 #define OPT_NOBUF 0x0200 #define OPT_CKSUM 0x0400 #define OPT_ECHO 0x80000000 /* use sign bit for faster checking */ #define COPY_IN_OUT_QUIET 0 #define COPY_IN_OUT_VERB 1 /**************************************************/ void print_useage( void ); void print_version( void ); void parse_args( int argc, char **argv ); int do_connect_inet(volatile int *sock, char *host, char *port); int do_connect_unix(volatile int *sock, char *host, char *port); int do_listen_inet(volatile int *serv_sock, char *host, char *port); int do_listen_unix(volatile int *serv_sock, char *host, char *port); int do_accept_inet(volatile int *sock, volatile int serv_sock); int do_accept_unix(volatile int *sock, volatile int serv_sock); void copy_in_out_verb(int input_fd, int output_fd); void copy_in_out_quiet(int input_fd, int output_fd); void copy_in_out4_quiet(int input1_fd, int input2_fd, int output1_fd, int output2_fd); #if defined(__BEOS__) void copy_sin_fout_quiet(int input_fd, int output_fd); void copy_sin_fout_verb(int input_fd, int output_fd); void copy_fin_sout_quiet(int input_fd, int output_fd); void copy_fin_sout_verb(int input_fd, int output_fd); void copy_in_out4_not_available(int a, int b, int c, int d); #endif void set_input_buffer(int fd, int restore); int main( int argc, char **argv ); /**************************************************/ void *xmalloc(size_t size) { void *p; p = malloc(size); if(p == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } return p; } void print_useage( void ) { fprintf(stdout, "useage: "); fprintf(stdout, "%s [options] [file]...\n\n", program_name); fprintf(stdout, " Options are:\n"); fprintf(stdout, " -B, --socket-buffer \tuse n byte socket buffer\n"); fprintf(stdout, " -b, --buffer-size \tuse n byte buffer\n"); fprintf(stdout, " -c, --continue\t\tcontinue to accept connections\n"); #ifdef DEBUG fprintf(stdout, " -D, --debug\t\t\tturn on debuggin\n"); #endif fprintf(stdout, " -e, --echo \t\techo data to file\n"); fprintf(stdout, " -h, --host [:]\thost to connect to\n"); fprintf(stdout, " -i, --input\t\t\twhen listening input from stdin\n"); fprintf(stdout, " -l, --listen \t\tlisten for connections on port\n"); fprintf(stdout, " -n, --nobuf\t\t\tif stdin is a terminal do not use line \n\t\t\t\t" "buffering. generate a SIGQUIT (CTRL-\\) to stop \n\t\t\t\t" "or use a timeout\n"); fprintf(stdout, " -o, --output\t\t\twhen connecting output to stdout\n"); fprintf(stdout, " -p, --port \t\tuse port for connect or listen\n"); fprintf(stdout, " -P, --bind-port \tuse port for binded connects (ipaddr:port works)\n"); fprintf(stdout, " -s, --sum\t\t\tprint Adler32 checksum\n"); fprintf(stdout, " -q, --quiet\t\t\tdon't print extra info\n"); fprintf(stdout, " -t, --timeout \tused only when both -i and -o are specified\n"); fprintf(stdout, " -u, --unix-host \taf_unix file to connect to\n"); fprintf(stdout, " -U, --unix-listen \tlisten for connections on af_unix file\n"); fprintf(stdout, " -v, --verbose\t\t\tprint progress dots for stdin/out, " "usually dots \n\t\t\t\tare only printed for sending files\n"); fprintf(stdout, " --help\t\t\tdisplay this help and exit\n"); fprintf(stdout, " --version\t\t\toutput version information and exit\n"); fprintf(stdout, " --credits\t\t\tprint the credits and exit\n"); fprintf(stdout, "\n"); fprintf(stdout, "using --input and --output basicly swap the roles of the\n"); fprintf(stdout, "client/server version of %s.\n", program_name); fprintf(stdout, "\n"); } void print_version( void ) { fprintf(stdout, "%s: - %s - $Id: tcpcat.c,v 1.24 2000/01/23 01:19:22 amackay Exp $\n", program_name, VERSION); } void print_credits( void ) { fprintf( stdout, "AUTHORS / CONTRIBUTORS\n" " Angus Mackay \n" "\n" ); } #if HAVE_SIGNAL_H RETSIGTYPE sig_handler(int sig) { char message[] = "interupted.\n"; write(2, message, sizeof(message)-1); // clean up the terminal if(options & OPT_NOBUF) { set_input_buffer(fileno(stdin), 1); } close(client_sockfd); close(server_sockfd); close(echo_fd); // clean up the socket file if( (options & OPT_AF_UNIX) && (options & OPT_LISTEN) && listen_port ) { unlink(listen_port); } } #endif /* * longfromstr * * a super atoi that takes things like * 64k == 65536 * 0x10M == 16777216 * 013 == 11 * 43 == 43 * */ long longfromstr(char *str) { int mult; int lastchar; long val; lastchar = strlen(str); lastchar = lastchar == 0 ? 0 : lastchar - 1; switch(str[lastchar]) { case 'k': case 'K': mult = 1024; break; case 'm': case 'M': mult = 1024*1024; break; case 'g': case 'G': mult = 1024*1024*1024; break; default: mult = 1; break; } val = strtol(str, NULL, 0) * mult; return(val); } #ifdef HAVE_GETOPT_LONG # define xgetopt( x1, x2, x3, x4, x5 ) getopt_long( x1, x2, x3, x4, x5 ) #else # define xgetopt( x1, x2, x3, x4, x5 ) getopt( x1, x2, x3 ) #endif void parse_args( int argc, char **argv ) { #ifdef HAVE_GETOPT_LONG struct option long_options[] = { {"socket-buffer", required_argument, 0, 'B'}, {"buffer-size", required_argument, 0, 'b'}, {"continue", no_argument, 0, 'c'}, {"debug", no_argument, 0, 'D'}, {"echo", required_argument, 0, 'e'}, {"host", required_argument, 0, 'h'}, {"input", no_argument, 0, 'i'}, {"listen", required_argument, 0, 'l'}, {"nobuf", no_argument, 0, 'n'}, {"output", no_argument, 0, 'o'}, {"port", required_argument, 0, 'p'}, {"bind-port", required_argument, 0, 'P'}, {"quiet", no_argument, 0, 'q'}, {"sum", no_argument, 0, 's'}, {"timeout", required_argument, 0, 't'}, {"unix-listen", required_argument, 0, 'U'}, {"unix-host", required_argument, 0, 'u'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'H'}, {"version", no_argument, 0, 'V'}, {"credits", no_argument, 0, 'C'}, {0,0,0,0} }; #else # define long_options NULL #endif int opt; char *tmp; while((opt = xgetopt(argc, argv, "B:b:cDe:h:il:noP:p:qst:U:u:vHVC", long_options, NULL)) != -1) { switch (opt) { case 'B': socketbuffersize = longfromstr(optarg); if(socketbuffersize < MIN_BUFFER_SIZE) { fprintf(stderr, "invalid buffer size of %d, must be larger than %d byte%s\n", socketbuffersize, MIN_BUFFER_SIZE, MIN_BUFFER_SIZE == 1 ? "" : "s"); exit(1); } dprintf((stderr, "socketbuffersize %d\n", socketbuffersize)); break; case 'b': buffersize = longfromstr(optarg); if(buffersize < MIN_BUFFER_SIZE) { fprintf(stderr, "invalid buffer size of %d, must be larger than %d byte%s\n", buffersize, MIN_BUFFER_SIZE, MIN_BUFFER_SIZE == 1 ? "" : "s"); exit(1); } dprintf((stderr, "buffersize %d\n", buffersize)); break; case 'c': options |= OPT_CONTINUOUS; dprintf((stderr, "continuous enabled\n")); break; case 'D': options |= OPT_DEBUG; dprintf((stderr, "debugging on\n")); break; case 'e': options |= OPT_ECHO; options |= OPT_INPUT; options |= OPT_OUTPUT; if(echo_file) { free(echo_file); } echo_file = strdup(optarg); dprintf((stderr, "echo_file: %s\n", echo_file)); break; case 'h': options |= OPT_CONNECT; // look for the port and cut it off if found tmp = strchr(optarg, ':'); if (tmp) { *tmp++ = '\0'; if(connect_port) { free(connect_port); } connect_port = strdup(tmp); } if(host) { free(host); } host = strdup(optarg); dprintf((stderr, "host: %s\n", host)); dprintf((stderr, "connect_port: %s\n", connect_port)); break; case 'u': options |= OPT_CONNECT; options |= OPT_AF_UNIX; if(connect_port) { free(connect_port); } connect_port = strdup(optarg); dprintf((stderr, "connect_port: %s\n", connect_port)); break; case 'H': print_useage(); exit(0); break; case 'i': options |= OPT_INPUT; break; case 'l': options |= OPT_LISTEN; if(listen_port) { free(listen_port); } listen_port = strdup(optarg); dprintf((stderr, "listen_port: %s\n", listen_port)); break; case 'n': options |= OPT_NOBUF; break; case 'U': options |= OPT_LISTEN; options |= OPT_AF_UNIX; if(listen_port) { free(listen_port); } listen_port = strdup(optarg); dprintf((stderr, "listen_port: %s\n", listen_port)); break; case 'o': options |= OPT_OUTPUT; break; // only use the -p port if we don't already have ports case 'p': if(connect_port == NULL) { connect_port = strdup(optarg); dprintf((stderr, "connect_port: %s\n", connect_port)); } if(listen_port == NULL) { listen_port = strdup(optarg); dprintf((stderr, "listen_port: %s\n", listen_port)); } break; case 'P': if(connect_bind_port == NULL) { connect_bind_port = strdup(optarg); dprintf((stderr, "connect_bind_port: %s\n", connect_bind_port)); } break; case 'q': options |= OPT_QUIET; dprintf((stderr, "options |= OPT_QUIET\n")); break; case 's': options |= OPT_CKSUM; dprintf((stderr, "options |= OPT_CKSUM\n")); break; case 't': if(timeout) { free(timeout); } timeout = (struct timeval*)malloc((int)sizeof(struct timeval)); timeout->tv_sec = strtol(optarg, NULL, 10); timeout->tv_usec = (atof(optarg) - timeout->tv_sec) * 1000000L; dprintf((stderr, "timeout: %ld.%06ld\n", timeout->tv_sec, timeout->tv_usec)); break; case 'v': options |= OPT_VERBOSE; break; case 'V': print_version(); exit(0); break; case 'C': print_credits(); exit(0); break; default: #ifdef HAVE_GETOPT_LONG fprintf(stderr, "Try `%s --help' for more information\n", argv[0]); #else fprintf(stderr, "Try `%s -H' for more information\n", argv[0]); fprintf(stderr, "warning: this program was compilied without getopt_long\n"); fprintf(stderr, " as such all long options will not work!\n"); #endif exit(1); break; } } // we need a port if( connect_port == NULL && listen_port == NULL ) { #ifdef HAVE_GETOPT_LONG fprintf(stderr, "Try `%s --help' for more information\n", argv[0]); #else fprintf(stderr, "Try `%s -H' for more information\n", argv[0]); fprintf(stderr, "warning: this program was compilied without getopt_long\n"); fprintf(stderr, " as such all long options will not work!\n"); #endif exit(1); } } /* * do_connect * * connect a socket and return the file descriptor * */ int do_connect_common(volatile int *sock, char *host, char *port) { int oplen; int sendsize; int recvsize; #if HAVE_SETSOCKOPT && defined(SO_SNDBUF) && defined(SO_RCVBUF) if(socketbuffersize != 0) { sendsize = socketbuffersize; oplen = sizeof(sendsize); if(setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendsize, oplen) == -1) { perror("setsockopt:SO_SNDBUF"); } recvsize = socketbuffersize; oplen = sizeof(recvsize); if(setsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (void *)&recvsize, oplen) == -1) { perror("setsockopt:SO_RCVBUF"); } } if( options & OPT_VERBOSE ) { oplen = sizeof(sendsize); if(getsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendsize, &oplen) == -1) { perror("setsockopt:SO_SNDBUF"); } oplen = sizeof(recvsize); if(getsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (void *)&recvsize, &oplen) == -1) { perror("setsockopt:SO_RCVBUF"); } fprintf(stderr, "using %d byte socket send buffer\n", sendsize); fprintf(stderr, "using %d byte socket recv buffer\n", recvsize); } #endif return 0; } /* * do_connect * * connect a socket and return the file descriptor * */ int do_connect_inet(volatile int *sock, char *host, char *port) { struct sockaddr_in address; struct hostent *hostinfo; struct servent *servinfo; // set up the socket *sock = socket(AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; // get the host address hostinfo = gethostbyname(host); if(!hostinfo) { herror("gethostbyname"); exit(EXIT_NO_HOST); } address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list; // get the host port servinfo = getservbyname(port, "tcp"); if(servinfo) { address.sin_port = servinfo->s_port; } else { address.sin_port = htons(atoi(port)); } // bind the socket if(connect_bind_port != NULL) { struct sockaddr_in bind_address; struct servent *servinfo; int len; char* host; char* port; char* tmp; bind_address.sin_family = AF_INET; tmp = strchr(connect_bind_port, ':'); if(tmp) { *tmp++ = '\0'; port = tmp; host = connect_bind_port; } else { host = NULL; port = connect_bind_port; } if(host == NULL) { bind_address.sin_addr.s_addr = htonl(INADDR_ANY); } else { if(inet_aton(host, &(bind_address.sin_addr)) == 0) { fprintf(stderr, "can't decipher ipaddr: %s\n", host); return(-1); } } servinfo = getservbyname(port, "tcp"); if(servinfo) { bind_address.sin_port = servinfo->s_port; } else { bind_address.sin_port = htons(atoi(port)); } len = sizeof(bind_address); if( bind(*sock, (struct sockaddr *)&bind_address, len) != 0 ) { perror("bind"); return(-1); } } // connect the socket if(connect(*sock, (struct sockaddr *)&address, sizeof(address)) != 0) { perror("connect"); return(-1); } // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "connected to %s (%s) on port %d.\n", host, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); } if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "connected to %s (%s) on port %d.\n", host, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); } return(do_connect_common(sock, host, port)); } int do_connect_unix(volatile int *sock, char *host, char *port) { #if HAVE_SYS_UN_H struct sockaddr_un address; // set up the socket *sock = socket(AF_UNIX, SOCK_STREAM, 0); address.sun_family = AF_UNIX; strcpy(address.sun_path, port); // bind the socket if(connect_bind_port != NULL) { struct sockaddr_un bind_address; int len; bind_address.sun_family = AF_UNIX; strcpy(bind_address.sun_path, connect_bind_port); // warning: you have to delete this socket file now len = sizeof(bind_address); if( bind(*sock, (struct sockaddr *)&bind_address, len) != 0 ) { perror("bind"); return(-1); } } // connect the socket if(connect(*sock, (struct sockaddr *)&address, sizeof(address)) != 0) { perror("connect"); return(-1); } // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "connected to unix:%s.\n", port); } if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "connected to unix:%s.\n", port); } return(do_connect_common(sock, host, port)); #else fprintf(stderr, "AF_UNIX support not enabled at compile time\n"); return(-1); #endif } /* * do_accept * * do_accept is a one shot deal so it simply sets up a server * socket, accepts a single connection on that socket, copies * all of the input data to stdout, then shuts down the server * socket and the connected socket. * */ int do_accept_common(volatile int *sock, volatile int serv_sock) { int oplen; int sendsize; int recvsize; #if HAVE_SETSOCKOPT && defined(SO_SNDBUF) && defined(SO_RCVBUF) if(socketbuffersize != 0) { sendsize = socketbuffersize; oplen = sizeof(sendsize); if(setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendsize, oplen) == -1) { perror("setsockopt:SO_SNDBUF"); } recvsize = socketbuffersize; oplen = sizeof(recvsize); if(setsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (void *)&recvsize, oplen) == -1) { perror("setsockopt:SO_RCVBUF"); } } if( options & OPT_VERBOSE ) { oplen = sizeof(sendsize); if(getsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendsize, &oplen) == -1) { perror("getsockopt:SO_SNDBUF"); } oplen = sizeof(recvsize); if(getsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (void *)&recvsize, &oplen) == -1) { perror("getsockopt:SO_RCVBUF"); } fprintf(stderr, "using %d byte socket send buffer\n", sendsize); fprintf(stderr, "using %d byte socket recv buffer\n", recvsize); } #endif return 0; } int do_listen_common(volatile int *serv_sock, char *host, char *port) { return 0; } int do_listen_unix(volatile int *serv_sock, char *host, char *port) { #ifdef HAVE_SYS_UN_H int server_len; int client_len; struct sockaddr_un server_address; struct sockaddr_un client_address; // set up the server socket if( (*serv_sock=socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { perror("socket"); return(-1); } server_address.sun_family = AF_UNIX; strcpy(server_address.sun_path, port); // bind it to a port server_len = sizeof(server_address); if( bind(*serv_sock, (struct sockaddr *)&server_address, server_len) != 0 ) { perror("bind"); return(-1); } // listen with a backlog of one, this should be zero but the BeOS // doesn't interpret 0 correctly and most OSes don't enforce a backlog // of 0 anyway if( listen(*serv_sock, 1) != 0 ) { perror("listen"); return(-1); } // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "listening on unix:%s.\n", port); } if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "listening on unix:%s.\n", port); } return(do_listen_common(serv_sock, host, port)); #else fprintf(stderr, "AF_UNIX support not enabled at compile time\n"); return(-1); #endif } /* * do_accept * * do_accept is a one shot deal so it simply sets up a server * socket, accepts a single connection on that socket, copies * all of the input data to stdout, then shuts down the server * socket and the connected socket. * */ int do_accept_unix(volatile int *sock, volatile int serv_sock) { int server_len; int client_len; struct sockaddr_un server_address; struct sockaddr_un client_address; // accept a connection on the server socket client_len = sizeof(client_address); *sock = accept( serv_sock, (struct sockaddr *)&client_address, &client_len ); // check the socket if( *sock == -1 ) { perror("accept"); return(-1); } // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "connection from unix:%s.\n", client_address.sun_path); } if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "connection from unix:%s.\n", client_address.sun_path); } return(do_accept_common(sock, serv_sock)); } int do_listen_inet(volatile int *serv_sock, char *host, char *port) { int server_len; int client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; int res; int x; #if HAVE_SETSOCKOPT # ifdef SO_LINGER struct linger ling; # endif #endif // set up the server socket if( (*serv_sock=socket(AF_INET, SOCK_STREAM, 0)) == -1 ) { perror("socket"); return(-1); } #if HAVE_SETSOCKOPT # ifdef SO_REUSEADDR x = 1; res = setsockopt(*serv_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&x, sizeof(x)); if(res == -1) { perror("setsockopt:SO_REUSEADDR"); } # endif # ifdef SO_LINGER ling.l_onoff = 1; ling.l_linger = 10000; res = setsockopt(*serv_sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); if(res == -1) { perror("setsockopt:SO_LINGER"); } # endif #endif server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(atoi(port)); // bind it to a port server_len = sizeof(server_address); if( bind(*serv_sock, (struct sockaddr *)&server_address, server_len) != 0 ) { perror("bind"); return(-1); } // listen with a backlog of one, this should be zero but the BeOS // doesn't interpret 0 correctly and most OSes don't enforce a backlog // of 0 anyway if( listen(*serv_sock, 1) != 0 ) { perror("listen"); return(-1); } // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "listening on %s port %d.\n", inet_ntoa(server_address.sin_addr), ntohs(server_address.sin_port) ); } return(do_listen_common(serv_sock, host, port)); } int do_accept_inet(volatile int *sock, volatile int serv_sock) { int server_len; int client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; int res; int x; struct linger ling; // accept a connection on the server socket client_len = sizeof(client_address); *sock = accept( serv_sock, (struct sockaddr *)&client_address, &client_len ); // check the socket if( *sock == -1 ) { perror("accept"); return(-1); } // stop listening after we have 1 connection //close(serv_sock); // print out some info if( !(options & OPT_QUIET) ) { fprintf(stderr, "connection from %s on port %d.\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port)); } if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "connection from %s on port %d.\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port)); } return(do_accept_common(sock, serv_sock)); } int read_data(int fd, void* buf, int bufsize) { int bread = read(fd, buf, buffersize); if(options & OPT_CKSUM) { g_cksum = adler32(g_cksum, buf, bread); } return(bread); } int revc_data(int fd, void* buf, int bufsize) { int bread = recv(fd, buf, buffersize, 0); if(options & OPT_CKSUM) { g_cksum = adler32(g_cksum, buf, bread); } return(bread); } /* * copy_in_out_verb * * simply read the input file descriptor and write the * data to the output file descriptor. * * also ouput a progressmeter. * */ void copy_in_out_verb(int input_fd, int output_fd) { char *buf; int bytes_read; buf = xmalloc(buffersize); while( (bytes_read=read_data(input_fd, buf, buffersize)) > 0 ) { write(output_fd, buf, bytes_read); bytes_copied += bytes_read; fputc('.', stderr); } free(buf); } /* * copy_in_out_quiet * * simply read the input file descriptor and write the * data to the output file descriptor. * */ void copy_in_out_quiet(int input_fd, int output_fd) { char *buf; int bytes_read; buf = xmalloc(buffersize); while( (bytes_read=read_data(input_fd, buf, buffersize)) > 0 ) { write(output_fd, buf, bytes_read); bytes_copied += bytes_read; } free(buf); } /* * copy_in_out4_quiet * * read input1_fd => write input2_fd * read input2_fd => write output_fd * * basicly just like telnet. * */ void copy_in_out4_quiet(int input1_fd, int input2_fd, int output1_fd, int output2_fd) { char *buf; int bytes_read = 0; int max_fd; fd_set readfds; fd_set select_readfds; struct timeval *tv = NULL; int ret; buf = xmalloc(buffersize); FD_ZERO(&readfds); FD_SET(input1_fd, &readfds); FD_SET(input2_fd, &readfds); max_fd = (input1_fd > input2_fd ? input1_fd : input2_fd); if(timeout != NULL) { tv = (struct timeval*)malloc(sizeof(struct timeval)); if(tv == NULL) { fprintf(stderr, "internal error, timeout will be ignored\n"); timeout = NULL; } } for(;;) { // refresh our fd set and timeout memcpy(&select_readfds, &readfds, sizeof(readfds)); if(timeout) { memcpy(tv, timeout, sizeof(struct timeval)); } // select on the readfds ret = select(max_fd + 1, &select_readfds, NULL, NULL, tv ); dprintf((stderr, "select: %d\n", ret)); if ( ret == -1) { perror("select"); break; } if( ret == 0 ) { fprintf(stderr, "timeout\n"); break; } /* if we woke up on input1_fd do the data passing */ if(FD_ISSET(input1_fd, &select_readfds)) { if( (bytes_read=read_data(input1_fd, buf, buffersize) ) <= 0) break; if(write(output2_fd, buf, bytes_read) != bytes_read) break; if( options & OPT_ECHO ) { write(echo_fd, "L: ", 3); write(echo_fd, buf, bytes_read); } dprintf((stderr, "transfered %d bytes (%d -> %d)\n", bytes_read, input1_fd, output2_fd)); } else if(FD_ISSET(input2_fd, &select_readfds)) { if( (bytes_read=read_data(input2_fd, buf, buffersize) ) <= 0) break; if(write(output1_fd, buf, bytes_read) != bytes_read) break; if( options & OPT_ECHO ) { write(echo_fd, "H: ", 3); write(echo_fd, buf, bytes_read); } dprintf((stderr, "transfered %d bytes (%d -> %d)\n", bytes_read, input2_fd, output1_fd)); } else { dprintf((stderr, "error: case not handled.")); } bytes_copied += bytes_read; } if(tv) { free(tv); } free(buf); } #if defined(__BEOS__) /* * needed for silly OSes */ void copy_sin_fout_quiet(int input_fd, int output_fd) { char *buf; int bytes_read; buf = xmalloc(buffersize); while( (bytes_read=recv_data(input_fd, buf, buffersize, 0)) > 0 ) { write(output_fd, buf, bytes_read); bytes_copied += bytes_read; } free(buf); } void copy_sin_fout_verb(int input_fd, int output_fd) { char *buf; int bytes_read; buf = xmalloc(buffersize); while( (bytes_read=recv_data(input_fd, buf, buffersize, 0)) > 0 ) { write(output_fd, buf, bytes_read); bytes_copied += bytes_read; fputc('.', stderr); } free(buf); } void copy_fin_sout_quiet(int input_fd, int output_fd) { int bytes_read; char *buf; buf = xmalloc(buffersize); while( (bytes_read=read_data(input_fd, buf, buffersize)) > 0 ) { send(output_fd, buf, bytes_read, 0); bytes_copied += bytes_read; } free(buf); } void copy_fin_sout_verb(int input_fd, int output_fd) { char *buf; int bytes_read; buf = xmalloc(buffersize); while( (bytes_read=read_data(input_fd, buf, buffersize)) > 0 ) { send(output_fd, buf, bytes_read, 0); bytes_copied += bytes_read; fputc('.', stderr); } free(buf); } void copy_in_out4_not_available(int a, int b, int c, int d) { fprintf( stderr, "this functionality is not abailable due to the fact that the OS\n" "that you are using spererates socket descriptors from file descriptorss.\n" "the feature could be implemented by using two threads but that isn't very\n" "pretty.\n" ); } #endif /* * enable or restore terminal line buffering state */ void set_input_buffer(int fd, int restore) { #ifdef HAVE_TERMIOS_H static struct termios orig; struct termios new; #else # ifdef HAVE_TERMIO_H static struct termio orig; struct termio new; # endif #endif static int need_restore = 0; if(!isatty(fd)) { return; } dprintf((stderr, "restore: %d\n", restore)); if(!restore) { #ifdef HAVE_TERMIOS_H if(!need_restore) { if(tcgetattr(fd, &orig) != 0) { perror("tcgetattr"); } } new = orig; dprintf((stderr, "iflag: 0x%08x oflag: 0x%08x cflag: 0x%08x lflag: 0x%08x\n", new.c_iflag, new.c_oflag, new.c_cflag, new.c_lflag)); new.c_lflag &= ~ICANON; dprintf((stderr, "iflag: 0x%08x oflag: 0x%08x cflag: 0x%08x lflag: 0x%08x\n", new.c_iflag, new.c_oflag, new.c_cflag, new.c_lflag)); if(tcsetattr(fd, TCSANOW, &new) != 0) { perror("tcsetattr"); } else { need_restore = 1; } #else # ifdef HAVE_TERMIO_H if(!need_restore) { if(ioctl(fd, TCGETA, &orig) != 0) { perror("ioctl"); } } new = orig; dprintf((stderr, "iflag: 0x%08x oflag: 0x%08x cflag: 0x%08x lflag: 0x%08x\n", new.c_iflag, new.c_oflag, new.c_cflag, new.c_lflag)); new.c_lflag &= ~ICANON; dprintf((stderr, "iflag: 0x%08x oflag: 0x%08x cflag: 0x%08x lflag: 0x%08x\n", new.c_iflag, new.c_oflag, new.c_cflag, new.c_lflag)); if(ioctl(fd, TCSETA, &new) != 0) { perror("ioctl"); } else { need_restore = 1; } # else # endif #endif } else { if(!need_restore) { return; } #ifdef HAVE_TERMIOS_H if(tcsetattr(fd, TCSANOW, &orig) != 0) { perror("tcgetattr"); } else { need_restore = 0; } #else # ifdef HAVE_TERMIO_H if(ioctl(fd, TCSETA, &orig) != 0) { perror("ioctl"); } else { need_restore = 0; } # else # endif #endif } } void print_sum(void) { fprintf(stderr, "checksum: %u\n", g_cksum); } int main( int argc, char **argv ) { int i; int input_fd; struct timeval time1; struct timeval time2; int first_arg; void (*copy_in_out4_fp)(int, int, int, int); void (*copy_sin_fout_fp)(int, int); void (*copy_fin_sout_fp)(int, int); void (*send_file_fp)(int, int); dprintf((stderr, "staring...\n")); program_name = argv[0]; options = 0; bytes_copied = 0; timeout = NULL; #if HAVE_SIGNAL_H // catch user interupts signal(SIGINT, sig_handler); signal(SIGQUIT, sig_handler); #endif parse_args(argc, argv); first_arg = optind; // set defaults if( !host ) { host = strdup("localhost"); } // set socket domain if((options & OPT_AF_UNIX)) { do_connect = do_connect_unix; do_listen = do_listen_unix; do_accept = do_accept_unix; } else { do_connect = do_connect_inet; do_listen = do_listen_inet; do_accept = do_accept_inet; } // corelate options if( (options & OPT_CONNECT) && (options & OPT_LISTEN) ) { options |= OPT_INPUT; options |= OPT_OUTPUT; } // more error checking if( !(options & OPT_CONNECT) && connect_bind_port != NULL) { fprintf(stderr, "you can not use --bind-port if you are not connecting, \njust use --port instead\n"); exit(1); } // if we are listening and not doing input we do output by default if( (options & OPT_LISTEN) && !(options & OPT_INPUT) ) { options |= OPT_OUTPUT; } // if we are connecting and not doing output we do input by default if( !(options & OPT_LISTEN) && !(options & OPT_OUTPUT) ) { options |= OPT_INPUT; } dprintf((stderr, "options: 0x%04X\n", options)); dprintf((stderr, "host: %s\n", host ? host : "(null)")); dprintf((stderr, "connect_port: %s\n", connect_port ? connect_port : "(null)")); dprintf((stderr, "listen_port: %s\n", listen_port ? listen_port : "(null)")); // check for conflicting options if( (first_arg != argc) && (options & OPT_OUTPUT) ) { fprintf(stderr, "Error: when using the output (`-o') option files can"); fprintf(stderr, " not be supplied\n"); exit(1); } /* * set up the function pointers to handle for silly OSes and * the options */ if( (options & OPT_VERBOSE) && !(options & OPT_QUIET) ) { #if __BEOS__ copy_in_out4_fp = copy_in_out4_not_available; copy_fin_sout_fp = copy_fin_sout_verb; copy_sin_fout_fp = copy_sin_fout_verb; send_file_fp = copy_fin_sout_verb; #else copy_in_out4_fp = copy_in_out4_quiet; copy_fin_sout_fp = copy_in_out_verb; copy_sin_fout_fp = copy_in_out_verb; send_file_fp = copy_in_out_verb; #endif } else { #if __BEOS__ copy_in_out4_fp = copy_in_out4_not_available; copy_fin_sout_fp = copy_fin_sout_quiet; copy_sin_fout_fp = copy_sin_fout_quiet; if( options & OPT_QUIET) { send_file_fp = copy_fin_sout_quiet; } else { send_file_fp = copy_fin_sout_verb; } #else copy_in_out4_fp = copy_in_out4_quiet; copy_fin_sout_fp = copy_in_out_quiet; copy_sin_fout_fp = copy_in_out_quiet; if( options & OPT_QUIET) { send_file_fp = copy_in_out_quiet; } else { send_file_fp = copy_in_out_verb; } #endif } if( options & OPT_VERBOSE ) { fprintf(stderr, "using %d byte buffer\n", buffersize); } // open our echo file if( (options & OPT_ECHO) && echo_file != NULL ) { if( (echo_fd=open(echo_file, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1 ) { perror(echo_file); exit(1); } if( (echo_fp=fdopen(echo_fd, "w")) == NULL) { perror(echo_file); exit(1); } // no buffering on the echo file so we can mix calls to write // and fprintf setvbuf(echo_fp, NULL, _IONBF, 0); } if(options & OPT_NOBUF) { set_input_buffer(fileno(stdin), 0); } server_sockfd = -1; do { if( (options & OPT_ECHO) && echo_file != NULL ) { fprintf(echo_fp, "=============\n"); } // open up our sockets if( (options & OPT_LISTEN) && (options & OPT_CONNECT) ) { if( server_sockfd == -1 ) { if( do_listen(&server_sockfd, NULL, listen_port) != 0 ) { fprintf(stderr, "error creating server socket\n"); exit(1); } } if( do_accept(&client_sockfd, server_sockfd) != 0 ) { fprintf(stderr, "error accepting connections\n"); exit(1); } if(!(options & OPT_CONTINUOUS)) { // stop listening after we have 1 connection close(server_sockfd); } if( do_connect(&client_sockfd2, host, connect_port) != 0 ) { fprintf(stderr, "error connecting socket\n"); exit(1); } } else if( options & OPT_LISTEN ) { if( server_sockfd == -1 ) { if( do_listen(&server_sockfd, NULL, listen_port) != 0 ) { fprintf(stderr, "error creating server socket\n"); exit(1); } } if( do_accept(&client_sockfd, server_sockfd) != 0 ) { fprintf(stderr, "error accepting connections\n"); exit(1); } if(!(options & OPT_CONTINUOUS)) { // stop listening after we have 1 connection close(server_sockfd); } } else if( options & OPT_CONNECT ) { if( do_connect(&client_sockfd, host, connect_port) != 0 ) { fprintf(stderr, "error connecting socket\n"); exit(1); } } gettimeofday(&time1, NULL); // if we don't have files supplied on the command line if( first_arg == argc ) { /* * figure out what to do, * if we have -i and -o then act like * telnet, otherwise check for -o or -l if not found default * to read in, write out. */ if( (options & OPT_OUTPUT) && (options & OPT_INPUT) ) { if( (options & OPT_CONNECT) && (options & OPT_LISTEN) ) { copy_in_out4_fp(client_sockfd, client_sockfd2, client_sockfd, client_sockfd2); } else { // read socket, stdin, write stdout copy_in_out4_fp(STDIN_FILENO, client_sockfd, STDOUT_FILENO, client_sockfd); } } else if( (options & OPT_OUTPUT) ) { if(options & OPT_VERBOSE) { fprintf(stderr, "receiving "); } // read socket, write stdout copy_sin_fout_fp(client_sockfd, STDOUT_FILENO);; if(options & OPT_VERBOSE) { fprintf(stderr, " done.\n"); } } else if( (options & OPT_INPUT) ) { if(options & OPT_VERBOSE) { fprintf(stderr, "sending "); } // read stdin, write socket copy_fin_sout_fp(STDIN_FILENO, client_sockfd); if(options & OPT_VERBOSE) { fprintf(stderr, " done.\n"); } } else { dprintf((stderr, "case not handeled\n")); } } else { // proccess the files for(i=first_arg; i