/* 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 #include #include #include #include #include #ifdef _WIN32 // Msft Windows Ultra 365 Millennium XP Live Professional XBox #include /* 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 // 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 #include #include #include #include #include #include // 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