/* shared library frontend for qwixx * create the so or dll with: gcc -shared -o libfester.dll -fPIC fester.c .\http-server.o .\server.o -lws2_32 compile the example gcc .\example.c -L . -lfester -lws2_32 */ #include "server.h" #include "fester.h" #include "http-server/http-server.h" /* Handles the setup of a socket for the use by the server. The steps to * get and configure a socket on an Operating System are: * 1. request a socket file descriptor from the OS * 2. optionally set the reuse flag for this socket * 3. set the non-blocking flag for this socket * 4. request that the socket has a reserved port exclusive for our use * 5. set socket passive, incoming connections are queued by the OS * * The system calls for steps 1-5 are: socket, setsockopt, fcntl, bind, listen * * The setup process will step through each of these system calls and return * the file descriptor. Any error will result in the system error printed * to standard error and a zero returned to the caller, the program should * exit. To improve on this, just use the same system calls in a new function * that can handle the various errors. * * setup makes use of the getaddrinfo helper function which when using Msft * products only exists in their Winsock2 API. There is not that big of a * difference not using getaddrinfo, but it does make it easier for the user * to switch to either a IPV4 or IPV6 address scheme easier. * * [?] The pre-processor macro, MYPORT is used to set the listening port * * [!] If using Msft, you are required to run their proprietary code before * calling this routine. This is important enough to Msft that they make * it impossible to use sockets without doing so. Doing this aids Msft in * maintaining a monopoly by generating applications and code that only * work on Msft platforms. It is pretty easy, albeit annoying, to sidestep * this by keeping track of Msft's shenanigans (see WSAStartup). */ int setup() { int c, sock; struct addrinfo hints, *form; #ifndef _WIN32 /* install a signal handler so that when send attempts to write to a * broken pipe and the OS sends the SIGPIPE error we can keep running * instead of having our process killed off */ signal(SIGPIPE, handle_sigpipe); #endif // setup the helper structure for getaddrinfo() routine { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; } // fill out request form using a port, and previously defined hints if(0 != (c = getaddrinfo(NULL, MYPORT, &hints, &form))) { fprintf(stderr, "FATAL getaddrinfo: %s\n", gai_strerror(c)); return 0; } // request socket from operating system (OS) using completed form if(0 > (sock = socket(form->ai_family, form->ai_socktype, form->ai_protocol))) { fprintf(stderr, "FATAL socket: %s\n", strerror(errno)); return 0; } /* [!] Deal with a minor annoyance, OSes prevent applications from using * an address that was recently used and subsequently returned. This * feature is not desired - turn it off before we lock in the port portion * of the address (currently the socket only has IP, no Port). */ { optType okay = 1; if(0 > setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &okay, sizeof(okay))) { fprintf(stderr, "FATAL reuse: %s\n", strerror(errno)); return 0; } } // ask OS to set the non-blocking flag on the socket if(0 > setNonBlocking(sock)) { fprintf(stderr, "FATAL non-blocking flag request: %s\n", strerror(errno)); return 0; } // ask OS to associate our scoket with the port specified on the form if(0 > bind(sock, form->ai_addr /* [?] has port # */, form->ai_addrlen)) { fprintf(stderr, "FATAL bind: %s\n", strerror(errno)); return 0; } // discard the form, it is not used anymore freeaddrinfo(form); // notify OS that this socket is going to listen for incoming connections if(0 > listen(sock, BACKLOG)) { fprintf(stderr, "FATAL listen: %s\n", strerror(errno)); return 0; } // give the caller the server socket file descriptor return sock; } struct client * allocateBunnies(int sock) { struct client * pool = malloc (sizeof(struct client) *POOL_SIZE); if(NULL == pool) return NULL; // initialize client pool for(uint32_t i = 0; i < POOL_SIZE; i++) pool[i].fd = -1; // server listen port is first socket file descriptor memset(&pool[0], 0, sizeof(pool[0])); pool[0].fd = sock; return pool; } int somethingNeedDoing(uint16_t milliseconds, struct client pool[]) { #ifdef USE_CLIENT_POOL if(!faceItMetal(1000 /* ms */, httpEcho, pool)) return softCuteBunnies(-1); #else if(!faceItMetal(sock, 1000 /* ms */, httpEcho)) return softCuteBunnies(-1); #endif return softCuteBunnies(0); } #ifdef _WIN32 int dumbdumbdumbdumbdumb(WSADATA *w) { return !msftVendorLockInCode(w); } #endif