diff --git a/Makefile b/Makefile index 942c338..5ab1387 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ SRC = $(wildcard src/*.c) OBJ = $(subst .c,.o,$(subst src,work,$(SRC))) -LIBS = +LIBS = -pthread -pie $(shell pkg-config --libs gnutls) CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror -CFLAGS += -Isrc/include -pthread +CFLAGS += -Isrc/include -fpie $(shell pkg-config --cflags gnutls) INSTALLDIR := /usr/bin OUT = swebs diff --git a/documentation/sitefiles.md b/documentation/sitefiles.md index 672a193..2cb55d4 100644 --- a/documentation/sitefiles.md +++ b/documentation/sitefiles.md @@ -11,14 +11,23 @@ sitefiles also allow comments with # # Part 2: Commands -* ```set [variable] [value]``` - sets some variable for the following pages +* ```set [variable] [value]``` - sets some local variable for the following pages +* ```define [variable] [value]``` - sets some global variable * ```read [http path] [file path]``` - if the requested path matches ```[http path]```, return the contents of ```[file path]```. If [file path] is a directory, then the http path is appended to [file path] and that is read instead. ##### Other than set, commands should take in a regex as argument 1 and operate on a file specified in argument 2. -# Part 3: Variables +# Part 3: Local variables -* ```respondto```: - The type of http request to respond to. One of: +* ```respondto``` - The type of http request to respond to. One of: * GET (defualt) * POST -* ```host```: - The hostname to respond to. Case insensitive, default: localhost +* ```host``` - The hostname to respond to. Case insensitive, default: localhost + +# Part 4: Global variables + +* ```transport``` - the type of connection to use. One of: + * TCP (default) + * TLS +* ```key``` - The filepath of the private key to use if transport == TLS +* ```cert``` - The filepath of the certificate to use if transport == TLS diff --git a/example/domain.crt b/example/domain.crt new file mode 100644 index 0000000..e43180f --- /dev/null +++ b/example/domain.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnTCCAoUCFGPzRWSwuJ0gy97miUAkPSMNdH38MA0GCSqGSIb3DQEBCwUAMIGK +MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEzARBgNVBAcMCkNlZGFyIFBh +cmsxDjAMBgNVBAoMBXN3ZWJzMQ4wDAYDVQQLDAVzd2ViczESMBAGA1UEAwwJTmF0 +ZSBDaG9lMSIwIAYJKoZIhvcNAQkBFhNuYXRlY2hvZTlAZ21haWwuY29tMB4XDTIy +MDEyOTE3MjEzMFoXDTIyMDIyODE3MjEzMFowgYoxCzAJBgNVBAYTAlVTMQ4wDAYD +VQQIDAVUZXhhczETMBEGA1UEBwwKQ2VkYXIgUGFyazEOMAwGA1UECgwFc3dlYnMx +DjAMBgNVBAsMBXN3ZWJzMRIwEAYDVQQDDAlOYXRlIENob2UxIjAgBgkqhkiG9w0B +CQEWE25hdGVjaG9lOUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQC8l/c9+MDg32sxwx7xG8QIiUMNzsn7Ef9ZxKmOcj49crouYWhINaSB +oMHt3obEZPYHs9n7uYLavlMiT8mQFpSejD7nsyQYQoyPwgSAb9MbSA6aJt4x+RAo +54DnU4MpF1+pKoMOf5X5Kcs7UfR5I66YM2KXXKZUW96CIsAkmidfrpjPhdnhuafs +5A5FVXD5B6PYUr4z7Z0CxEIvzIJzmoBCzTBwZEk0/p7WXsGkv0w7PCiWAMghO7WQ +BdU5tQogbyQJckHS83puy13GjgJgM19v7R89g0GkHReNQ+2LlGkKZMZqDNVE3xuB +x1/6dXeTHjZYOvVColLb5o+NFLHG3wQ/AgMBAAEwDQYJKoZIhvcNAQELBQADggEB +AC/YmTpOOIkb26lzVE6Jbzi4a+0OTJXJBMT2RIgxSAzZ+ON8CZEzD1etCxTRrKVw +dCjs9x+BQRpux/lw+YGlSBqNd/jc5YIACRaBEEc1nc/uMNMni1d2yPSbQlqiyS9o +Q5aCz1urCQEXvuV4SCOWp84keyUzy1o/kCHSWj8Do67ah1rDf1g5MWc40gypU7FD +ANHMUsPkxMlQreDtQ88/xwxYsOsmxwllR9WaL51L3VCa86PQrm4cX19ZYqRE/P8B +WlKCBOkTMv2wX7Oa37/RVn+oWYWkrC5zwfVlToKSiM5vw5HztLbQoIVIBo5ye2cZ +FC9F8qS0EGkrajXA5+hN0zQ= +-----END CERTIFICATE----- diff --git a/example/domain.key b/example/domain.key new file mode 100644 index 0000000..b2e7858 --- /dev/null +++ b/example/domain.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAvJf3PfjA4N9rMcMe8RvECIlDDc7J+xH/WcSpjnI+PXK6LmFo +SDWkgaDB7d6GxGT2B7PZ+7mC2r5TIk/JkBaUnow+57MkGEKMj8IEgG/TG0gOmibe +MfkQKOeA51ODKRdfqSqDDn+V+SnLO1H0eSOumDNil1ymVFvegiLAJJonX66Yz4XZ +4bmn7OQORVVw+Qej2FK+M+2dAsRCL8yCc5qAQs0wcGRJNP6e1l7BpL9MOzwolgDI +ITu1kAXVObUKIG8kCXJB0vN6bstdxo4CYDNfb+0fPYNBpB0XjUPti5RpCmTGagzV +RN8bgcdf+nV3kx42WDr1QqJS2+aPjRSxxt8EPwIDAQABAoIBADaVZSGylWCASW4l +oxb1ik9OKvIcH6ljFBzPJqPrvMfcJLa3yo7bt7ks5fQKq8a70dl43GZW0uCspTMR +FK13W5xnyu0UlCXRXQ1egh8KPndu64sCJN0h0OOF3XEWYqfJ9A3qF/RiSO3Mgt2w +YqmNAkNa2/YjAqD31fakSChP1YWDa7nGYOdIawAA8lHaJ8t6mW6Ey5yuTYwmLewC +6DfxXjNWZnOIJVNJ8knySI4pedf8OgC+bcho09HrA06XhiT96WjKHhFV7hcqFhdQ +RcyaY7OghoOkzLsBqrlejYnFwVDOyzF9xlh2PKkT+3vi39QJLYXHEOCAkUKAcJOh +Z5y/goECgYEA5RWMwiG2e/Gt9sqXzRvDThz6lXgoP2mqkzxHSblFD+axlHR60yNw +8sfaB6fwbOy7gzQF6agD17MJneszyXuOYBS9R/4953E5Xd5bthZ60V5GawQfut7y +1OWcX22yP7TVN9YQ0Dw+bnI1kFeSNjjK2mfH4Y9r8PhwymD2OS1MZmcCgYEA0sCH +5xYn2erat3SGqRQxKF5nraBSBa0h+CBROvTw1a5tiRAfHIOUuRGWgg1og/F1EPaK +7DOYcaOPaxx52Lb2YZyOA4BwDGO1c4aBJ/GdIEn0hb2APeRj8ptvv6vSP3zcYOcA +VzlCx6GJwncudg67bCYoRV9oPcjgS92Y3ebkXGkCgYBlVn4Y1o8CDyw0kvRJ8HEa +Gemjuy6atqSVU0H50/JAEX4WvNwkkHYDf/LsFhdeJ+7fIGFJNmDUx0eGyyfyhiy6 +RhJhSY+a1VRaOOX7C8Cy43BlIMLkiIGFOjlNYZpLYjQ76f8wDqZTd0RIoOR2BfN+ +YBBksGxAgbZrYC8rpeU5GwKBgGQT+zU4JyprRPtTAVMu/Hzv/4nKlMiPQ49BQXGe +uPKvhuJMXom6zhfoCzGszlHBilbIzIWHpr9n7QXvGslXcL4/ioyNJCgt/Q9j8tcV +/AhGNjCAIXLCjte5CiLZo9h0IW5+o4HH9jc4NWO8FrsHGecsY3k16hlU13YPP7U4 +HH7pAoGABvK/Dlb3djIwfiMTOK1EjvjJAkrSb6P4AQZea08GksVGfhTdsfkTbnhr +n9WMf9p3IgAbmuHboJ89tPPTUVvDNrSSgWVtQ5rbREpXt05TAfT55STQFYOaSRG2 +FD0rK6CZrivPXgTpXWcy2zvfSseZZwEzVw23o4lwMCeVDI8+utg= +-----END RSA PRIVATE KEY----- diff --git a/example/run.sh b/example/run.sh index 2969e78..785c4bb 100755 --- a/example/run.sh +++ b/example/run.sh @@ -1,3 +1,3 @@ #!/bin/sh -swebs -s sitefile -o logs -p 8000 +../build/swebs -s sitefile -o logs -p 8000 diff --git a/example/sitefile b/example/sitefile index 8cbb562..3a17673 100644 --- a/example/sitefile +++ b/example/sitefile @@ -1,3 +1,7 @@ +define transport TLS +define key domain.key +define cert domain.crt + set host localhost:8000 read ^/$ site/index.html read ^/hello$ site/hello.html diff --git a/src/connections.c b/src/connections.c index 8319023..b444e2b 100644 --- a/src/connections.c +++ b/src/connections.c @@ -32,8 +32,8 @@ #include #include -int newConnection(int fd, Connection *ret) { - ret->fd = fd; +int newConnection(Stream *stream, Connection *ret) { + ret->stream = stream; ret->progress = RECEIVE_REQUEST; ret->currLineAlloc = 30; @@ -66,7 +66,7 @@ void resetConnection(Connection *conn) { } void freeConnection(Connection *conn) { - shutdown(conn->fd, SHUT_RDWR); + freeStream(conn->stream); free(conn->currLine); free(conn->path); free(conn->fields); @@ -198,7 +198,7 @@ static int processChar(Connection *conn, char c, Sitefile *site) { int updateConnection(Connection *conn, Sitefile *site) { char buff[300]; for (;;) { - ssize_t received = read(conn->fd, buff, sizeof(buff)); + ssize_t received = recvStream(conn->stream, buff, sizeof(buff)); if (received < 0) return errno != EAGAIN; if (received == 0) diff --git a/src/include/connections.h b/src/include/connections.h index afac43e..a84266e 100644 --- a/src/include/connections.h +++ b/src/include/connections.h @@ -18,6 +18,7 @@ #ifndef _HAVE_CONNECTIONS #define _HAVE_CONNECTIONS #include +#include #include typedef enum { @@ -33,7 +34,7 @@ typedef struct { } Field; typedef struct Connection { - int fd; + Stream *stream; ConnectionSteps progress; RequestType type; @@ -60,7 +61,7 @@ typedef struct Connection { //Ephemeral fields: Things which are freed and reallocated after each new //request, path, body -int newConnection(int fd, Connection *ret); +int newConnection(Stream *stream, Connection *ret); //returns non-zero on error. creates a new connection bound to fd void resetConnection(Connection *conn); void freeConnection(Connection *conn); diff --git a/src/include/sitefile.h b/src/include/sitefile.h index 4ed092b..4704b69 100644 --- a/src/include/sitefile.h +++ b/src/include/sitefile.h @@ -36,8 +36,11 @@ typedef struct { typedef struct { int size; SiteCommand *content; + SocketType type; + char *key; + char *cert; } Sitefile; -Sitefile *parseFile(char *path); +Sitefile *parseSitefile(char *path); void freeSitefile(Sitefile *site); #endif diff --git a/src/include/sockets.h b/src/include/sockets.h new file mode 100644 index 0000000..942ce05 --- /dev/null +++ b/src/include/sockets.h @@ -0,0 +1,56 @@ +/* + swebs - a simple web server + Copyright (C) 2022 Nate Choe + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef _HAVE_SOCKETS +#define _HAVE_SOCKETS +#include + +#include +#include + +#include + +typedef struct { + SocketType type; + int fd; + struct sockaddr_in addr; + socklen_t addrlen; + gnutls_certificate_credentials_t creds; + gnutls_priority_t priority; +} Listener; + +typedef struct { + SocketType type; + int fd; + gnutls_session_t session; +} Stream; + +int initTLS(); +Listener *createListener(SocketType type, uint16_t port, int backlog, ...); +//extra arguments depend on type (similar to fcntl): +//tcp: (void) +//tls: (char *keyfile, char *certfile, char *ocspfile) +Stream *acceptStream(Listener *listener, int flags); +//returns 1 on error, accepts fcntl flags + +void freeListener(Listener *listener); +void freeStream(Stream *stream); + +ssize_t sendStream(Stream *stream, void *data, size_t len); +ssize_t recvStream(Stream *stream, void *data, size_t len); +//return value is the same as the read and write syscalls. +#endif diff --git a/src/include/util.h b/src/include/util.h index 158fc82..9fba6bc 100644 --- a/src/include/util.h +++ b/src/include/util.h @@ -17,6 +17,11 @@ */ #ifndef _HAVE_UTIL #define _HAVE_UTIL +typedef enum { + TCP, + TLS, +} SocketType; + typedef enum { GET, POST, @@ -30,6 +35,8 @@ typedef enum { //INVALID in HTTP/1.1. } RequestType; +int initLogging(char *path); +int createLog(char *msg); int istrcmp(char *s1, char *s2); //case insensitive strcmp RequestType getType(char *str); diff --git a/src/main.c b/src/main.c index 064414e..c1af31c 100644 --- a/src/main.c +++ b/src/main.c @@ -20,25 +20,25 @@ #include #include #include +#include #include #include #include -#include +#include #include +#include #include -FILE *logs; - int main(int argc, char **argv) { char *logout = "/var/log/swebs.log"; char *sitefile = NULL; int processes = 8; - uint16_t port = htons(80); + uint16_t port = 443; int backlog = 100; for (;;) { - int c = getopt(argc, argv, "o:j:s:c:p:b:h"); + int c = getopt(argc, argv, "o:j:s:p:b:c:hl"); if (c == -1) break; switch (c) { @@ -52,11 +52,26 @@ int main(int argc, char **argv) { sitefile = optarg; break; case 'p': - port = htons(atoi(optarg)); + port = atoi(optarg); break; case 'b': backlog = atoi(optarg); break; + case 'l': + printf( +"swebs Copyright (C) 2022 Nate Choe\n" +"This is free software, and you are welcome to redistribute under certain\n" +"conditions, but comes with ABSOLUTELY NO WARRANTY. For more details see the\n" +"GNU General Public License Version 3\n" +"\n" +"This program dynamically links with:\n" +" gnutls (gnutls.org)\n" +"\n" +"For any complaints, email me at natechoe9@gmail.com\n" +"I'm a programmer not a lawyer, so there's a good chance I accidentally\n" +"violated the LGPL.\n" + ); + exit(EXIT_SUCCESS); case 'h': printf( "Usage: swebs [options]\n" @@ -65,39 +80,44 @@ int main(int argc, char **argv) { " -s [site file] Use that site file (required)\n" " -p [port] Set the port (default: 443)\n" " -b [backlog] Set the socket backlog (default: 100)\n" +" -l Show some legal details\n" +" -h Show this help message\n" ); exit(EXIT_SUCCESS); case '?': + fprintf(stderr, "-h for help\n"); exit(EXIT_FAILURE); } } - int fd = socket(AF_INET, SOCK_STREAM, 0); - assert(fd >= 0); - int opt = 1; - assert(setsockopt(fd, SOL_SOCKET, - SO_REUSEPORT, - &opt, sizeof(opt)) >= 0); - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = port; - socklen_t addrlen = sizeof(addr); - assert(bind(fd, (struct sockaddr *) &addr, addrlen) >= 0); - assert(listen(fd, backlog) >= 0); if (sitefile == NULL) { fprintf(stderr, "No sitefile configured\n"); exit(EXIT_FAILURE); } - Sitefile *site = parseFile(sitefile); + Sitefile *site = parseSitefile(sitefile); if (site == NULL) { fprintf(stderr, "Invalid sitefile %s\n", sitefile); exit(EXIT_FAILURE); } - logs = fopen(logout, "a"); - if (logs == NULL) { + Listener *listener; + switch (site->type) { + case TCP: default: + listener = createListener(TCP, port, backlog); + break; + case TLS: + initTLS(); + listener = createListener(TLS, port, backlog, + site->key, site->cert); + break; + } + if (listener == NULL) { + fprintf(stderr, "Failed to create socket\n"); + exit(EXIT_FAILURE); + } + + if (initLogging(logout)) { fprintf(stderr, "Couldn't open logs file %s\n", logout); exit(EXIT_FAILURE); } @@ -122,16 +142,15 @@ int main(int argc, char **argv) { (void*(*)(void*)) runServer, args); } + createLog("swebs started"); + for (;;) { - fsync(fd); - //TODO: Find out why this works - int newfd = accept(fd, (struct sockaddr *) &addr, - &addrlen); - if (newfd < 0) - exit(EXIT_FAILURE); - int flags = fcntl(newfd, F_GETFL); - if (fcntl(newfd, F_SETFL, flags | O_NONBLOCK)) - exit(EXIT_FAILURE); + Stream *stream = acceptStream(listener, O_NONBLOCK); + if (stream == NULL) { + createLog("Accepting a stream failed"); + continue; + } + int lowestThread = 0; int lowestCount = pending[0]; for (int i = 1; i < processes - 1; i++) { @@ -140,8 +159,8 @@ int main(int argc, char **argv) { lowestCount = pending[i]; } } - if (write(notify[lowestThread][1], &newfd, sizeof(newfd)) - < sizeof(newfd)) + if (write(notify[lowestThread][1], &stream, sizeof(&stream)) + < sizeof(&stream)) exit(EXIT_FAILURE); } } diff --git a/src/responses.c b/src/responses.c index 39528aa..16ccdb2 100644 --- a/src/responses.c +++ b/src/responses.c @@ -86,7 +86,6 @@ static void readResponse(Connection *conn, char *path) { if (sendBinaryResponse(conn, "200 OK", data, len) < len) goto error; free(data); - fsync(conn->fd); return; error: sendErrorResponse(conn, ERROR_500); diff --git a/src/responseutil.c b/src/responseutil.c index d3f7c6e..74ca9ae 100644 --- a/src/responseutil.c +++ b/src/responseutil.c @@ -21,7 +21,7 @@ #include #include #include -#include + #include #include @@ -40,7 +40,7 @@ static int sendConnection(Connection *conn, char *format, ...) { va_start(ap, format); vsprintf(data, format, ap); - if (write(conn->fd, data, len) < len) { + if (sendStream(conn->stream, data, len) < len) { free(data); return 1; } @@ -83,5 +83,5 @@ int sendBinaryResponse(Connection *conn, char *status, , status, len) ) return 1; - return write(conn->fd, data, len) < len; + return sendStream(conn->stream, data, len) < len; } diff --git a/src/runner.c b/src/runner.c index aff7d21..4e93863 100644 --- a/src/runner.c +++ b/src/runner.c @@ -23,9 +23,7 @@ #include #include -#include #include -#include #include #include @@ -74,13 +72,13 @@ void *runServer(RunnerArgs *args) { exit(EXIT_FAILURE); connections = newconns; } - int newfd; - if (read(notify, &newfd, sizeof(newfd)) < sizeof(newfd)) + Stream *stream; + if (read(notify, &stream, sizeof(stream)) < sizeof(stream)) exit(EXIT_FAILURE); - fds[connCount].fd = newfd; + fds[connCount].fd = stream->fd; fds[connCount].events = POLLIN; - if (newConnection(newfd, connections + connCount)) + if (newConnection(stream, connections + connCount)) exit(EXIT_FAILURE); connCount++; pending[id]++; diff --git a/src/sitefile.c b/src/sitefile.c index d6004f1..f66b95b 100644 --- a/src/sitefile.c +++ b/src/sitefile.c @@ -165,13 +165,16 @@ static char *copyString(char *str) { return ret; } -Sitefile *parseFile(char *path) { +Sitefile *parseSitefile(char *path) { FILE *file = fopen(path, "r"); if (file == NULL) return NULL; Sitefile *ret = malloc(sizeof(Sitefile)); if (ret == NULL) return NULL; + ret->type = TCP; + ret->key = NULL; + ret->cert = NULL; int allocatedLength = 50; ret->size = 0; ret->content = malloc(allocatedLength * sizeof(SiteCommand)); @@ -219,6 +222,25 @@ Sitefile *parseFile(char *path) { setValue: continue; } + else if (strcmp(argv[0], "define") == 0) { + if (argc < 3) + goto error; + if (strcmp(argv[1], "transport") == 0) { + if (strcmp(argv[2], "TCP") == 0) + ret->type = TCP; + else if (strcmp(argv[2], "TLS") == 0) + ret->type = TLS; + else + goto error; + } + else if (strcmp(argv[1], "key") == 0) + ret->key = copyString(argv[2]); + else if (strcmp(argv[1], "cert") == 0) + ret->cert = copyString(argv[2]); + else + goto error; + continue; + } if (ret->size >= allocatedLength) { allocatedLength *= 2; SiteCommand *newcontent = realloc(ret->content, diff --git a/src/sockets.c b/src/sockets.c new file mode 100644 index 0000000..1bad478 --- /dev/null +++ b/src/sockets.c @@ -0,0 +1,178 @@ +/* + swebs - a simple web server + Copyright (C) 2022 Nate Choe + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +int initTLS() { + assert(gnutls_global_init() >= 0); + return 0; +} + +Listener *createListener(SocketType type, uint16_t port, int backlog, ...) { + Listener *ret = malloc(sizeof(Listener)); + if (ret == NULL) + return NULL; + ret->type = type; + ret->fd = socket(AF_INET, SOCK_STREAM, 0); + if (ret->fd < 0) { + free(ret); + return NULL; + } + int opt = 1; + if (setsockopt(ret->fd, SOL_SOCKET, + SO_REUSEPORT, + &opt, sizeof(opt)) < 0) { + goto error; + } + ret->addr.sin_family = AF_INET; + ret->addr.sin_addr.s_addr = INADDR_ANY; + ret->addr.sin_port = htons(port); + ret->addrlen = sizeof(ret->addr); + if (bind(ret->fd, (struct sockaddr *) &ret->addr, ret->addrlen) < 0) + goto error; + if (listen(ret->fd, backlog) < 0) + goto error; + + va_list ap; + va_start(ap, backlog); + switch (type) { + case TCP: default: + break; + case TLS: { + char *keyfile = va_arg(ap, char *); + char *certfile = va_arg(ap, char *); + if (gnutls_certificate_allocate_credentials(&ret->creds) + < 0) + goto error; + if (gnutls_certificate_set_x509_key_file(ret->creds, + certfile, keyfile, + GNUTLS_X509_FMT_PEM) < 0) + goto error; + if (gnutls_priority_init(&ret->priority, + NULL, NULL) < 0) + goto error; +#if GNUTLS_VERSION_NUMBER >= 0x030506 + gnutls_certificate_set_known_dh_params(ret->creds, + GNUTLS_SEC_PARAM_MEDIUM); +#endif + break; + } + } + va_end(ap); + return ret; +error: + close(ret->fd); + free(ret); + return NULL; +} + +Stream *acceptStream(Listener *listener, int flags) { + Stream *ret = malloc(sizeof(Stream)); + if (ret == NULL) + return NULL; + ret->type = listener->type; + ret->fd = accept(listener->fd, (struct sockaddr *) &listener->addr, + &listener->addrlen); + + if (ret->fd < 0) { + free(ret); + return NULL; + } + + switch (listener->type) { + case TCP: default: + break; + case TLS: + if (gnutls_init(&ret->session, GNUTLS_SERVER) < 0) + goto error; + if (gnutls_priority_set(ret->session, + listener->priority) < 0) + goto error; + if (gnutls_credentials_set(ret->session, + GNUTLS_CRD_CERTIFICATE, + listener->creds) < 0) + goto error; + gnutls_certificate_server_set_request(ret->session, + GNUTLS_CERT_IGNORE); + gnutls_handshake_set_timeout(ret->session, + GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + gnutls_transport_set_int(ret->session, ret->fd); + if (gnutls_handshake(ret->session) < 0) + goto error; + break; + } + { + int flags = fcntl(ret->fd, F_GETFD); + fcntl(ret->fd, F_SETFD, flags | O_NONBLOCK); + } + return ret; +error: + close(ret->fd); + free(ret); + return NULL; +} + +void freeListener(Listener *listener) { + if (listener->type == TLS) { + gnutls_certificate_free_credentials(listener->creds); + gnutls_priority_deinit(listener->priority); + } + close(listener->fd); + free(listener); +} + +void freeStream(Stream *stream) { + if (stream->type == TLS) { + gnutls_bye(stream->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(stream->session); + } + close(stream->fd); + free(stream); +} + +ssize_t sendStream(Stream *stream, void *data, size_t len) { + switch (stream->type) { + case TCP: + return write(stream->fd, data, len); + case TLS: + return gnutls_record_send(stream->session, data, len); + default: + return -1; + } +} + +ssize_t recvStream(Stream *stream, void *data, size_t len) { + switch (stream->type) { + case TCP: + return read(stream->fd, data, len); + case TLS: + return gnutls_record_recv(stream->session, data, len); + default: + return -1; + } +} diff --git a/src/util.c b/src/util.c index ef515d0..85faa57 100644 --- a/src/util.c +++ b/src/util.c @@ -15,12 +15,39 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include +#include #include #include #include #include +static FILE *logs; + +int initLogging(char *path) { + logs = fopen(path, "a"); + return logs == NULL; +} + +int createLog(char *msg) { + time_t currenttime; + time(¤ttime); + struct tm *timeinfo = gmtime(¤ttime); + if (timeinfo == NULL) + return 1; + fprintf(logs, "[%d-%02d-%02dT%02d:%02d:%02dZ] %s\n", + timeinfo->tm_year + 1900, + timeinfo->tm_mon + 1, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec, + msg); + fflush(logs); + return 0; +} + int istrcmp(char *s1, char *s2) { for (int i = 0;; i++) { char c1 = tolower(s1[i]);