/*
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.
*/

#ifndef TCPTOOL_H_INCLUDED
#define TCPTOOL_H_INCLUDED	1

#include <dk.h>
#include <dkconfig.h>

#ifndef TCPTOOL_MAX_CLIENTS
#define TCPTOOL_MAX_CLIENTS 256
#endif
#ifndef TCPTOOL_MAX_ALLOWED_IP
#define TCPTOOL_MAX_ALLOWED_IP TCPTOOL_MAX_CLIENTS
#endif

#include <stdio.h>

#if DK_HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#if DK_HAVE_WINDOWS_H
#include <windows.h>
#endif
#if DK_HAVE_WINBASE_H
#include <winbase.h>
#endif
#if DK_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if DK_HAVE_SIGNAL_H
#include <signal.h>
#endif
#if DK_HAVE_PROCESS_H
#include <process.h>
#endif
#if DK_HAVE_CTYPE_H
#include <ctype.h>
#endif
#if DK_HAVE_STRING_H
#include <string.h>
#endif
#if DK_HAVE_ERRNO_H
#include <errno.h>
#endif
#if DK_HAVE_IO_H
#include <io.h>
#endif
#if DK_HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if DK_HAVE_NETDB_H
#include <netdb.h>
#endif
#if DK_HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if DK_TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if DK_HAVE_TIME_H
#include <time.h>
#else
#if DK_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#endif
#endif
#if DK_HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#include "dktools-version.h"


/**	Mode: Run as sender. */
#define TCPTOOL_RUN_AS_SENDER		0x02

/**	Mode: Run as recipient. */
#define TCPTOOL_RUN_AS_RECIPIENT	0x01

/**	Mask to find mode. */
#define TCPTOOL_RUN_MASK (TCPTOOL_RUN_AS_SENDER | TCPTOOL_RUN_AS_RECIPIENT)

/**	Command: Show help text. */
#define TCPTOOL_CMD_HELP		1

/**	Command: Show version. */
#define TCPTOOL_CMD_VERSION		2

#ifdef ON_WINDOWS_SYSTEM
#undef ON_WINDOWS_SYSTEM
#endif

#if defined(_WIN32) || defined(WIN32)
/**	Indicator: On a Windows system. */
#define ON_WINDOWS_SYSTEM	1
#else
#if defined(_WIN64) || defined(WIN64)
/**	Indicator: On a Windows system. */
#define ON_WINDOWS_SYSTEM	1
#endif
#endif


#if ON_WINDOWS_SYSTEM
/* +++++ Windows */

/**	Create socket. */
#define TCP_SOCKET(a,b,c)	socket(a, b, c)

/**	Check whether socket is OK. */
#define TCP_OK_SOCKET(s)	s != INVALID_SOCKET

/**	Close socket. */
#define TCP_CLOSE_SOCKET(s)	closesocket(s)

/**	Data type used for socket. */
#define TCP_TYPE_SOCKET		SOCKET

/**	Return type of read/write operations. */
#define TCP_TYPE_READ_WRITE	int

/**	Listen for incoming connection requests. */
#define TCP_LISTEN(a,b)		listen(a, b)

/**	Accept incoming connection request. */
#define TCP_ACCEPT(a,b,c)	accept(a, b, c)

/**	Connect socket. */
#define TCP_CONNECT(a,b,c)	connect(a, b, c)

/**	Send data over socket. */
#define TCP_SEND(a,b,c,d)	send(a, b, c, d)

/**	Receive data from socket. */
#define TCP_RECV(a,b,c,d)	recv(a, b, c, d)

/**	Shutdown socket for write operations. */
#define TCP_SHUTDOWN_WRITE(s)	shutdown(s, SD_SEND)

/**	Shutdown socket for read operations. */
#define TCP_SHUTDOWN_READ(s)	shutdown(s, SD_RECEIVE)

/**	Start TCP/IP networking. */
#define TCP_START()		tcpip_start()

/**	End TCP/IP networking. */
#define TCP_END()		tcpip_end()

/**	Variable to activate TCP options. */
#define TCP_VARIABLE_YES	int yes = 1

/**	Set address as reusable. */
#define TCP_REUSEADDR(s)	(void)setsockopt(\
s, SOL_SOCKET, SO_REUSEADDR, (const char *)(&yes), sizeof(yes)\
)

/**	Bind local socket to address. */
#define TCP_BIND(a,b,c)		bind(a, b, c)

/* ----- Windows */
#else
/* +++++ Unix/Linux */

/**	Create socket. */
#define TCP_SOCKET(a,b,c)	socket(a, b, c)

/**	Check whether socket is OK. */
#define TCP_OK_SOCKET(s)	s > -1

/**	Close socket. */
#define TCP_CLOSE_SOCKET(s)	close(s)

/**	Return type of read/write operations. */
#define TCP_TYPE_READ_WRITE	ssize_t

/**	Data type used for socket. */
#define TCP_TYPE_SOCKET		int

/**	Listen for incoming connection requests. */
#define TCP_LISTEN(a,b)		listen(a, b)

/**	Accept incoming connection request. */
#define TCP_ACCEPT(a,b,c)	accept(a, b, c)

/**	Connect socket. */
#define TCP_CONNECT(a,b,c)	connect(a, b, c)

/**	Send data over socket. */
#define TCP_SEND(a,b,c,d)	send(a, b, c, d)

/**	Receive data from socket. */
#define TCP_RECV(a,b,c,d)	recv(a, b, c, d)

/**	Shutdown socket for write operations. */
#define TCP_SHUTDOWN_WRITE(s)	shutdown(s, SHUT_WR)

