/*
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 <port> [-c <maxclients>]
	tcptool -r <host:port> [-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"




#line 59 "tcptool.ctr"




/**	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;
  
  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;
  
  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;
  
  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;
	    
	  }
	}
      }
    }
  }
  
  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
  
#line 515 "tcptool.ctr"

  
  if(ttj->host_name) {			
    sockfd = TCP_SOCKET(PF_INET,SOCK_STREAM,0);
    if(TCP_OK_SOCKET(sockfd)) {			
      ipaddr = dotted_string_to_ip(ttj->host_name);
      if(ipaddr) {			
        ipaddr = htonl(ipaddr);
      } else {
        ipaddr = lookup_host(ttj->host_name);
      }
      if(ipaddr) {			
        TCP_REUSEADDR(sockfd);
        soin.sin_family = AF_INET; 
        soin.sin_port = htons(ttj->host_port);
        soin.sin_addr.s_addr = ipaddr;
	res = TCP_CONNECT(sockfd,(struct sockaddr *)(&soin),SZSOIN);
	if(res == 0) {			
	  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) {		
	        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 {			
	    /* ERROR: Failed to send start byte */
	    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 5);
	  }
	} else {			
	  /* ERROR: Failed to connect */
	  ttj_msg1(ttj, TCPTOOL_LL_ERROR, 6);
	}
      } else {				
        /* ERROR: Host not found */
	ttj_msg1(ttj, TCPTOOL_LL_ERROR, 7);
      }
      TCP_CLOSE_SOCKET(sockfd);
    } else {				
      /* ERROR: Failed to create socket */
      ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8);
    }
  } else {				
    /* ERROR: No host name */
    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 9);
  }
  
  
#line 613 "tcptool.ctr"

}



/**	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;
  
#line 659 "tcptool.ctr"

  
  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) {				
    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;
	/* 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)) {		
              TCP_REUSEADDR(socks);
	        
	        if(szsoin == SZSOIN) {	
	          if(is_acceptable_client(soin.sin_addr.s_addr)) {
	            
		    /* 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) {		
		      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) {	
		        can_continue = 0;
		        sz = fread((void *)buffer, 1, sizeof(buffer), fipo);
		        if(sz > 0) {	
		          can_continue = 1;
		          sz2 = TCP_SEND(socks,(void *)buffer,sz,0);
		          if(sz2 != sz) { 
		            ttj->exval = 1;
			    /* ERROR: Problem while sending data */
			    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 22);
		          }
		        } else {
		          /* Finished */
		        } 
		      }
		      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 {		
		      /* ERROR: Failed to read initial byte */
		      finished = -1;
		      ttj_msg1(ttj, TCPTOOL_LL_ERROR, 12);
		    }
	          } else {		
	            /* 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 {		
		  finished = -1;
	          /* ERROR: Peer name has wrong size */
		  ttj_msg1(ttj, TCPTOOL_LL_ERROR, 14);
	        }
	      TCP_CLOSE_SOCKET(socks);
	    } else {			
	      finished = -1;
	      /* ERROR: Failed to accept client connection */
	      ttj_msg1(ttj, TCPTOOL_LL_ERROR, 16);
	    }
	  } else {			
	    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 {				
        /* ERROR: Failed to bind local socket */
	ttj_msg1(ttj, TCPTOOL_LL_ERROR, 18);
      }
      TCP_CLOSE_SOCKET(socka);
    } else {				
      /* 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 {				
    /* ERROR: Failed to open file */
    ttj_msg3(ttj, TCPTOOL_LL_ERROR, 19, 20, ttj->file_name);
  } 
  
#line 835 "tcptool.ctr"

}


#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 */
  
#line 940 "tcptool.ctr"

  
  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);
  }
  
  
#line 1153 "tcptool.ctr"

}

