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