/**	Shutdown socket for read operations. */
#define TCP_SHUTDOWN_READ(s)	shutdown(s, SHUT_RD)

/**	Start TCP/IP networking. */
#define TCP_START()		tcpip_start()

/**	End TCP/IP networking. */
#define TCP_END()		tcpip_end()

/**	Variable to activate TCP options. */
#define TCP_VARIABLE_YES	int yes = 1
#if DK_HAVE_SETSOCKOPT
#ifdef SO_REUSEADDR
/**	Set addrss as reusable. */
#define TCP_REUSEADDR(s)	(void)setsockopt(\
s, SOL_SOCKET, SO_REUSEADDR, (const char *)(&yes), sizeof(yes)\
)
#else
/**	Set address as reusable. */
#define TCP_REUSEADDR(s)	/* nix */
#endif
#else
/**	Set address as reusable. */
#define TCP_REUSEADDR(s)	/* nix */
#endif
/**	Bind local socket to address. */
#define TCP_BIND(a,b,c)		bind(a, b, c)

/* ----- Unix/Linux */
#endif


/**	TCPtool job.
 */
typedef struct {
  char			*host_name;	/**< Host name to connect. */
  char			*file_name;	/**< File name. */
  unsigned long		max_clients;	/**< Maximum number of clients. */
  int			exval;		/**< Exit status. */
  int			cmd;		/**< Command to run. */
  int			ll;		/**< Log level. */
  unsigned char		sender_or_recipient;	/**< Flag: Sender. */
  unsigned char		start_now;	/**< Flag: Can start. */
  unsigned short	host_port;	/**< Port number. */
#if ON_WINDOWS_SYSTEM
  unsigned long		wait_timeout;	/**< Timeout: Feedback from threads. */
#endif
} TTJ;


typedef struct sockaddr_in SOIN;
#define SZSOIN sizeof(SOIN)



/**	Error information.
 */
typedef struct {
	int		error_code;	/**< Error code. */
	time_t		error_time;	/**< Timestamp. */
	unsigned long	error_ip;	/**< Peer IP. */
	unsigned short	error_port;	/**< Peer port. */
} TCPTOOL_ERROR_INFO;


/**	TCPtool process.
 */
typedef struct {
#if ON_WINDOWS_SYSTEM
  TCPTOOL_ERROR_INFO		ei;		/**< Error information. */
  TTJ				*ttj;		/**< TCPtool job. */
  TCP_TYPE_SOCKET		socks;		/**< Socket. */
  unsigned long			ip;		/**< IP address of peer. */
  unsigned long			conn_no;	/**< Connection number. */
#ifdef _MSC_VER
#if _MSC_VER <= 1100
  unsigned long			thread_id;	/**< Thread ID. */
#else
  uintptr_t			thread_id;	/**< Thread ID. */
#endif
#else
  unsigned long			thread_id;	/**< Thread ID. */
#endif
  HANDLE			hEventSlave;	/**< Slave waits for event. */
  HANDLE			hEventMaster;	/**< Master waits for event. */
  unsigned char			did_timeout;	/**< Flag: Timeout occured. */
#else
  int	data_write_head;	/**< Data pipe write end (master). */
  int	data_read_head;		/**< Data pipe read end (slave). */
  int	ctrl_write_head;	/**< Control pipe write end (slave). */
  int	ctrl_read_head;		/**< Control pipe read end (master). */
  int	socks;			/**< Service socket. */
  pid_t	pid;			/**< Slave pid (0 in the slave itself. */
  unsigned long	ip;		/**< IP address of peer. */
  unsigned short port;		/**< IP port number of peer. */
  unsigned long	conn_no;	/**< Connection number. */
#endif
} TTPI;


/**	Error: No error occured, everything OK. */
#define TCPTOOL_ERROR_NONE		0

/**	Error: Peer disconnected. */
#define TCPTOOL_ERROR_DISCONNECTED	1

/**	Error: SIGPIPE occured (no reader for connection. */
#define TCPTOOL_ERROR_SIGPIPE		2

/**	Error: Failed to send. */
#define TCPTOOL_ERROR_SENDFAILED	3

/**	Error: Timeout in thread. */
#define TCPTOOL_ERROR_THREAD_TIMEOUT	4


/**	Log level: Don't print message. */
#define TCPTOOL_LL_NONE			0

/**	Log level: Error. */
#define TCPTOOL_LL_ERROR		1

/**	Log level: Warning. */
#define TCPTOOL_LL_WARNING		2

/**	Log level: Informational. */
#define TCPTOOL_LL_INFO			3

/**	Log level: Progress. */
#define TCPTOOL_LL_PROGRESS		4

/**	Log level: Debug. */
#define TCPTOOL_LL_DEBUG		5


#if DK_HAVE_SIGACTION
/**	Signal handler function type. */
typedef void SIGHANDLER(int);

/**	Refresh signal handler. */
#define SIGREFRESH(i,d) /* No need to do anything to refresh signal handler */
#else
#if DK_HAVE_SIGSET
/**	Signal handler function type. */
typedef void SIGHANDLER(int);

/**	Refresh signal handler. */
#define SIGREFRESH(i,d) /* No need to do anything to refresh signal handler */
#else
#if DK_HAVE_SIGNAL
/**	Signal handler function type. */
typedef void SIGHANDLER(int);

/**	Refresh signal handler. */
#define SIGREFRESH(i,d) signal(i,d);
#else
/**	Signal handler function type. */
typedef void SIGHANDLER(int);

/**	Refresh signal handler. */
#define SIGREFRESH(i,d) /* No need to do anything to refresh signal handler */
#endif
#endif
#endif



#endif

