Added global variables in sitefiles to allow for https support
This commit is contained in:
4
Makefile
4
Makefile
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
22
example/domain.crt
Normal 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
27
example/domain.key
Normal 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-----
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
swebs -s sitefile -o logs -p 8000
|
../build/swebs -s sitefile -o logs -p 8000
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
56
src/include/sockets.h
Normal 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
|
||||||
@@ -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);
|
||||||
|
|||||||
85
src/main.c
85
src/main.c
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/runner.c
10
src/runner.c
@@ -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]++;
|
||||||
|
|||||||
@@ -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
178
src/sockets.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/util.c
27
src/util.c
@@ -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(¤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) {
|
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]);
|
||||||
|
|||||||
Reference in New Issue
Block a user