/* Copyright (c) 2008-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR CONTRIBUTORS 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. */ /* tcptool -s [-c ] tcptool -r [-t] -s port Sender listens on port for connections. -l maxclients Maximum number of clients (0 for "no limit yet") -r host:port Receiver connects to sender host and port -t Start transmission, this is the last recipient */ /** @file tcptool.c The tcptool program. */ #include "tcptool.h" $(trace-include) /** Flag: SIGPIPE received. */ static volatile int sigpipe_received = 0; /** Program name. */ static char program_name[] = { "tcptool" }; /** Message texts used by the program. */ static char *messages[] = { /* 0 */ "ERROR", /* 1 */ "Warning", /* 2 */ "Failed to open output file \"", /* 3 */ "\"!", /* 4 */ "Problem while writing to output file!", /* 5 */ "Failed to send initialization byte to sender!", /* 6 */ "Failed to connect to sender!", /* 7 */ "Sender host IP not found!", /* 8 */ "Failed to create TCP/IP socket!", /* 9 */ "Missing host name!", /* 10 */ "Failed to create new process for connection!", /* 11 */ "Failed to create pipe for communication with sender process!", /* 12 */ "Failed to obtain initialization byte from recipient!", /* 13 */ "Client ", /* 14 */ "Wrong peer name size!", /* 15 */ "Failed to get peer name!", /* 16 */ "Failed to accept client connection!", /* 17 */ "Failed to listen for connection attempts!", /* 18 */ "Failed to bind to specified port!", /* 19 */ "Failed to open input file \"", /* 20 */ "\"!", /* 21 */ "Failed to transfer data to sender process!", /* 22 */ "Failed to send data!", /* 23 */ "Illegal port number: ", /* 24 */ "!", /* 25 */ "Missing port number!", /* 26 */ "Multiple hosts specified!", /* 27 */ "Not enough memory (RAM/swap space)!", /* 28 */ "Illegal host:port combination \"", /* 29 */ "\"!", /* 30 */ "Host:port \"", /* 31 */ "\" too long!", /* 32 */ "Too many access rules!", /* 33 */ "Invalid IP/mask \"", /* 34 */ "\"!", /* 35 */ "Missing IP/mask!", /* 36 */ "Number of clients reduced to ", /* 37 */ ".", /* 38 */ "Illegal number of clients!", /* 39 */ "Illegal option \"", /* 40 */ "\"!", /* 41 */ "Too many file names \"", /* 42 */ "\"!", /* 43 */ "No sender/recipient mode was choosen!", /* 44 */ "Old file name: \"", /* 45 */ "\".", /* 46 */ "Transmission succeeded for ", /* 47 */ ".", /* 48 */ "Transmission failed for ", /* 49 */ "!", /* 50 */ "Failed to initialize TCP/IP subsystem!", /* 51 */ "Failed to start new sender thread!", /* 52 */ "Failed to create event for thread communication!", /* 53 */ "Problem while reading input!", /* 54 */ "Start of transmission.", /* 55 */ "End of transmission.", /* 56 */ "Client ", /* 57 */ " accepted.", /* 58 */ "Listening for connection requests.", /* 59 */ "Finished listening for connection requests.", /* 60 */ "Cleanup finished.", /* 61 */ "Sender sub-processes terminated.", /* 62 */ " not authorized!", NULL }; /** Array of data structures represening the subprocesses/threads for sending data. */ static TTPI ttpi[TCPTOOL_MAX_CLIENTS]; /** Print message consisting of multiple parts. @param ttj Tcptool job. @param msg_time Time of message. @param ll Log level. @param msgs The parts of the message. */ static void ttj_msg_multi DK_P4(TTJ *,ttj, time_t,msg_time, int,ll, char **,msgs) { static time_t previous_message_time = (time_t)0; char **ptr; /* Pointer to traverse array msgs. */ time_t timer; /* Variable to store current time. */ struct tm *tm; /* Time conversion into readable format. */ if((ll <= ttj->ll) && (msgs[0])) { timer = msg_time; if(timer == (time_t)0) { time(&timer); } if(timer != previous_message_time) { previous_message_time = timer; tm = localtime(&timer); if(tm) { fprintf( stderr, "# %04d-%02d-%02d %02d:%02d:%02d\n", (1900 + tm->tm_year), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } } fprintf(stderr, "%s: ", program_name); switch(ll) { case TCPTOOL_LL_ERROR: { fprintf(stderr, "%s: ", messages[0]); } break; case TCPTOOL_LL_WARNING: { fprintf(stderr, "%s: ", messages[1]); } break; } ptr = msgs; while(*ptr) { fprintf(stderr, "%s", *(ptr++)); } fprintf(stderr, "\n"); } } /** Print message consisting of one part. @param ttj Tcptool job. @param ll Log level. @param n Index of text in messages[] array. */ static void ttj_msg1 DK_P3(TTJ *,ttj, int,ll, size_t,n) { char *msgs[2]; msgs[0] = messages[n]; msgs[1] = NULL; ttj_msg_multi(ttj, (time_t)0, ll, msgs); } /** Print message consisting of three parts. @param ttj Tcptool job. @param ll Log level. @param n1 Index of first part message text in messages[]. @param t Second part of message, direct text, i.e. file name. @param n2 Index of third part message text in messages[]. */ static void ttj_msg3 DK_P5(TTJ *,ttj, int,ll, size_t,n1, size_t,n2, char *,t) { char *msgs[4]; msgs[0] = messages[n1]; msgs[1] = t; msgs[2] = messages[n2]; msgs[3] = NULL; ttj_msg_multi(ttj, (time_t)0, ll, msgs); } /** Handler to process SIGPIPE signal. @param signo Signal to handle (SIGPIPE). */ static void sigpipe_handler DK_P1(int,signo) { SIGREFRESH(signo,sigpipe_handler) sigpipe_received = 1; } /** Start TCP/IP subsystem. Only necessary on Windows. @return 1 on success, 0 on error. */ static int tcpip_start DK_P0() { int back = 1; #if ON_WINDOWS_SYSTEM WORD vrq; WSADATA wsa; vrq = MAKEWORD(2,0); if(WSAStartup(vrq, &wsa) != 0) { back = 0; } #endif return back; } /** End TCP/IP subsystem. Only necessary on Windows. */ static void tcpip_end DK_P0() { #if ON_WINDOWS_SYSTEM WSACleanup(); #endif } /** Obtain IP address from dotted string notation, return _host_ representation. @param hn String to test for IP address. @return IP4 address in host byte order or 0UL. */ static unsigned long dotted_string_to_ip DK_P1(char *,hn) { unsigned long back = 0UL; unsigned long u1 = 0UL, u2 = 0UL, u3 = 0UL, u4 = 0UL, u = 0UL; int ende, state; char *ptr; $? "+ dotted_string_to_ip %s", TR_STR(hn) if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { if(isdigit(*ptr)) { u = 0UL; switch(*ptr) { case '0': u = 0UL; break; case '1': u = 1UL; break; case '2': u = 2UL; break; case '3': u = 3UL; break; case '4': u = 4UL; break; case '5': u = 5UL; break; case '6': u = 6UL; break; case '7': u = 7UL; break; case '8': u = 8UL; break; case '9': u = 9UL; break; } switch(state) { case 0: u1 = 10UL * u1 + u; break; case 1: u2 = 10UL * u2 + u; break; case 2: u3 = 10UL * u3 + u; break; case 3: u4 = 10UL * u4 + u; break; } } else { if(*ptr == '.') { state++; if(state >= 4) { ende = 1; } } } ptr++; } else { ende = 1; } } } u1 = u1 << 24; u1 = u1 & 0xFF000000UL; u2 = u2 << 16; u2 = u2 & 0x00FF0000UL; u3 = u3 << 8; u3 = u3 & 0x0000FF00UL; u4 = u4 & 0x000000FFUL; back = u1 | u2 | u3 | u4; $? "- dotted_string_to_ip %lu.%lu.%lu.%lu", u1, u2, u3, u4 return back; } /** Retrieve IP for host name in _network_ representation. @param hn String containing IP address or host name. @return IP4 address on success, 0UL on error. */ static unsigned long lookup_host DK_P1(char *,hn) { unsigned long back = 0UL, *ulptr; struct hostent *he; char **xptr; $? "+ lookup_host" he = gethostbyname(hn); if(he) { if(he->h_addrtype == AF_INET) { if(he->h_length == 4) { if(he->h_addr_list) { xptr = he->h_addr_list; ulptr = (unsigned long *)(*xptr); if(ulptr) { back = *ulptr; $? ". host found" } } } } } $? "- lookup_host" return back; } /** IP subnets of allowed clients. */ static unsigned long allowed_ip[TCPTOOL_MAX_ALLOWED_IP]; /** Netmasks of allowed clients. */ static unsigned long allowed_masks[TCPTOOL_MAX_ALLOWED_IP]; /** Number of allwoed clients in list. */ static size_t allowed_in_use = 0; /** Add a host/subnet to the list of allowed clients. @param i IP address of host/subnet. @param m Subnet mask. @return 1 on success, 0 on error (too many clients). */ static int add_allowed_ip DK_P2(unsigned long,i, unsigned long,m) { int back = 0; if(allowed_in_use < TCPTOOL_MAX_ALLOWED_IP) { allowed_ip[allowed_in_use] = i; allowed_masks[allowed_in_use] = m; allowed_in_use++; back = 1; } return back; } /** Check whether a client IP address in _network_ representation is acceptable. (It is either in the list or the list is empty.) @param haddr Client address in network representation. @return 1 if client is allowed, 0 if client is denied. */ static int is_acceptable_client DK_P1(unsigned long,haddr) { int back = 0; size_t i; /* Index to traverse allowed_ip[]/allowed_masks[]. */ if(allowed_in_use) { for(i = 0; ((i < allowed_in_use) && (back == 0)); i++) { if((allowed_ip[i] & allowed_masks[i]) == (haddr & allowed_masks[i])) { back = 1; } } } else { back = 1; } return back; } /** Print IP address in _network_ representation to buffer. @param b Buffer (must be at least 95 bytes). @param ip IP address to print. @param pn Port number to print. */ static void show_ip_address DK_P3(char *,b, unsigned long,ip, unsigned short,pn) { unsigned long ul, ul1, ul2, ul3, ul4; unsigned short up; ul = ntohl(ip); up = ntohs(pn); ul1 = ((ul >> 24) & 255UL); ul2 = ((ul >> 16) & 255UL); ul3 = ((ul >> 8) & 255UL); ul4 = ((ul ) & 255UL); sprintf(b, "%lu.%lu.%lu.%lu:%u", ul1, ul2, ul3, ul4, (unsigned)up); } /** Write diagnostic message for TCPTOOL_ERROR_INFO datagram. @param ttj Tcptool job. @param ei Structure containing information about error/success. */ static void report_error_info DK_P2(TTJ *,ttj, TCPTOOL_ERROR_INFO *,ei) { char ipbuffer[128], *msgs[4]; int ll = TCPTOOL_LL_INFO; show_ip_address(ipbuffer, ei->error_ip, ei->error_port); msgs[3] = NULL; msgs[1] = ipbuffer; switch(ei->error_code) { case TCPTOOL_ERROR_NONE: { msgs[0] = messages[46]; msgs[2] = messages[47]; } break; default: { msgs[0] = messages[48]; msgs[2] = messages[49]; ll = TCPTOOL_LL_ERROR; } break; } ttj_msg_multi(ttj, ei->error_time, ll, msgs); } /** Receiver functionality. @param ttj Tcptool job. */ static void run_recipient DK_P1(TTJ *,ttj) { FILE *fipo = NULL; /* used to write output file */ TCP_VARIABLE_YES; int res = 0; /* result of read/write ops */ int can_continue; /* Flag: not yet finished */ int is_first; /* Flag: First data block */ TCP_TYPE_SOCKET sockfd; /* Socket for network data transfer */ struct sockaddr_in soin; /* IP address of sender */ char buffer[1024]; /* Buffer for incoming data */ unsigned long ipaddr; /* IP address to connect to */ TCP_TYPE_READ_WRITE sz, sz2; /* Size variables */ #if ON_WINDOWS_SYSTEM int oldmode = _O_TEXT; #endif $(trace-init tcptool-recipient.deb) $? "+ run_recipient" if(ttj->host_name) { $? ". host name ok" sockfd = TCP_SOCKET(PF_INET,SOCK_STREAM,0); if(TCP_OK_SOCKET(sockfd)) { $? ". socket ok" ipaddr = dotted_string_to_ip(ttj->host_name); if(ipaddr) { $? ". ipaddr ok" ipaddr = htonl(ipaddr); } else { ipaddr = lookup_host(ttj->host_name); } if(ipaddr) { $? ". ipaddr ok" TCP_REUSEADDR(sockfd); soin.sin_family = AF_INET; $? ". port = %u", (unsigned)(ttj->host_port) soin.sin_port = htons(ttj->host_port); soin.sin_addr.s_addr = ipaddr; res = TCP_CONNECT(sockfd,(struct sockaddr *)(&soin),SZSOIN); if(res == 0) { $? ". connected" if(TCP_SEND(sockfd,(const void *)(&(ttj->start_now)),1,0) == 1) { TCP_SHUTDOWN_WRITE(sockfd); can_continue = 1; is_first = 1; fipo = NULL; while(can_continue) { can_continue = 0; sz = TCP_RECV(sockfd,(void *)buffer,sizeof(buffer),0); if(sz > 0) { $? ". data found" can_continue = 1; if(is_first) { if(ttj->file_name) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(ttj->file_name, "wb"); #else fipo = fopen(ttj->file_name, "wb"); #endif if(fipo) { ttj->exval = 0; } else { /* ERROR: Failed to write output file */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 2, 3, ttj->file_name); } } else { #if ON_WINDOWS_SYSTEM oldmode = _setmode(_fileno(stdout), _O_BINARY); #endif ttj->exval = 0; } } is_first = 0; if(ttj->file_name) { if(fipo) { sz2 = fwrite((void *)buffer, 1, sz, fipo); if(sz2 != sz) { ttj->exval = 1; /* ERROR: Failure during write operation */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 4); } } } else { sz2 = fwrite((void *)buffer, 1, sz, stdout); if(sz2 != sz) { ttj->exval = 1; /* ERROR: Failure during write operation */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 4); } } } } #if ON_WINDOWS_SYSTEM if(!is_first) { if(!(ttj->file_name)) { fflush(stdout); _setmode(_fileno(stdout), oldmode); } } #endif TCP_SHUTDOWN_READ(sockfd); if(fipo) { fclose(fipo); } } else { $? "! failed to send start byte" /* ERROR: Failed to send start byte */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 5); } } else { $? "! failed to connect %d", errno /* ERROR: Failed to connect */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 6); } } else { $? "! remote host not found" /* ERROR: Host not found */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 7); } TCP_CLOSE_SOCKET(sockfd); } else { $? "! failed to create socket" /* ERROR: Failed to create socket */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8); } } else { $? "! missing host name" /* ERROR: No host name */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 9); } $? "- run_recipient" $(trace-end) } /** Run sender for one connection. Listen for incoming connection request and handle one client. If we know to have just one client we do not need to create sub processes, we can send directly instead. @param ttj TCP tool job. */ static void run_sender_one DK_P1(TTJ *,ttj) { FILE *fipo = NULL; /* Used to read input */ TCP_VARIABLE_YES; TCP_TYPE_SOCKET socka; /* Socket to listen for conn req */ TCP_TYPE_SOCKET socks; /* Socket for data transfer */ int can_continue; /* Flag: not yet finished */ struct sockaddr_in soin; /* Address of client */ size_t szsoin; /* Size of client address */ char buffer[1024]; /* Buffer for send operations */ TCP_TYPE_READ_WRITE sz, sz2; /* Size variables */ int finished; /* Flag: finished listening */ char ipbuffer[128]; /* Buffer to show IP address */ #if ON_WINDOWS_SYSTEM int oldmode = _O_TEXT; /* text/binary mode */ #endif #if DK_HAVE_SIGACTION /* sigaction available */ struct sigaction act, oact; SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGSET /* sigset available */ SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGNAL /* signal available */ SIGHANDLER *oldsigpipe = NULL; #else /* no signal handling available */ #endif #endif #endif fipo = NULL; $(trace-init tcptool-send-one.deb) $? "+ run_sender_one" if(ttj->file_name) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(ttj->file_name, "rb"); #else fipo = fopen(ttj->file_name, "rb"); #endif } else { #if ON_WINDOWS_SYSTEM oldmode = _setmode(_fileno(stdin), _O_BINARY); #endif fipo = stdin; } if(fipo) { $? ". input file ok" socka = TCP_SOCKET(PF_INET,SOCK_STREAM,0); if(TCP_OK_SOCKET(socka)) { $? ". listen socket ok" TCP_REUSEADDR(socka); soin.sin_family = AF_INET; soin.sin_port = htons(ttj->host_port); soin.sin_addr.s_addr = htonl(INADDR_ANY); if(TCP_BIND(socka,(struct sockaddr *)(&soin),SZSOIN) == 0) { $? ". local address bind ok" finished = 0; /* PROGRESS: start listening for connection attempts */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 58); while(!finished) { if(TCP_LISTEN(socka,2) == 0) { szsoin = SZSOIN; socks = TCP_ACCEPT(socka,(struct sockaddr *)(&soin),&szsoin); if(TCP_OK_SOCKET(socks)) { $? ". client accepted" TCP_REUSEADDR(socks); $? ". peer name found" if(szsoin == SZSOIN) { $? ". peer name size ok" if(is_acceptable_client(soin.sin_addr.s_addr)) { $? ". peer acceptable" /* INFO: client accepted */ show_ip_address(ipbuffer, soin.sin_addr.s_addr, soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_INFO, 56, 57, ipbuffer); finished = 1; sz = TCP_RECV(socks,(void *)buffer,1,0); if(sz == 1) { $? ". byte was read" ttj->exval = 0; /* install SIGPIPE handler */ #if DK_HAVE_SIGACTION #ifdef SIGPIPE act.sa_handler = sigpipe_handler; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; if(sigaction(SIGPIPE, &act, &oact) == 0) { oldsigpipe = oact.sa_handler; } else { oldsigpipe = SIG_ERR; } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE oldsigpipe = sigset(SIGPIPE, sigpipe_handler); #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE oldsigpipe = signal(SIGPIPE, sigpipe_handler); #endif #else /* no signal handling available */ #endif #endif #endif /* PROGRESS: start of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 54); can_continue = 1; while(can_continue) { $? ". start of loop" can_continue = 0; sz = fread((void *)buffer, 1, sizeof(buffer), fipo); if(sz > 0) { $? ". data found in file" can_continue = 1; sz2 = TCP_SEND(socks,(void *)buffer,sz,0); if(sz2 != sz) { $? ". problem while sending data" ttj->exval = 1; /* ERROR: Problem while sending data */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 22); } } else { /* Finished */ } $? ". end of loop" } TCP_SHUTDOWN_WRITE(socks); can_continue = 1; while(can_continue) { can_continue = 0; sz2 = TCP_RECV(socks,(void *)buffer,sizeof(buffer),0); if(sz2 > 0) { can_continue = 1; } } TCP_SHUTDOWN_READ(socks); /* PROGRESS: end of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 55); /* uninstall SIGPIPE handler */ #if DK_HAVE_SIGACTION #ifdef SIGPIPE if(oldsigpipe) { act.sa_handler = oldsigpipe; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; sigaction(SIGPIPE, &act, &oact); } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE if(oldsigpipe) { sigset(SIGPIPE, oldsigpipe); } #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE if(oldsigpipe) { signal(SIGPIPE, oldsigpipe); } #endif #else /* no signal handling available */ #endif #endif #endif } else { $? "! failed to read initial byte" /* ERROR: Failed to read initial byte */ finished = -1; ttj_msg1(ttj, TCPTOOL_LL_ERROR, 12); } } else { $? "! peer not allowed to connect" /* ERROR: Not in the list of allowed recipients */ show_ip_address(ipbuffer, soin.sin_addr.s_addr, soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_ERROR, 13, 62, ipbuffer); } } else { $? "! peer name has wrong size" finished = -1; /* ERROR: Peer name has wrong size */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 14); } TCP_CLOSE_SOCKET(socks); } else { $? "! failed to accept client" finished = -1; /* ERROR: Failed to accept client connection */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 16); } } else { $? "! failed to listen" finished = -1; /* ERROR: Failed to listen on port */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 17); } } if(finished == 1) { /* PROGRESS: end listening for connection requests */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 59); } } else { $? "! failed to bind local port" /* ERROR: Failed to bind local socket */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 18); } TCP_CLOSE_SOCKET(socka); } else { $? "! failed to create listen socket" /* ERROR: Failed to create socket */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8); } if(ttj->file_name) { fclose(fipo); fipo = NULL; } else { #if ON_WINDOWS_SYSTEM _setmode(_fileno(stdin), oldmode); #endif } } else { $? "! no input file" /* ERROR: Failed to open file */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 19, 20, ttj->file_name); } $? "- run_sender_one" $(trace-end) } #if ON_WINDOWS_SYSTEM /* +++ On a Windows system */ /** Windows: Transfer buffer, written by master thread, read by slave threads. */ static char buffer[1024]; /** WIndows: Number of bytes available in transfer buffer. */ static TCP_TYPE_READ_WRITE sz_buffer; /** Windows: Action the slaves have to do, SLAVE_ACTION_xxx. */ static int slave_action = 0; /** Windows: Slaves can send. */ #define SLAVE_ACTION_SEND 1 /** Windows: Waiting for "finished" event from slaves. */ #define SLAVE_ACTION_FINISH 2 /** Windows: Thread function dealing with one connection. @param dataforthread TTPI pointer for connection. */ static void __cdecl run_slave DK_P1(void *,dataforthread) { TTJ *ttj; TTPI *ttpiptr; /* data pointer for connection */ int can_continue; /* Flag: not yet finished */ DWORD res; /* Read/write operation result */ TCP_TYPE_READ_WRITE sz; /* Read/write data size */ ttpiptr = (TTPI *)dataforthread; ttj = ttpiptr->ttj; do { can_continue = 1; res = WaitForSingleObjectEx(ttpiptr->hEventSlave, INFINITE, FALSE); switch(res) { case WAIT_OBJECT_0: { switch(slave_action) { case SLAVE_ACTION_FINISH: { can_continue = 0; } break; case SLAVE_ACTION_SEND: { /* buffer senden wenn noch kein Fehler */ if(!((ttpiptr->ei).error_code)) { sz = TCP_SEND(ttpiptr->socks,(void *)buffer,sz_buffer,0); if(sz != sz_buffer) { (ttpiptr->ei).error_code = TCPTOOL_ERROR_SENDFAILED; time(&((ttpiptr->ei).error_time)); } } } break; } SetEvent(ttpiptr->hEventMaster); } break; } } while(can_continue); _endthread(); } /** Windows: Send data to multiple recipients. A new thread is created to deal with each connection. The main thread reads the input file, sends a ready-to-send event to the connection threads and waits for a sending-finished event from the connection threads (in a loop for all input blocks). @param ttj Tcptool job. */ static void run_sender_multiple DK_P1(TTJ *,ttj) { TCP_VARIABLE_YES; TCP_TYPE_SOCKET socka; /* Socket to listen for incoming con req */ TCP_TYPE_SOCKET socks; /* Socket for data transfer */ unsigned long num_connections = 0UL; /* No of connections establ. */ unsigned long i = 0UL; /* Index to traverse ttpi[]*/ struct sockaddr_in soin; /* IP address of peer */ size_t szsoin; /* Size of peer IP address */ int finished; /* Flag: finished listening */ TTPI *ttpiptr; /* Pointer to TTPI for current conn */ unsigned char uc; /* Flag: All clients connected */ FILE *fipo; /* Used to read input file */ int oldmode = _O_TEXT; /* Old I/O mode */ DWORD dwMsWait; /* Timeout when waiting for thread */ DWORD res; /* Result of wait */ int can_continue; /* Flag: not yet finished */ char ipbuffer[128]; /* Buffer to show IP address */ $(trace-init tcptool-send-multi.deb) $? "+ run_sender_multi (Windows)" dwMsWait = 1000UL * ttj->wait_timeout; num_connections = 0UL; socka = TCP_SOCKET(PF_INET,SOCK_STREAM,0); if(TCP_OK_SOCKET(socka)) { TCP_REUSEADDR(socka); soin.sin_family = AF_INET; soin.sin_port = htons(ttj->host_port); soin.sin_addr.s_addr = htonl(INADDR_ANY); if(TCP_BIND(socka,(struct sockaddr *)(&soin), SZSOIN) == 0) { finished = 0; ttpiptr = ttpi; num_connections = 0UL; /* PROGRESS: start listening for connection requests */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 58); while(!finished) { if(TCP_LISTEN(socka,2) == 0) { szsoin = SZSOIN; socks = TCP_ACCEPT(socka,(struct sockaddr *)(&soin),&szsoin); if(TCP_OK_SOCKET(socks)) { if(szsoin == SZSOIN) { if(is_acceptable_client(soin.sin_addr.s_addr)) { uc = 0x00; if(TCP_RECV(socks,(void *)(&uc),1,0) == 1) { if(uc) { finished = 1; } (ttpiptr->ei).error_code = TCPTOOL_ERROR_NONE; (ttpiptr->ei).error_time = (time_t)0; (ttpiptr->ei).error_ip = soin.sin_addr.s_addr; (ttpiptr->ei).error_port = soin.sin_port; ttpiptr->ttj = ttj; ttpiptr->socks = socks; ttpiptr->ip = soin.sin_addr.s_addr; // ttpiptr->port = soin.sin_port; ttpiptr->conn_no = num_connections; ttpiptr->thread_id = 0; ttpiptr->hEventSlave = NULL; ttpiptr->hEventMaster = NULL; ttpiptr->did_timeout = 0x00; ttpiptr->hEventSlave = CreateEvent(NULL, FALSE, FALSE, NULL); if(ttpiptr->hEventSlave != NULL) { ttpiptr->hEventMaster = CreateEvent(NULL,FALSE,FALSE,NULL); if(ttpiptr->hEventMaster != NULL) { ResetEvent(ttpiptr->hEventSlave); ResetEvent(ttpiptr->hEventMaster); ttpiptr->thread_id = _beginthread( run_slave, 0, (void *)ttpiptr ); if(ttpiptr->thread_id != -1L) { ttpiptr++; num_connections++; if(ttj->max_clients) { if(num_connections >= ttj->max_clients) { finished = 1; } } if(num_connections >= TCPTOOL_MAX_CLIENTS) { finished = 1; } /* INFO: client accepted */ show_ip_address(ipbuffer, soin.sin_addr.s_addr, soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_INFO, 56, 57, ipbuffer); } else { finished = 1; TCP_CLOSE_SOCKET(socks); CloseHandle(ttpiptr->hEventSlave); CloseHandle(ttpiptr->hEventMaster); /* ERROR: Failed to start thread */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 51); } } else { finished = 1; TCP_CLOSE_SOCKET(socks); CloseHandle(ttpiptr->hEventSlave); /* ERROR: CreateEvent() */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 52); } } else { finished = 1; TCP_CLOSE_SOCKET(socks); /* ERROR: CreateEvent() */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 52); } } else { finished = 1; TCP_CLOSE_SOCKET(socks); ttj_msg1(ttj, TCPTOOL_LL_ERROR, 12); } } else { TCP_CLOSE_SOCKET(socks); show_ip_address(ipbuffer, soin.sin_addr.s_addr, soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_ERROR, 13, 62, ipbuffer); } } else { finished = 1; TCP_CLOSE_SOCKET(socks); ttj_msg1(ttj, TCPTOOL_LL_ERROR, 14); } } else { finished = 1; ttj_msg1(ttj, TCPTOOL_LL_ERROR, 16); } } else { finished = 1; ttj_msg1(ttj, TCPTOOL_LL_ERROR, 17); } } /* PROGRESS: end listening for connection requests */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 59); } else { ttj_msg1(ttj, TCPTOOL_LL_ERROR, 18); } TCP_CLOSE_SOCKET(socka); if(num_connections) { ttj->exval = 0; if(ttj->file_name) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(ttj->file_name, "rb"); #else fipo = fopen(ttj->file_name, "rb"); #endif if(!fipo) { /* ERROR: fopen() */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 2, 3, ttj->file_name); ttj->exval = 1; } } else { fipo = stdin; oldmode = _setmode(_fileno(stdin), _O_BINARY); } if(fipo) { slave_action = SLAVE_ACTION_SEND; /* PROGRESS: start of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 54); do { can_continue = 0; sz_buffer = fread((void *)buffer, 1, sizeof(buffer), fipo); if(sz_buffer > 0) { can_continue = 1; /* allow slave threads to send */ for(i = 0; i < num_connections; i++) { if(!(ttpi[i].did_timeout)) { SetEvent(ttpi[i].hEventSlave); } } /* wait for slave threads to complete */ for(i = 0; i < num_connections; i++) { if(!(ttpi[i].did_timeout)) { res = WaitForSingleObjectEx(ttpi[i].hEventMaster, dwMsWait, FALSE); switch(res) { case WAIT_TIMEOUT: { ttpi[i].did_timeout = 0x01; ttj->exval = 1; } break; } } } } else { if(sz_buffer < 0) { /* ERROR: fread() */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 53); ttj->exval = 1; } } } while(can_continue); /* PROGRESS: end of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 55); /* tell slave threads to finish */ slave_action = SLAVE_ACTION_FINISH; for(i = 0; i < num_connections; i++) { SetEvent(ttpi[i].hEventSlave); } /* wait for slave threads to finish */ for(i = 0; i < num_connections; i++) { res = WaitForSingleObjectEx(ttpi[i].hEventMaster, dwMsWait, FALSE); switch(res) { case WAIT_TIMEOUT: { ttpi[i].did_timeout = 0x01; ttj->exval = 1; } break; } } if(ttj->file_name) { fclose(fipo); } else { _setmode(_fileno(stdin), oldmode); } } /* release handles, close sockets */ for(i = 0; i < num_connections; i++) { CloseHandle(ttpi[i].hEventSlave); ttpi[i].hEventSlave = NULL; CloseHandle(ttpi[i].hEventMaster); ttpi[i].hEventMaster = NULL; TCP_SHUTDOWN_WRITE(ttpi[i].socks); do { can_continue = 0; sz_buffer = TCP_RECV(ttpi[i].socks,(void *)buffer,sizeof(buffer),0); if(sz_buffer > 0) { can_continue = 1; } } while(can_continue); TCP_SHUTDOWN_READ(ttpi[i].socks); TCP_CLOSE_SOCKET(ttpi[i].socks); ttpi[i].socks = INVALID_SOCKET; } /* PROGRESS: cleanup finished */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 60); /* print messages */ for(i = 0; i < num_connections; i++) { if(ttpi[i].did_timeout) { ttpi[i].ei.error_code = TCPTOOL_ERROR_THREAD_TIMEOUT; time(&(ttpi[i].ei.error_time)); } if(ttpi[i].ei.error_code) { ttj->exval = 1; } } for(i = 0; i < num_connections; i++) { report_error_info(ttj, &(ttpi[i].ei)); } } } else { ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8); } $? "- run_sender_multi" $(trace-end) } /* --- On a Windows system */ #else /* +++ UNIX/Linux operating system */ /** UNIX: subprocess for one connection. @param ttj Tcptool job. @param ttpiptr Pointer to current TTPI. @param allttpi Pointer to the TTPI array. @param cn Current connection number. @param socka Socket used for listening, must close this. */ static void run_slave DK_P5(TTJ *,ttj, TTPI *,ttpiptr, TTPI *,allttpi, unsigned long,cn, int,socka) { unsigned long i; /* Index, used to close fdesks */ int can_continue; /* Flag: not yet finished */ char buffer[1024]; /* Data buffer */ ssize_t sz; /* Size of data in bytes */ ssize_t sz2; /* Number of bytes sent */ TCPTOOL_ERROR_INFO ei; /* Store error information */ #if DK_HAVE_SIGACTION /* sigaction available */ struct sigaction act, oact; SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGSET /* sigset available */ SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGNAL /* signal available */ SIGHANDLER *oldsigpipe = NULL; #else /* no signal handling available */ #endif #endif #endif $? "+ run_slave conn=%lu", ttpiptr->conn_no /* detach from parent (no SIGCHLD if process exits) */ /* The master process waits for the subprocesses, so we do not need to detach. */ #if 0 #if DK_HAVE_SETSID setsid(); #else #if DK_HAVE_SETPGRP setpgrp(); #endif #endif #endif /* close all file handles and pipes not used by ours */ close(socka); for(i = 0; i < cn; i++) { close(allttpi[i].data_write_head); close(allttpi[i].ctrl_read_head); allttpi[i].data_write_head = allttpi[i].ctrl_read_head = -1; } /* install SIGPIPE handler */ #if DK_HAVE_SIGACTION /* sigaction available */ #ifdef SIGPIPE act.sa_handler = sigpipe_handler; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; if(sigaction(SIGPIPE, &act, &oact) == 0) { oldsigpipe = oact.sa_handler; } else { oldsigpipe = SIG_ERR; } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE /* sigset available */ oldsigpipe = sigset(SIGPIPE, sigpipe_handler); #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE /* signal available */ oldsigpipe = signal(SIGPIPE, sigpipe_handler); #endif #else /* no signal handling available */ #endif #endif #endif /* run transmission */ ei.error_code = TCPTOOL_ERROR_NONE; ei.error_time = (time_t)0; ei.error_ip = ttpiptr->ip; ei.error_port = ttpiptr->port; can_continue = 1; while(can_continue) { $? ". while (conn=%lu)", ttpiptr->conn_no can_continue = 0; // sz = read(ttpiptr->fd_m2s[0], (void *)buffer, sizeof(buffer)); sz = read(ttpiptr->data_read_head, (void *)buffer, sizeof(buffer)); if(sz > 0) { $? ". read() = %d (conn=%lu)", (int)sz, ttpiptr->conn_no can_continue = 1; if(!(ei.error_code)) { $? ". going to send (conn=%lu)", ttpiptr->conn_no sz2 = send(ttpiptr->socks, (void *)buffer, sz, 0); $? ". bytes sent = %u (conn=%lu)", (unsigned)sz2, ttpiptr->conn_no if(sz2 != sz) { $? "! send() = %d (conn=%lu)", (int)sz2, ttpiptr->conn_no ei.error_code = TCPTOOL_ERROR_SENDFAILED; time(&(ei.error_time)); #ifdef EPIPE if(sz2 == -1) { if(errno == EPIPE) { ei.error_code = TCPTOOL_ERROR_SIGPIPE; } } #endif } if(sigpipe_received) { $? ". sigpipe_received (conn=%lu)", ttpiptr->conn_no ei.error_code = TCPTOOL_ERROR_SIGPIPE; time(&(ei.error_time)); } } else { $? ". Skipping transmision, previous errors (conn=%lu)", ttpiptr->conn_no } } } if(ei.error_code) { $? ". sending error code to master (conn=%lu)", ttpiptr->conn_no /* send error information to master */ (void)write(ttpiptr->ctrl_write_head, (void *)(&ei), sizeof(TCPTOOL_ERROR_INFO)); } else { $? ". no error, orderly release for socket (conn=%lu)", ttpiptr->conn_no /* orderly release socket */ shutdown(ttpiptr->socks, SHUT_WR); can_continue = 1; while(can_continue) { can_continue = 0; sz = recv(ttpiptr->socks, (void *)buffer, sizeof(buffer), 0); if(sz > 0) { can_continue = 1; } } $? ". sending success message to master (conn=%lu)", ttpiptr->conn_no if(!(ei.error_time)) { time(&(ei.error_time)); } (void)write(ttpiptr->ctrl_write_head,(void *)(&ei),sizeof(TCPTOOL_ERROR_INFO)); } close(ttpiptr->socks); /* close pipe file descriptor (slave end) */ close(ttpiptr->data_read_head); ttpiptr->data_read_head = -1; close(ttpiptr->ctrl_write_head); ttpiptr->ctrl_write_head = -1; /* uninstall SIGPIPE handler */ #if DK_HAVE_SIGACTION #ifdef SIGPIPE /* sigaction available */ if(oldsigpipe) { act.sa_handler = oldsigpipe; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; sigaction(SIGPIPE, &act, &oact); } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE /* sigset available */ if(oldsigpipe) { sigset(SIGPIPE, oldsigpipe); } #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE /* signal available */ if(oldsigpipe) { signal(SIGPIPE, oldsigpipe); } #endif #else /* no signal handling available */ #endif #endif #endif $? "- run_slave (conn=%lu)", ttpiptr->conn_no exit(0); } /** UNIX: Send data to multiple recipients. Start subprocesses for individual connections, data is provided to the subprocesses using pipes. @param ttf Tcptool job. */ static void run_sender_multiple DK_P1(TTJ *,ttj) { TCPTOOL_ERROR_INFO eibuffer; /* Store error information */ FILE *fipo = NULL; /* Read input file */ unsigned long num_connections = 0UL; /* No of conns establ. */ unsigned long i = 0UL; /* Traverse allttpi[] */ struct sockaddr_in soin; /* Peer address */ TTPI *ttpiptr; /* Current TTPI */ unsigned char uc; /* Flag: Last client conn. */ char buffer[1024]; /* Data transfer buffer */ ssize_t sz; /* Number of bytes in buffer */ ssize_t sz2; /* Number of bytes sent */ size_t szsoin; /* Size of peer address */ int pfd[2]; /* Temp. array for pipe() */ int can_continue; /* Flag: Not yet finished */ int socka; /* Socket to listen */ int socks; /* Data transfer socket */ int finished; /* Flag: finished listening */ char ipbuffer[128]; /* Show IP address */ #if DK_HAVE_SETSOCKOPT int yes = 1; /* Enable options */ #endif #if DK_HAVE_SIGACTION /* sigaction available */ struct sigaction act, oact; SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGSET /* sigset available */ SIGHANDLER *oldsigpipe = NULL; #else #if DK_HAVE_SIGNAL /* signal available */ SIGHANDLER *oldsigpipe = NULL; #else /* no signal handling available */ #endif #endif #endif $(trace-init tcptool-send-multi.deb) $? "+ run_sender_multi" num_connections = 0UL; socka = socket(PF_INET, SOCK_STREAM, 0); if(socka > -1) { $? ". socket()" #if DK_HAVE_SETSOCKOPT setsockopt(socka, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); #endif soin.sin_family = AF_INET; soin.sin_port = htons(ttj->host_port); soin.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(socka, (struct sockaddr *)(&soin), SZSOIN) == 0) { $? ". bind()" finished = 0; ttpiptr = ttpi; num_connections = 0UL; /* PROGRESS: start listening for connection requests */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 58); while(!finished) { $? ". not finished" if(listen(socka, 2) == 0) { $? ". listen()" szsoin = SZSOIN; socks = accept(socka, (struct sockaddr *)(&soin), &szsoin); if(socks > -1) { $? ". accept()" if(szsoin == SZSOIN) { if(is_acceptable_client(soin.sin_addr.s_addr)) { uc = 0x00; $? ". IP ok" if(recv(socks, (void *)(&uc), 1, 0) == 1) { $? ". recv()" if(uc) { finished = 1; } // ttpiptr->fd_m2s[0] = -1; ttpiptr->fd_m2s[1] = -1; ttpiptr->data_write_head = ttpiptr->data_read_head = -1; ttpiptr->socks = socks; ttpiptr->pid = (pid_t)0; ttpiptr->ip = soin.sin_addr.s_addr; ttpiptr->port = soin.sin_port; if(pipe(pfd) == 0) { $? ". pipe()" ttpiptr->data_read_head = pfd[0]; ttpiptr->data_write_head = pfd[1]; if(pipe(pfd) == 0) { ttpiptr->ctrl_read_head = pfd[0]; ttpiptr->ctrl_write_head = pfd[1]; ttpiptr->pid = fork(); switch((int)(ttpiptr->pid)) { case -1: { $? "! fork()" (void)close(ttpiptr->data_read_head); (void)close(ttpiptr->data_write_head); ttpiptr->data_read_head = ttpiptr->data_write_head = -1; (void)close(ttpiptr->ctrl_read_head); (void)close(ttpiptr->ctrl_write_head); ttpiptr->ctrl_read_head = ttpiptr->ctrl_write_head = -1; finished = 1; close(socks); socks = -1; /* ERROR: fork() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 10); } break; case 0: { close(ttpiptr->data_write_head); ttpiptr->data_write_head = -1; close(ttpiptr->ctrl_read_head); ttpiptr->ctrl_read_head = -1; ttpiptr->conn_no = num_connections; run_slave(ttj,ttpiptr,ttpi,num_connections,socka); } break; default: { $? ". fork() master" close(ttpiptr->data_read_head); ttpiptr->data_read_head = -1; close(ttpiptr->ctrl_write_head); ttpiptr->ctrl_write_head = -1; ttpiptr++; num_connections++; if(ttj->max_clients) { if(num_connections >= ttj->max_clients) { finished = 1; $? ". max_clients soft" } } if(num_connections >= TCPTOOL_MAX_CLIENTS) { finished = 1; $? ". max_clients hard" } close(socks); socks = -1; /* INFO: client accepted */ show_ip_address(ipbuffer, soin.sin_addr.s_addr, soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_INFO, 56, 57, ipbuffer); } break; } } else { finished = 1; close(socks); socks = -1; $? "! pipe()" /* ERROR: pipe() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 11); } } else { /* if(pipe() == 0) */ finished = 1; close(socks); socks = -1; $? "! pipe()" /* ERROR: pipe() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 11); } /* if(pipe() == 0) */ } else { /* if(recv() == 1) */ finished = 1; close(socks); socks = -1; $? "! recv()" /* Failed to read initial byte */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 12); } /* if(recv() == 1) */ } else { /* if(is_acceptable_client()) */ close(socks); socks = -1; $? "! IP wrong" /* Client not allowed */ show_ip_address(ipbuffer,soin.sin_addr.s_addr,soin.sin_port); ttj_msg3(ttj, TCPTOOL_LL_ERROR, 13, 62, ipbuffer); } /* if(is_acceptable_client()) */ } else { /* if(szsoin == SZSOIN) */ finished = 1; close(socks); socks = -1; $? "! peer name size" /* ERROR: Wrong peer name size */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 14); } /* if(szsoin == SZSOIN) */ } else { /* if(socks > -1) */ finished = 1; $? "! accept()" /* ERROR: accept() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 16); } /* if(socks > -1) */ } else { /* if(listen() == 0) */ finished = 1; $? "! listen()" /* ERROR: listen() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 17); } /* if(listen() == 0) */ } /* while(!finished) */ /* PROGRESS: end listening for connection requests */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 59); $? ". while loop finished" } else { /* bind() == 0 */ $? "! bind()" /* ERROR: bind() failed */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 18); } /* bind() == 0 */ close(socka); if(num_connections) { $? ". num_connections" /* install SIGPIPE handler */ #if DK_HAVE_SIGACTION #ifdef SIGPIPE /* sigaction available */ act.sa_handler = sigpipe_handler; $? ". sigaction" sigemptyset(&(act.sa_mask)); act.sa_flags = 0; if(sigaction(SIGPIPE, &act, &oact) == 0) { oldsigpipe = oact.sa_handler; } else { oldsigpipe = SIG_ERR; } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE /* sigset available */ oldsigpipe = sigset(SIGPIPE, sigpipe_handler); $? ". sigset" #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE /* signal available */ oldsigpipe = signal(SIGPIPE, sigpipe_handler); $? ". signal" #endif #else /* no signal handling available */ #endif #endif #endif /* close the slave side pipe ends */ $? ". closing all slave side connections" // for(i = 0; i < num_connections; i++) { close(ttpi[i].fd_m2s[1]); } $? ". opening input file" if(ttj->file_name) { $? ". input file name" #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(ttj->file_name, "rb"); #else fipo = fopen(ttj->file_name, "rb"); #endif if(!fipo) { $? "! failed to open input file" /* ERROR: Failed to read input */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 19, 20, ttj->file_name); } } else { /* if(ttj->file_name) */ fipo = stdin; $? ". stdin" } /* if(ttj->file_name) */ if(fipo) { /* PROGRESS: start of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 54); can_continue = 1; while(can_continue) { can_continue = 0; sz = fread((void *)buffer, 1, sizeof(buffer), fipo); $? ". read() = %u", (unsigned)sz if(sz > 0) { can_continue = 1; $? ". writing bytes to pipe" for(i = 0; i < num_connections; i++) { $? ". writing to connection %lu", i // sz2 = write(ttpi[i].fd_m2s[0], (void *)buffer, sz); sz2 = write(ttpi[i].data_write_head, (void *)buffer, sz); $? ". write() = %d", (int)sz2 if(sz2 != sz) { /* ERROR: Problem while writing */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 21); $? "! problem while writing to pipe, %d", errno } } } /* if(sz > 0) */ } /* while(can_continue) */ if(ttj->file_name) { fclose(fipo); } /* PROGRESS: end of transmission */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 55); } /* if(fipo) */ /* close master pipe ends */ $? ". close master pipe ends" // for(i = 0; i < num_connections; i++) { close(ttpi[i].fd_m2s[0]); } for(i = 0; i < num_connections; i++) { close(ttpi[i].data_write_head); ttpi[i].data_write_head = -1; } for(i = 0; i < num_connections; i++) { can_continue = 1; while(can_continue) { can_continue = 0; sz = read( ttpi[i].ctrl_read_head, (void *)(&eibuffer), sizeof(TCPTOOL_ERROR_INFO) ); if(sz > 0) { can_continue = 1; if(sz == sizeof(TCPTOOL_ERROR_INFO)) { report_error_info(ttj, &eibuffer); } } } } for(i = 0; i < num_connections; i++) { close(ttpi[i].ctrl_read_head); ttpi[i].ctrl_read_head = -1; } /* PROGRESS: Cleanup finished */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 60); /* uninstall SIGPIPE handler */ #if DK_HAVE_SIGACTION #ifdef SIGPIPE /* sigaction available */ if(oldsigpipe) { act.sa_handler = oldsigpipe; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; sigaction(SIGPIPE, &act, &oact); } #endif #else #if DK_HAVE_SIGSET #ifdef SIGPIPE /* sigset available */ if(oldsigpipe) { sigset(SIGPIPE, oldsigpipe); } #endif #else #if DK_HAVE_SIGNAL #ifdef SIGPIPE /* signal available */ if(oldsigpipe) { signal(SIGPIPE, oldsigpipe); } #endif #else /* no signal handling available */ #endif #endif #endif while(wait(NULL) > 0) { } /* PROGRESS: All subprocesses terminated */ ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 61); } /* if(num_connections */ } else { /* if(socka > -1) */ $? "! socket()" /* ERROR: socket() */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8); } /* if(socka > -1) */ $? "- run_sender_multi" $(trace-end) } /* --- UNIX/Linux operating system */ #endif /** Initialize TTJ structure before using it. @param ttj Tcptool job. */ static void ttj_init DK_P1(TTJ *,ttj) { ttj->max_clients = 1UL; /* default no. of clients */ ttj->exval = 1; /* no success yet */ ttj->cmd = 0; /* run normally */ ttj->sender_or_recipient = 0x00; /* not yet known */ ttj->start_now = 0x00; /* set from command line */ ttj->host_name = NULL; /* command line, recipients only */ ttj->host_port = (unsigned short)0; /* command line */ ttj->file_name = NULL; /* file name to send/receive */ ttj->ll = TCPTOOL_LL_PROGRESS; /* log level */ #if ON_WINDOWS_SYSTEM ttj->wait_timeout = 10; #endif } /** Process command line arguments. @param ttj Tcptool job. @param argc Number of command line arguments. @param argv Command line arguments array. @return 1 on success, 0 on error. */ static int process_arguments DK_P3(TTJ *,ttj, int,argc, char **,argv) { char buffer[256], *p1, *x; int back = 1; int i; char *optr, *ptr, **lfdptr; unsigned u; unsigned long ul, ulip, ulmask; i = 1; lfdptr = argv; lfdptr++; while(i < argc) { optr = ptr = *lfdptr; if(*ptr == '-') { ptr++; switch(*ptr) { case '-': { /* ##### Long option */ } break; case 's': { ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { if(sscanf(ptr, "%u", &u) == 1) { ttj->host_port = (unsigned short)u; ttj->sender_or_recipient |= TCPTOOL_RUN_AS_SENDER; } else { back = 0; /* ERROR: Illegal port number */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 23, 24, ptr); } } else { back = 0; /* ERROR: Missing port number */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 25); } } break; case 'r': { ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { if(strlen(ptr) < sizeof(buffer)) { strcpy(buffer, ptr); p1 = strchr(buffer, ':'); if(p1) { *(p1++) = '\0'; if(sscanf(p1, "%u", &u)) { ttj->host_port = (unsigned short)u; if(ttj->host_name) { x = ttj->host_name; ttj->host_name = NULL; free(x); /* Warning: host name redefined */ ttj_msg1(ttj, TCPTOOL_LL_WARNING, 26); } ttj->host_name = strdup(buffer); if(ttj->host_name) { ttj->sender_or_recipient |= TCPTOOL_RUN_AS_RECIPIENT; } else { back = 0; /* ERROR: Memory */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 27); } } else { back = 0; /* ERROR: Illegal port number */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 23, 24, p1); } } else { back = 0; /* ERROR: Illegal host:port */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 28, 29, buffer); } } else { back = 0; /* ERROR: host:port too long */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 30, 31, ptr); } } } break; case 'a': { ptr++; if(!(*ptr)) { i++; lfdptr++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { if(strlen(ptr) < sizeof(buffer)) { strcpy(buffer, ptr); p1 = strchr(buffer, '/'); if(p1) { *(p1++) = '\0'; ulip = dotted_string_to_ip(buffer); ulmask = dotted_string_to_ip(p1); if(!add_allowed_ip(ulip, ulmask)) { back = 0; /* ERROR: Too many access rules */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 32); } } else { back =0; /* ERROR: Invalid IP/mask */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 33, 34, buffer); } } else { back = 0; /* ERROR: Missing IP/mask */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 35); } } } break; case 'c': { ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { if(sscanf(ptr, "%lu", &ul) == 1) { if(ul > TCPTOOL_MAX_CLIENTS) { char xb[32]; ul = TCPTOOL_MAX_CLIENTS; sprintf(xb, "%lu", ul); /* Warning: Number of clients reduced to ... */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 36, 37, xb); } ttj->max_clients = ul; } else { if(strcmp(ptr, "unlimited")) { back = 0; /* ERROR: Illegal number of clients */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 38); } else { ttj->max_clients = 0UL; } } } } break; case 't': { ttj->start_now = 0x01; } break; case 'h': { ttj->cmd |= TCPTOOL_CMD_HELP; } break; case 'l': { ttj->cmd |= TCPTOOL_CMD_VERSION; } break; case 'd': { ptr++; if(!(*ptr)) { ptr = NULL; lfdptr++; i++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { switch(*ptr) { case 'n': ttj->ll = TCPTOOL_LL_NONE; break; case 'e': ttj->ll = TCPTOOL_LL_ERROR; break; case 'w': ttj->ll = TCPTOOL_LL_WARNING; break; case 'i': ttj->ll = TCPTOOL_LL_INFO; break; case 'p': ttj->ll = TCPTOOL_LL_PROGRESS; break; case 'd': ttj->ll = TCPTOOL_LL_DEBUG; break; } } } break; default: { back = 0; /* ERROR: Illegal option */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 39, 40, optr); } break; } } else { if(ttj->file_name) { back = 0; /* ERROR: Too many file names */ ttj_msg3(ttj, TCPTOOL_LL_ERROR, 41, 42, ptr); ttj_msg3(ttj, TCPTOOL_LL_ERROR, 44, 45, ttj->file_name); } else { ttj->file_name = ptr; } } lfdptr++; i++; } switch((ttj->sender_or_recipient) & TCPTOOL_RUN_MASK) { case TCPTOOL_RUN_MASK: { back = 0; /* ERROR: Choose sender or recipient */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 43); } break; case 0x00: { if(!(ttj->cmd)) { back = 0; /* ERROR: Choose sender or recipient */ ttj_msg1(ttj, TCPTOOL_LL_ERROR, 43); } } break; } return back; } /** Help text. */ static char *help_text[] = { "", "tcptool - Send data to multiple recipients", "==========================================", "", "Overview", "--------", "The tcptool program can be used to send data from one sender to multiple", "recipients using unicast TCP.", "", "Usage", "-----", "tcptool -s [-c ]", "\tstarts the sender. The sender will listen on the specified port number", "\tfor incoming connection requests.", "\tThe -c option can be used to specify the maximum number of clients. If", "\tthe last client has established the connection the server automatically", "\tstarts the data transmission. The default for the number of clients", "\tis 1. Specify 0 if the number of clients is not yet known when starting", "\tthe sender.", "", "tcptool -r [-t]", "\tstarts the recipient (client). The client attempts to connect to the", "\tserver on the specified host and port. If maxclients=0 was specified", "\twhen starting the sender use the -t option on the last client", "\tconnecting to start the data transmission.", "", NULL }; /** Version and license text. */ static char *version_text[] = { "tcptool - Data transfer tool using TCP, version " VERSNUMB, "Copyright (C) 2008-2010, Dirk Krause", "http://dktools.sourceforge.net/tcptool.html", "", "Redistribution and use in source and binary forms, with or without", "modification, are permitted provided that the following conditions are met:", "* Redistributions of source code must retain the above copyright notice, this", " list of conditions and the following disclaimer.", "* Redistributions in binary form must reproduce the above copyright notice,", " this list of conditions and the following disclaimer in the documentation", " and/or other materials provided with the distribution.", "* Neither the name of the Dirk Krause nor the names of other contributors may", " be used to endorse or promote products derived from this software without", " specific prior written permission.", "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"", "AND ANY EXPRESS 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 COPYRIGHT OWNER OR CONTRIBUTORS 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.", NULL }; /** Print a help text. */ static void print_text DK_P1(char **,t) { char **ptr; ptr = t; while(*ptr) { printf("%s\n", *(ptr++)); } } /** Convert allowed IPs and netmasks from host byte order to net byte order. */ static void correct_allowed_clients_and_masks DK_P0() { size_t i; for(i = 0; i < allowed_in_use; i++) { allowed_ip[i] = htonl(allowed_ip[i]); allowed_masks[i] = htonl(allowed_masks[i]); } } /** Run normally or print version/help. @param ttj Tcptool job. */ static void run DK_P1(TTJ *,ttj) { if(ttj->cmd) { if((ttj->cmd) & TCPTOOL_CMD_VERSION) { print_text(version_text); } if((ttj->cmd) & TCPTOOL_CMD_HELP) { print_text(help_text); } } else { if(TCP_START()) { correct_allowed_clients_and_masks(); switch((ttj->sender_or_recipient) & TCPTOOL_RUN_MASK) { case TCPTOOL_RUN_AS_RECIPIENT: { run_recipient(ttj); } break; case TCPTOOL_RUN_AS_SENDER: { if(ttj->max_clients != 1UL) { run_sender_multiple(ttj); } else { run_sender_one(ttj); } } break; } TCP_END(); } else { ttj_msg1(ttj, TCPTOOL_LL_ERROR, 50); } } } /** The main() function of the tcptool program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { TTJ ttj; ttj_init(&ttj); if(process_arguments(&ttj, argc, argv)) { run(&ttj); } else { print_text(help_text); } exit(ttj.exval); return ttj.exval; }