/* --- 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
  
  /* 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) {	
    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) {	
      can_continue = 1;
      if(!(ei.error_code)) {
        
        sz2 = send(ttpiptr->socks, (void *)buffer, sz, 0);
	
        if(sz2 != sz) {
	  
          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) {
	  
	  ei.error_code = TCPTOOL_ERROR_SIGPIPE;
	  time(&(ei.error_time));
	}
      } else {
      
      }
    }
  }
  if(ei.error_code) {
    
    /* send error information to master */
    (void)write(ttpiptr->ctrl_write_head, (void *)(&ei), sizeof(TCPTOOL_ERROR_INFO));
  } else {
    
    /* 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;
      }
    } 
    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
  
  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
  
#line 1389 "tcptool.ctr"

  
  num_connections = 0UL;
  socka = socket(PF_INET, SOCK_STREAM, 0);
  if(socka > -1) {					
#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) {	
      finished = 0; ttpiptr = ttpi; num_connections = 0UL;
      /* PROGRESS: start listening for connection requests */
      ttj_msg1(ttj, TCPTOOL_LL_PROGRESS, 58);
      while(!finished) {				
        if(listen(socka, 2) == 0) {			
	  szsoin = SZSOIN;
	  socks = accept(socka, (struct sockaddr *)(&soin), &szsoin);
	  if(socks > -1) {				
	      if(szsoin == SZSOIN) {
	        if(is_acceptable_client(soin.sin_addr.s_addr)) {
		  uc = 0x00;				
		  if(recv(socks, (void *)(&uc), 1, 0) == 1) {	
		    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) {	
		      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: {				
			    (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: {			
			    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;		
			      }
			    }
			    if(num_connections >= TCPTOOL_MAX_CLIENTS) {
			      finished = 1;		
			    }
			    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;   
			/* ERROR: pipe() failed */
			ttj_msg1(ttj, TCPTOOL_LL_ERROR, 11);
		      }
		    } else {			/* if(pipe() == 0) */
		      finished = 1; close(socks); socks = -1;	
		      /* ERROR: pipe() failed */
		      ttj_msg1(ttj, TCPTOOL_LL_ERROR, 11);
		    }				/* if(pipe() == 0) */
		  } else {			/* if(recv() == 1) */
		    finished = 1; close(socks); socks = -1;	
		    /* Failed to read initial byte */
		    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 12);
		  }				/* if(recv() == 1) */
		} else {			/* if(is_acceptable_client()) */
		  close(socks); socks = -1; 
		  /* 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;	
		/* ERROR: Wrong peer name size */
		ttj_msg1(ttj, TCPTOOL_LL_ERROR, 14);
	      }					/* if(szsoin == SZSOIN) */
	  } else {				/* if(socks > -1) */
	    finished = 1;			
	    /* ERROR: accept() failed */
	    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 16);
	  }					/* if(socks > -1) */
	} else {				/* if(listen() == 0) */
	  finished = 1;				
	  /* 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);
      
    } else {					/* bind() == 0 */
      
      /* ERROR: bind() failed */
      ttj_msg1(ttj, TCPTOOL_LL_ERROR, 18);
    }						/* bind() == 0 */
    close(socka);
    if(num_connections) {			
/* install SIGPIPE handler */
#if DK_HAVE_SIGACTION
#ifdef SIGPIPE
/* sigaction available */
      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
      /* close the slave side pipe ends */
      
      /* for(i = 0; i < num_connections; i++) { close(ttpi[i].fd_m2s[1]); } */
      
      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: Failed to read input */
	  ttj_msg3(ttj, TCPTOOL_LL_ERROR, 19, 20, ttj->file_name);
	}
      } else {					/* if(ttj->file_name) */
        fipo = 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);
	  
	  if(sz > 0) {
	    can_continue = 1;   
	    for(i = 0; i < num_connections; i++) {
	      
	      /* sz2 = write(ttpi[i].fd_m2s[0], (void *)buffer, sz); */
	      sz2 = write(ttpi[i].data_write_head, (void *)buffer, sz);
	      
	      if(sz2 != sz) {
	        /* ERROR: Problem while writing */
		ttj_msg1(ttj, TCPTOOL_LL_ERROR, 21);
		
	      }
	    }
	  }					/* 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 */
      
      /* 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) */
    
    /* ERROR: socket() */
    ttj_msg1(ttj, TCPTOOL_LL_ERROR, 8);
  }						/* if(socka > -1) */
  
  
#line 1657 "tcptool.ctr"

}



/* --- 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 <port> [-c <maxclients>]",
"\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 <host:port> [-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;
}


