diff --git a/Makefile b/Makefile index 94f579a..461450d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=199506L +CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L LD = gcc LDFLAGS = -lpthread -lrt @@ -7,16 +7,21 @@ BIN_NAME = cfloor all: $(BIN_NAME) +$(BIN_NAME): obj/main.o obj/networking.o obj/linked.o obj/logging.o obj/signals.o + $(LD) $(LDFLAGS) -o $@ $^ + test: obj/test.o obj/networking.o obj/linked.o obj/logging.o obj/signals.o $(LD) $(LDFLAGS) -o $@ $^ + valgrind: CFLAGS += -static -g valgrind: clean test valgrind --leak-check=yes ./test -obj/test.o: src/networking.h src/linked.h src/logging.h src/signals.h -obj/networking.o: src/networking.h src/headers.h src/linked.h +obj/main.o: src/networking.h src/linked.h src/logging.h src/signals.h src/misc.h +obj/test.o: src/networking.h src/linked.h src/logging.h src/signals.h src/misc.h +obj/networking.o: src/networking.h src/headers.h src/linked.h src/logging.h src/signals.h obj/linked.o: src/linked.h -obj/loggin.o: src/logging.h +obj/logging.o: src/logging.h obj/signals.o: src/signals.h obj/%.o: src/%.c obj diff --git a/src/main.c b/src/main.c index 2d5df3a..434117a 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,40 @@ +#include +#include +#include +#include "networking.h" +#include "logging.h" +handler_t handlerGetter(struct metaData metaData, const char* host, struct bind* bind) { + return NULL; +} int main(int argc, char** argv) { + setLogging(stderr, INFO, true); + setCriticalHandler(NULL); + struct networkingConfig config = { + .maxConnections = 1024, + .connectionTimeout = 30000, + .binds = { + .number = 1, + .binds = (struct bind[]) { + { + .address = "0.0.0.0", + .port = "1337" + } + } + }, + .defaultResponse = { + .number = 0 + }, + .getHandler = &handlerGetter + }; + + initNetworking(config); + + while(true) { + sleep(0xffff); + } } diff --git a/src/networking.c b/src/networking.c index 49b1f34..83b0adc 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1,44 +1,219 @@ #include #include +#include +#include +#include +#include + +#include +#include +#include +#include #include "networking.h" +#include "linked.h" +#include "logging.h" +#include "signals.h" struct networkingConfig networkingConfig; -struct connection connections[MAX_CONNECTIONS]; +linkedList_t connectionList; -static inline struct connection emptyConnection() { - return (struct connection) { - .used = false, - .connectionState = CLOSED, - .metaData = { +void cleanup() { + signal_block_all(); + + link_t* link = linked_first(&connectionList); + + int length = 0; + int unlinked = 0; + + while(link != NULL) { + length++; + struct connection* connection = link->data; + switch(connection->state) { + case OPENED: + break; + default: + unlinked++; + linked_unlink(link); + } + + link = linked_next(link); + } + + info("cleanup: %d/%d unlinked", length, unlinked); +} + + +pthread_t dataThreadId; +void dataHandler(int signo) { + info("data handler got called."); +} +void* dataThread(void* ignore) { + signal_block_all(); + signal_allow(SIGIO); + + signal_setup(SIGIO, &dataHandler); + + while(true) { + sleep(0xffff); + } +} + +void updateTiming(struct connection* connection, bool stateChange) { + struct timespec time; + + // no need to check result; none of the errors can happen + clock_gettime(CLOCK_REALTIME, &time); + + connection->timing.lastUpdate = time; + if (stateChange) + connection->timing.states[connection->state] = time; +} + +void* listenThread(void* _bind) { + signal_block_all(); + + struct bind* bindObj = (struct bind*) _bind; + + info("networking: Starting to listen on %s:%s", bindObj->address, bindObj->port); + + struct addrinfo hints; + struct addrinfo* result; + struct addrinfo* rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + int tmp; + + tmp = getaddrinfo(bindObj->address, bindObj->port, &hints, &result); + if (tmp < 0) { + error("networking: networking: could't get addrinfo: %s", gai_strerror(tmp)); + warn("networking: Not listening on %s:%s", bindObj->address == NULL ? "0.0.0.0" : bindObj->address, bindObj->port); + return NULL; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + tmp = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (tmp == -1) + continue; + + bindObj->_private.socketFd = tmp; + + if (bind(tmp, rp->ai_addr, rp->ai_addrlen) == 0) + break; + + close(tmp); + } + + if (rp == NULL) { + error("networking: Could not bind."); + warn("networking: Not listening on %s:%s", bindObj->address == NULL ? "0.0.0.0" : bindObj->address, bindObj->port); + return NULL; + } + + if (listen(tmp, LISTEN_BACKLOG) < 0) { + error("networking: Could not listen."); + warn("networking: Not listening on %s:%s", bindObj->address == NULL ? "0.0.0.0" : bindObj->address, bindObj->port); + return NULL; + } + + freeaddrinfo(result); + + while(true) { + struct sockaddr_in client; + socklen_t clientSize = sizeof (struct sockaddr_in); + + tmp = accept(bindObj->_private.socketFd, (struct sockaddr *) &client, &clientSize); + if (tmp < 0) { + switch(errno) { + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: + case ENONET: + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + case EAGAIN: + + case ECONNABORTED: + case EINTR: + // retry + break; + + default: + error("networking: Could not accept connection: %s", strerror(errno)); + warn("networking: Not listening on %s:%s", bindObj->address == NULL ? "0.0.0.0" : bindObj->address, bindObj->port); + return NULL; + } + + continue; + } + + struct connection* connection = malloc(sizeof (struct connection)); + if (connection == NULL) { + error("networking: Couldn't allocate connection objekt: %s", strerror(errno)); + continue; + } + + connection->state = OPENED; + connection->client = client; + connection->bind = bindObj; + connection->fd = tmp; + connection->metaData = (struct metaData) { .path = NULL, .queryString = NULL - }, - .headers = { + }; + connection->headers = (struct headers) { .number = 0, .headers = NULL - }, - .currentHeaderLength = 0, - .fd = -1, - /* - * ignored: - * .metaData.method - * .metaData.httpVersion - * .currentHeader - * .timing.* - */ - }; + }; + connection->currentHeaderLength = 0; + connection->currentHeader = NULL; + connection->handler = NULL; + updateTiming(connection, false); + + linked_push(&connectionList, connection); + } } void initNetworking(struct networkingConfig _networkingConfig) { networkingConfig = _networkingConfig; - for (int i = 0; i < MAX_CONNECTIONS; i++) { - connections[i] = emptyConnection(); + connectionList = linked_create(); + + timer_t timer = timer_createThreadTimer(&cleanup); + if (timer == NULL) { + critical("networking: Couldn't create cleaup timer."); + return; + } + if (timer_startInterval(timer, CLEANUP_INTERVAl) < 0) { + critical("networking: Couldn't start cleaup timer."); + return; + } + + if (pthread_create(&dataThreadId, NULL, &dataThread, NULL) != 0) { + critical("networking: Couldn't start data thread."); + return; + } + + signal_block(SIGIO); + + for(int i = 0; i < networkingConfig.binds.number; i++) { + struct bind bind = networkingConfig.binds.binds[i]; + if (pthread_create(&(bind._private.threadId), NULL, &listenThread, &bind) != 0) { + critical("networking: Couldn't start data thread."); + return; + } } } - -void newConnection() { - -} diff --git a/src/networking.h b/src/networking.h index 11e792e..aac2128 100644 --- a/src/networking.h +++ b/src/networking.h @@ -3,44 +3,53 @@ #include #include +#include + +#include +#include #include "headers.h" #include "misc.h" -#define MAX_CONNECTIONS (512) - -#define NR_CONNECTION_STATE (6) +#define NR_CONNECTION_STATE (5) enum connectionState { - ESTABLISHED = 0, - HEADERS_COMPLETE = 1, - DATA_COMPLETE = 2, - PROCESSING = 3, - ABORTED = 4, - CLOSED = 5, - FREED = 6 + OPENED = 0, + PROCESSING = 1, + ABORTED = 2, + CLOSED = 3, + FREED = 4 }; struct timing { - struct timeval states[NR_CONNECTION_STATE]; - struct timeval lastUpdate; + struct timespec states[NR_CONNECTION_STATE]; + struct timespec lastUpdate; }; -struct connection { - bool used; - enum connectionState connectionState; - struct metaData metaData; - struct headers headers; - size_t currentHeaderLength; - char* currentHeader; - int fd; - struct timing timing; +struct bind_private { + pthread_t threadId; + int socketFd; }; -typedef handler_t (*handlerGetter_t)(struct metaData metaData, const char* host); - struct bind { const char* address; const char* port; + bool tls; + struct bind_private _private; +}; + +typedef handler_t (*handlerGetter_t)(struct metaData metaData, const char* host, struct bind* bind); + +struct connection { + enum connectionState state; + struct sockaddr_in client; + struct bind* bind; + int fd; + struct metaData metaData; + struct headers headers; + size_t currentHeaderLength; + char* currentHeader; + handler_t handler; + struct timing timing; }; struct binds { @@ -51,10 +60,15 @@ struct binds { struct networkingConfig { struct binds binds; long connectionTimeout; + long maxConnections; struct headers defaultResponse; handlerGetter_t getHandler; }; +#define CLEANUP_INTERVAl (1000) + +#define LISTEN_BACKLOG (1024) + void initNetworking(struct networkingConfig networkingConfig); #endif