/*  this server component is a TCP socket server that supports the connect, 
 *  request, respond, disconnect communication pattern.  It is suitable for
 *  things like a HTTP server or anything that follows this pattern.
 *
 *  It supports most Operating Systems, timeouts, and offers two methods to
 *  handle multiple clients. Non-pooled allows serial handling of clients where
 *  a single client is connected at a one time.  Pooling allows parallel
 *  handling of clients.
 */

#ifndef SOCKET_SERVER
    #define SOCKET_SERVER

    /*  normally this server is a component of some larger software so
     *  it is common for the faceItMetal routine to be called freqently,
     *  maybe a couple times a second or minute, whatever the user wants.
     *  if there are no clients during the time the server is active the
     *  multiplexer will time out and can print a message to standard error
     */
    //#define PRINT_MULTIPLEXER_TIMEOUT

    // client disconnect detected when recv returns 0 on an active connection
    #define PRINT_CLIENT_DISCONNECT
    //#define PRINT_CLIENT_ACCEPTED

    // print time remaining whenever it is checked
    //#define DEBUG_REMAINING

    // print whatever data is in the client buffer on every accept event
    //#define PRINT_CLIENT_DATA

    //#define PRINT_SOCKET_ADD_TO_SET
    //#define PRINT_NO_SOCKETS_READY
    //#define PRINT_HOW_MANY_SOCKETS_READY

    #include <stdio.h>
    #include <time.h>
    #include <stdint.h>
    #include <limits.h>
    #include <unistd.h>
    #include <signal.h>

    #ifdef _WIN32
        // Msft Windows Ultra 365 Millennium XP Live Professional XBox
        #include <winsock2.h>
        /*  ws2tcpip.h --
         *  What a terrible filename.  Like a badge of incompetence it proudly
         *  misdirects users while the socket agnostic getaddrinfo() hides
         *  unbeknownst the to those seeking non-TCP connections.   And as a
         *  matter of tradition of making things as worse as possible, users
         *  are required to define a magical constant if they want the routine
         *  to be exposed.  Because the Earth would literally stop spinning if
         *  someone on a non-sanctioned Windows version tried to get socket
         *  information on a connection they have already made.
         *
         *  [?] We could just use winsock.h and ignore ws2tcpip.h, but
         *      supposedly getaddrinfo is the future...
         */
        #if (defined _WIN32_WINNT) && (_WIN32_WINNT < 0x0501)
            /*  MinGW32 has this defined as Windows 2000 (0x0500) which
             *  according to Msft is too old for winsock2.h getaddrinfo.  So,
             *  redefine to at least Windows XP (0x0501)
             */
            #pragma message ( "[!] needed to override _WIN_WINNT" )
            #undef _WIN32_WINNT
        #endif
        #ifndef _WIN32_WINNT
            // Windows XP (0x0501) is, good enough for winsock2
            #define _WIN32_WINNT 0x0501
        #endif
        // okay, finally we abscond with getaddrinfo, thanks a lot Msft
        #include <ws2tcpip.h>
        // and smash Msft's dream of proprietary vendor lock-in shackles
        int msftVendorLockInCode(WSADATA *w);
        int close(int fd);
        char * strWinsock(int e);
        /*  Msft winsockets does not set errno like other system api calls
         *
         *  redefine errno integer variable to the function WSAGetLastError()
         *  in addition also redefine strerror to a custom error decoder
         *
         *  On Msft systems redefining errno to a function makes it impossible
         *  to set errno by other software, so this fix has limited
         *  applicability.  Said another way, watch out if using any other
         *  Msft API that might set errno, because this is not going to work.
         */
        #ifdef errno
        #pragma message ( "[!] needed to override errno and strerror" )
        #undef errno
        #undef strerror
        #endif
        #define errno WSAGetLastError()
        #define strerror strWinsock
        /*  Msft copied all the Unix error codes for the Berkeley sockets API,
         *  well, some of them, and then renamed them.  I guess the names that
         *  were not changed were too scary for Msft so they scampered off.
         *  For everything else, they prepended WSA to each error code, you
         *  know, because branding.  I am surprised that each pixel on my
         *  computer screen isn't made up of tiny corporate logos.  Thanks!
         */
        //#define EAGAIN [not supported]
        #define EWOULDBLOCK WSAEWOULDBLOCK
        #define ECONNABORTED WSAECONNABORTED
        #define EINTR WSAEINTR
        #define EMFILE WSAEMFILE
        //#define ENFILE [not supported]
        #define ENOBUFS WSAENOBUFS
        //#define ENOMEM [not supported]
        //#define EPROTO WSAEPROTO
        #define EOPNOTSUPP WSAEOPNOTSUPP
        #define EBADF WSAEBADF
        #define ENOTSOCK WSAENOTSOCK
        //#define EINVAL WSAEINVAL
        #define EFAULT WSAEFAULT
		// On Win10 Pro not needed, but Win10 Home for some reason
		#ifndef EINVAL
		#define EINVAL WSAEINVAL
		#endif
        typedef char optType;
        /*  Msft poll is slow and built around Vista (0x0600) shenanigans.  Use
         *  Select based sleep call rather than trying to figure out what super-
         *  rainbow-black-hole-summoning-ritual is needed to get poll working.
         */
        #define MULTIPLEX_USE_SELECT
        // they also rename the SHUT_RW as SD_SEND  Wheeeeeendoz!!!
        #define SHUT_WR SD_SEND
    #else
        // Every other operating system (OS) in existence
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netdb.h>
        #include <errno.h>
        #include <fcntl.h>
        #include <poll.h>
        #include <string.h> // memset
        typedef int optType;
    #endif

    // [!] OSes deny non-privileged user bind requests to ports <1024
    #define MYPORT "9000"
    // [!] OSes usually reduce this number to a maximum of 20
    #define BACKLOG 7

    /*  faceItMetal buffer constant
     *  this routine will send data out to the world.  Every routine should
     *  make sure nothing critical leaks, but this is just a safety to limit
     *  a potential leak to *only* some small amount of data
     *  other programs can use this to determine if they are reaching this limit
     *  and react accordingly
     */
    #define FIM_BUFFER_LEN 256

    /*  faceItMetal and acceptOkay communicate the EWOULDBLOCK and EAGAIN
     *  condition by sending a synonym for EWOULDBLOCK to hopefully remove some
     *  confusion.  At least for posix people who might vet the code.
     */
    #define DONE_FOR_NOW EWOULDBLOCK

    /*  user defined callback function can instruct the faceItMetal routine
     *  to perform some operations, currently implemented:
     */
    enum CALLBACK_ERROR_CODES {
        SEND_MORE = INT_MIN,        // wait for more data, call again later
        SERVER_ERROR,               // default, send "server error\n" and disconnect
        INVALID_DATA,               // send client "invalid\n" and disconnect
        DROP_CLIENT                 // immediately disconnect the client
    };
    /*  time a client has to send/recv data, 50ms is a good starting point
     *  the time is evenly split in two, the first half waits to recive data
     *  from the client and the remaining half is allocated waiting for the
     *  client to close the connection
     */
    #define PER_CLIENT_TIMEOUT_MS 50
    #define INVALID_TEXT "invalid\n"
    #define TIMEOUT_TEXT "timeout\n"
    #define SERVER_ERROR_TEXT "server error\n"

    enum SocketStatus {
        READ_READY          = (1 << 0),
        WRITE_CLOSED        = (1 << 1),
        ACCEPT_LISTEN_TIME  = (1 << 2)
    };

    enum ConnectionFlags {
        SERVER_WANTS_CLOSE  = (1 << 0),
        CLOSE_IMMEDIATELY   = (1 << 1),
        CLIENT_IS_CLOSED    = (1 << 2)
    };

    // convenience symbol for clients irrevocably on their way out
    #define CLIENT_CLOSING (CLOSE_IMMEDIATELY | CLIENT_IS_CLOSED)

    struct client {
        // socket file descriptor
        int32_t fd;
        // client internet protocol address (either version 4 or 6)
        struct sockaddr_storage address;
        socklen_t addrSize;
        // remaining milliseconds before automatic disconnect
        uint32_t ms;
        // time that the client was initially accepted
        struct timespec t;
        // stores information about the socket read and write 
        enum SocketStatus status;
        enum ConnectionFlags flags;
        // fill level for the buffer
        uint32_t count;
        // storage for client request
        char buf[FIM_BUFFER_LEN];
    };

    /*  USE_CLIENT_POOL option
     *  Instead of handling clients serially, a pool of clients is handled in the
     *  same fashion.  The slight difference may allow more throughput at the cost
     *  of a tiny bit more memory and a tiny bit slowdown due to the requirement to
     *  remember multiple client states and higher CPU usage.  
     *
     *  [!] The attack surface of the interface is increased as well so an
     *  adversarial client can now cause denial of service attacks by opening
     *  and closing a connection making this demo needing to recalculate client
     *  state.  In addition if state is not calculated well, eg: the fractional time
     *  resolution is not good enough, an adversary could either cause the client
     *  pool to be always full or always empty.  Yay!
     */
    #define USE_CLIENT_POOL
    

    #ifdef USE_CLIENT_POOL
        // how many clients are handled at once, hard limit is less than 65535
        #define POOL_SIZE 10
    #else
        #define POOL_SIZE 2
        int handleClient(struct client p[],
                char * (*f)(char *s, int len, int *limit));
    #endif

    #ifndef _WIN32
    /*  Sending/writing to a socket will eventually cause the Operating System
     *  to send the server application a SIGPIPE signal.  On Unix based operating
     *  systems there is a flag that can be set in the socket API to request
     *  that no signals be sent when attempting to write to a closed socket.
     *  (see: MSG_NOSIGNAL on Unix based OSes)
     *
     *  The alternative, and what was done here, is to just install a signal
     *  handler and deal with the signal that the OS will send.
     */
    void handle_sigpipe(int s);
    #endif

    /*  create a send reason so that we only need one use of send and the
     *  cruft around it handling the somewhat rare events when a client decides
     *  to break the pipe without sending a notification first.
     */
    typedef enum SendReason {
        TIME_OUT_MSG,
        INVALID_MSG,
        SERVER_ERROR_MSG,
        RESPONSE_MSG
    } sendReason;

    int sendResponse(struct client *c, char *buf, uint32_t len, sendReason reason);


    // determines if socket error is recoverable, and usually prints logs info
    int acceptOkay(int e);

    // provides OS agnostic method: set socket file descriptor non-blocking flag
    int setNonBlocking(int sockfd);

    // provides OS agnostic method: set socket file descriptor linger options
    static void setLinger(int sockfd) __attribute__((always_inline));

    // compares time now to specified time offset by some limit
    static uint32_t msRemaining(struct timespec t, uint32_t allowed)
        __attribute__((always_inline));

    // for debugging, the buffer is trashed after this call, so use carefully
    void convertToPrintable(char * s, uint32_t len);

    // inaccurate OS agnostic 1ms resolution sleep
    void spookySleep(uint32_t t);

    // refactor part of faceItMetal, sends a timeout message and sets linger
    static void sendTimeout(struct client *c) __attribute__((always_inline));

    // refactor part of faceItMetal, read/verify client messages, replies on success
    static int replyOnValid(struct client *c,
            char * (*f)(char *s, int len, int *limit)) __attribute__((always_inline));

    // refactor part of faceItMetal, non-pooled select/poll error debug
    static int wakeupError(int e, struct client * c) __attribute__((always_inline));
    
    // refactor part of faceItMetal, accepts clients and adds them to a pool
    static int waitForClients(uint32_t ms, struct client pool[]) 
        __attribute__((always_inline));

    #ifdef USE_CLIENT_POOL
    // refactor part of faceItMetal, scans pool for timed out clients
    static void checkTimeouts(struct client pool[], int len)
        __attribute__((always_inline));

    // refactor part of faceItMetal, low precision timeout tracking (uses milliseconds)
    static void prepayEstimates(struct client p[], uint32_t len, struct timespec t1)
        __attribute__((always_inline));

    // specialized connection routine, handles up to one client per call
    int faceItMetal(uint32_t ms, char * (*f)(char *s, int len,
            int *limit), struct client pool[]);
    #else
    int faceItMetal(int sock, uint32_t ms, char * (*f)(char *s, int len, int *limit));
    #endif

    // example prototype and definition of a faceItMetal (tm) callback
    char * alwaysGood(char *s, int len, int *limit);

    // example prototype and definition of a another faceItMetal (tm) callback
    char * alwaysBad(char *s, int len, int *limit);

    // example prototype and definition of a another faceItMetal (tm) callback
    char * alwaysNotEnough(char *s, int len, int *limit);
    
    // OS agnostic efficient blocking with timeout in milliseconds
    int socketReady(struct client pool[], int len, uint32_t ms);

    // a cute way to hide another bit of the Msft proprietary scheme
    int softCuteBunnies(int c);
    

#endif
