Added global variables in sitefiles to allow for https support

This commit is contained in:
Nate Choe
2022-01-29 13:21:35 -06:00
parent db645b0bf5
commit c46ed581c3
18 changed files with 430 additions and 58 deletions

View File

@@ -1,8 +1,8 @@
SRC = $(wildcard src/*.c) SRC = $(wildcard src/*.c)
OBJ = $(subst .c,.o,$(subst src,work,$(SRC))) OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
LIBS = LIBS = -pthread -pie $(shell pkg-config --libs gnutls)
CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror
CFLAGS += -Isrc/include -pthread CFLAGS += -Isrc/include -fpie $(shell pkg-config --cflags gnutls)
INSTALLDIR := /usr/bin INSTALLDIR := /usr/bin
OUT = swebs OUT = swebs

View File

@@ -11,14 +11,23 @@ sitefiles also allow comments with #
# Part 2: Commands # 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. * ```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. ##### 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) * GET (defualt)
* POST * 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

22
example/domain.crt Normal file
View File

@@ -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-----

27
example/domain.key Normal file
View File

@@ -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-----

View File

@@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
swebs -s sitefile -o logs -p 8000 ../build/swebs -s sitefile -o logs -p 8000

View File

@@ -1,3 +1,7 @@
define transport TLS
define key domain.key
define cert domain.crt
set host localhost:8000 set host localhost:8000
read ^/$ site/index.html read ^/$ site/index.html
read ^/hello$ site/hello.html read ^/hello$ site/hello.html

View File

@@ -32,8 +32,8 @@
#include <responses.h> #include <responses.h>
#include <connections.h> #include <connections.h>
int newConnection(int fd, Connection *ret) { int newConnection(Stream *stream, Connection *ret) {
ret->fd = fd; ret->stream = stream;
ret->progress = RECEIVE_REQUEST; ret->progress = RECEIVE_REQUEST;
ret->currLineAlloc = 30; ret->currLineAlloc = 30;
@@ -66,7 +66,7 @@ void resetConnection(Connection *conn) {
} }
void freeConnection(Connection *conn) { void freeConnection(Connection *conn) {
shutdown(conn->fd, SHUT_RDWR); freeStream(conn->stream);
free(conn->currLine); free(conn->currLine);
free(conn->path); free(conn->path);
free(conn->fields); free(conn->fields);
@@ -198,7 +198,7 @@ static int processChar(Connection *conn, char c, Sitefile *site) {
int updateConnection(Connection *conn, Sitefile *site) { int updateConnection(Connection *conn, Sitefile *site) {
char buff[300]; char buff[300];
for (;;) { for (;;) {
ssize_t received = read(conn->fd, buff, sizeof(buff)); ssize_t received = recvStream(conn->stream, buff, sizeof(buff));
if (received < 0) if (received < 0)
return errno != EAGAIN; return errno != EAGAIN;
if (received == 0) if (received == 0)

View File

@@ -18,6 +18,7 @@
#ifndef _HAVE_CONNECTIONS #ifndef _HAVE_CONNECTIONS
#define _HAVE_CONNECTIONS #define _HAVE_CONNECTIONS
#include <runner.h> #include <runner.h>
#include <sockets.h>
#include <sitefile.h> #include <sitefile.h>
typedef enum { typedef enum {
@@ -33,7 +34,7 @@ typedef struct {
} Field; } Field;
typedef struct Connection { typedef struct Connection {
int fd; Stream *stream;
ConnectionSteps progress; ConnectionSteps progress;
RequestType type; RequestType type;
@@ -60,7 +61,7 @@ typedef struct Connection {
//Ephemeral fields: Things which are freed and reallocated after each new //Ephemeral fields: Things which are freed and reallocated after each new
//request, path, body //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 //returns non-zero on error. creates a new connection bound to fd
void resetConnection(Connection *conn); void resetConnection(Connection *conn);
void freeConnection(Connection *conn); void freeConnection(Connection *conn);

View File

@@ -36,8 +36,11 @@ typedef struct {
typedef struct { typedef struct {
int size; int size;
SiteCommand *content; SiteCommand *content;
SocketType type;
char *key;
char *cert;
} Sitefile; } Sitefile;
Sitefile *parseFile(char *path); Sitefile *parseSitefile(char *path);
void freeSitefile(Sitefile *site); void freeSitefile(Sitefile *site);
#endif #endif

56
src/include/sockets.h Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef _HAVE_SOCKETS
#define _HAVE_SOCKETS
#include <stddef.h>
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include <util.h>
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

View File

@@ -17,6 +17,11 @@
*/ */
#ifndef _HAVE_UTIL #ifndef _HAVE_UTIL
#define _HAVE_UTIL #define _HAVE_UTIL
typedef enum {
TCP,
TLS,
} SocketType;
typedef enum { typedef enum {
GET, GET,
POST, POST,
@@ -30,6 +35,8 @@ typedef enum {
//INVALID in HTTP/1.1. //INVALID in HTTP/1.1.
} RequestType; } RequestType;
int initLogging(char *path);
int createLog(char *msg);
int istrcmp(char *s1, char *s2); int istrcmp(char *s1, char *s2);
//case insensitive strcmp //case insensitive strcmp
RequestType getType(char *str); RequestType getType(char *str);

View File

@@ -20,25 +20,25 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <netinet/in.h>
#include <util.h>
#include <runner.h> #include <runner.h>
#include <sockets.h>
#include <sitefile.h> #include <sitefile.h>
FILE *logs;
int main(int argc, char **argv) { int main(int argc, char **argv) {
char *logout = "/var/log/swebs.log"; char *logout = "/var/log/swebs.log";
char *sitefile = NULL; char *sitefile = NULL;
int processes = 8; int processes = 8;
uint16_t port = htons(80); uint16_t port = 443;
int backlog = 100; int backlog = 100;
for (;;) { 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) if (c == -1)
break; break;
switch (c) { switch (c) {
@@ -52,11 +52,26 @@ int main(int argc, char **argv) {
sitefile = optarg; sitefile = optarg;
break; break;
case 'p': case 'p':
port = htons(atoi(optarg)); port = atoi(optarg);
break; break;
case 'b': case 'b':
backlog = atoi(optarg); backlog = atoi(optarg);
break; 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': case 'h':
printf( printf(
"Usage: swebs [options]\n" "Usage: swebs [options]\n"
@@ -65,39 +80,44 @@ int main(int argc, char **argv) {
" -s [site file] Use that site file (required)\n" " -s [site file] Use that site file (required)\n"
" -p [port] Set the port (default: 443)\n" " -p [port] Set the port (default: 443)\n"
" -b [backlog] Set the socket backlog (default: 100)\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); exit(EXIT_SUCCESS);
case '?': case '?':
fprintf(stderr, "-h for help\n");
exit(EXIT_FAILURE); 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) { if (sitefile == NULL) {
fprintf(stderr, "No sitefile configured\n"); fprintf(stderr, "No sitefile configured\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
Sitefile *site = parseFile(sitefile); Sitefile *site = parseSitefile(sitefile);
if (site == NULL) { if (site == NULL) {
fprintf(stderr, "Invalid sitefile %s\n", sitefile); fprintf(stderr, "Invalid sitefile %s\n", sitefile);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
logs = fopen(logout, "a"); Listener *listener;
if (logs == NULL) { 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); fprintf(stderr, "Couldn't open logs file %s\n", logout);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -122,16 +142,15 @@ int main(int argc, char **argv) {
(void*(*)(void*)) runServer, args); (void*(*)(void*)) runServer, args);
} }
createLog("swebs started");
for (;;) { for (;;) {
fsync(fd); Stream *stream = acceptStream(listener, O_NONBLOCK);
//TODO: Find out why this works if (stream == NULL) {
int newfd = accept(fd, (struct sockaddr *) &addr, createLog("Accepting a stream failed");
&addrlen); continue;
if (newfd < 0) }
exit(EXIT_FAILURE);
int flags = fcntl(newfd, F_GETFL);
if (fcntl(newfd, F_SETFL, flags | O_NONBLOCK))
exit(EXIT_FAILURE);
int lowestThread = 0; int lowestThread = 0;
int lowestCount = pending[0]; int lowestCount = pending[0];
for (int i = 1; i < processes - 1; i++) { for (int i = 1; i < processes - 1; i++) {
@@ -140,8 +159,8 @@ int main(int argc, char **argv) {
lowestCount = pending[i]; lowestCount = pending[i];
} }
} }
if (write(notify[lowestThread][1], &newfd, sizeof(newfd)) if (write(notify[lowestThread][1], &stream, sizeof(&stream))
< sizeof(newfd)) < sizeof(&stream))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }

View File

@@ -86,7 +86,6 @@ static void readResponse(Connection *conn, char *path) {
if (sendBinaryResponse(conn, "200 OK", data, len) < len) if (sendBinaryResponse(conn, "200 OK", data, len) < len)
goto error; goto error;
free(data); free(data);
fsync(conn->fd);
return; return;
error: error:
sendErrorResponse(conn, ERROR_500); sendErrorResponse(conn, ERROR_500);

View File

@@ -21,7 +21,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <responseutil.h> #include <responseutil.h>
@@ -40,7 +40,7 @@ static int sendConnection(Connection *conn, char *format, ...) {
va_start(ap, format); va_start(ap, format);
vsprintf(data, format, ap); vsprintf(data, format, ap);
if (write(conn->fd, data, len) < len) { if (sendStream(conn->stream, data, len) < len) {
free(data); free(data);
return 1; return 1;
} }
@@ -83,5 +83,5 @@ int sendBinaryResponse(Connection *conn, char *status,
, status, len) , status, len)
) )
return 1; return 1;
return write(conn->fd, data, len) < len; return sendStream(conn->stream, data, len) < len;
} }

View File

@@ -23,9 +23,7 @@
#include <assert.h> #include <assert.h>
#include <poll.h> #include <poll.h>
#include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h>
#include <runner.h> #include <runner.h>
#include <sitefile.h> #include <sitefile.h>
@@ -74,13 +72,13 @@ void *runServer(RunnerArgs *args) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
connections = newconns; connections = newconns;
} }
int newfd; Stream *stream;
if (read(notify, &newfd, sizeof(newfd)) < sizeof(newfd)) if (read(notify, &stream, sizeof(stream)) < sizeof(stream))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
fds[connCount].fd = newfd; fds[connCount].fd = stream->fd;
fds[connCount].events = POLLIN; fds[connCount].events = POLLIN;
if (newConnection(newfd, connections + connCount)) if (newConnection(stream, connections + connCount))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
connCount++; connCount++;
pending[id]++; pending[id]++;

View File

@@ -165,13 +165,16 @@ static char *copyString(char *str) {
return ret; return ret;
} }
Sitefile *parseFile(char *path) { Sitefile *parseSitefile(char *path) {
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
if (file == NULL) if (file == NULL)
return NULL; return NULL;
Sitefile *ret = malloc(sizeof(Sitefile)); Sitefile *ret = malloc(sizeof(Sitefile));
if (ret == NULL) if (ret == NULL)
return NULL; return NULL;
ret->type = TCP;
ret->key = NULL;
ret->cert = NULL;
int allocatedLength = 50; int allocatedLength = 50;
ret->size = 0; ret->size = 0;
ret->content = malloc(allocatedLength * sizeof(SiteCommand)); ret->content = malloc(allocatedLength * sizeof(SiteCommand));
@@ -219,6 +222,25 @@ Sitefile *parseFile(char *path) {
setValue: setValue:
continue; 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) { if (ret->size >= allocatedLength) {
allocatedLength *= 2; allocatedLength *= 2;
SiteCommand *newcontent = realloc(ret->content, SiteCommand *newcontent = realloc(ret->content,

178
src/sockets.c Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sockets.h>
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;
}
}

View File

@@ -15,12 +15,39 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <time.h>
#include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <util.h> #include <util.h>
static FILE *logs;
int initLogging(char *path) {
logs = fopen(path, "a");
return logs == NULL;
}
int createLog(char *msg) {
time_t currenttime;
time(&currenttime);
struct tm *timeinfo = gmtime(&currenttime);
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) { int istrcmp(char *s1, char *s2) {
for (int i = 0;; i++) { for (int i = 0;; i++) {
char c1 = tolower(s1[i]); char c1 = tolower(s1[i]);