--- /dev/null
+CC = cyg\r
+CFLAGS = -O0 -Wall -ggdb3\r
+LDFLAGS = -lssl -lcrypto\r
+\r
+SRCDIR = src\r
+OBJDIR = obj\r
+ifeq ($(OS),Windows_NT)\r
+ LIBDIR = C:/Users/quique/Documents/Programming/libs\r
+ RUNCMD = server.exe\r
+else\r
+ LIBDIR = /mnt/c/Users/quique/Documents/Programming/libs\r
+ RUNCMD = ./server.exe\r
+endif\r
+\r
+OBJS = $(addprefix $(OBJDIR)/, bit.o str.o log.o list.o crc64.o dir.o net.o ipc.o)\r
+\r
+INCL = -I$(LIBDIR) -I$(SRCDIR)\r
+\r
+\r
+\r
+all: server.exe worker.exe\r
+\r
+server.exe: $(SRCDIR)/main.c $(OBJS)\r
+ $(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o server.exe $(LDFLAGS)\r
+\r
+worker.exe: $(SRCDIR)/worker.c $(OBJS)\r
+ $(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o worker.exe $(LDFLAGS)\r
+\r
+$(OBJDIR)/%.o: $(SRCDIR)/%/*.c $(SRCDIR)/%/*.h\r
+ $(CC) -c $(SRCDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)\r
+\r
+$(OBJDIR)/%.o: $(LIBDIR)/%/*.c $(LIBDIR)/%/*.h\r
+ $(CC) -c $(LIBDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)\r
+\r
+clean:\r
+ rm -f worker.exe server.exe $(OBJDIR)/*.o\r
+\r
+memcheck:\r
+ sudo valgrind --show-leak-kinds=all --leak-check=full --track-origins=yes --trace-children=yes -s $(RUNCMD) 443\r
+ \r
--- /dev/null
+/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
+ *
+ * Specification of this CRC64 variant follows:
+ * Name: crc-64-jones
+ * Width: 64 bites
+ * Poly: 0xad93d23594c935a9
+ * Reflected In: True
+ * Xor_In: 0xffffffffffffffff
+ * Reflected_Out: True
+ * Xor_Out: 0x0
+ * Check("123456789"): 0xe9c6d914c4b8d9ca
+ *
+ * Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. */
+
+#include <stdint.h>
+
+static const uint64_t crc64_tab[256] = {
+ UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
+ UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
+ UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
+ UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
+ UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
+ UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
+ UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
+ UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
+ UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
+ UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
+ UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
+ UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
+ UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
+ UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
+ UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
+ UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
+ UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
+ UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
+ UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
+ UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
+ UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
+ UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
+ UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
+ UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
+ UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
+ UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
+ UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
+ UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
+ UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
+ UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
+ UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
+ UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
+ UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
+ UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
+ UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
+ UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
+ UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
+ UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
+ UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
+ UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
+ UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
+ UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
+ UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
+ UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
+ UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
+ UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
+ UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
+ UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
+ UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
+ UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
+ UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
+ UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
+ UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
+ UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
+ UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
+ UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
+ UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
+ UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
+ UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
+ UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
+ UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
+ UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
+ UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
+ UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
+ UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
+ UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
+ UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
+ UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
+ UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
+ UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
+ UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
+ UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
+ UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
+ UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
+ UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
+ UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
+ UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
+ UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
+ UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
+ UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
+ UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
+ UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
+ UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
+ UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
+ UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
+ UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
+ UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
+ UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
+ UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
+ UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
+ UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
+ UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
+ UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
+ UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
+ UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
+ UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
+ UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
+ UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
+ UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
+ UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
+ UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
+ UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
+ UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
+ UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
+ UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
+ UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
+ UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
+ UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
+ UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
+ UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
+ UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
+ UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
+ UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
+ UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
+ UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
+ UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
+ UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
+ UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
+ UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
+ UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
+ UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
+ UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
+ UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
+ UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
+ UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
+ UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
+ UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
+ UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
+};
+
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
+ uint64_t j;
+
+ for (j = 0; j < l; j++) {
+ uint8_t byte = s[j];
+ crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
+ }
+ return crc;
+}
+
+/* Test main */
+#ifdef TEST_MAIN
+#include <stdio.h>
+int main(void) {
+ printf("e9c6d914c4b8d9ca == %016llx\n",
+ (unsigned long long) crc64(0,(unsigned char*)"123456789",9));
+ return 0;
+}
+#endif
--- /dev/null
+#ifndef CRC64_H
+#define CRC64_H
+
+// Header file created because of project necessity
+// Source for c file: https://github.com/srned/baselib/blob/master/crc64.c
+
+#include <inttypes.h>
+
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
+
+#endif
--- /dev/null
+#include "dir.h"
+
+
+uint64_t get_fd_size(int fd){
+ struct stat st;
+ if(fstat(fd, &st) == 0){
+ return st.st_size;
+ }
+ return 0;
+}
+
+uint64_t get_fp_size(FILE *fp){
+ if(fp != NULL){
+ return get_fd_size(fileno(fp));
+ }
+ return 0;
+}
+
+uint64_t get_file_size(char *filename){
+ struct stat st;
+ if(stat(filename, &st) == 0){
+ return st.st_size;
+ }
+ return 0;
+}
+
+struct str get_file_format(struct str filename){
+ int i = 0;
+ while(filename.len-i > 0){
+ if(filename.ptr[filename.len-i-1] == '.') break;
+ i++;
+ }
+ if(i == 0 || i == filename.len){
+ return ((struct str){0});
+ }
+ struct str fmt;
+ fmt.len = i;
+ fmt.ptr = calloc(fmt.len+1, sizeof(char));
+ if(fmt.ptr == NULL) return ((struct str){0});
+ memcpy(fmt.ptr, filename.ptr+filename.len-i, fmt.len);
+ return fmt;
+}
+
+uint64_t getNEntries(const char *dir){
+ uint64_t r = 0;
+ DIR *d = opendir(dir);
+ if(d){
+ seekdir(d, (unsigned)-1);
+ r = telldir(d) - 2; // . and ..
+ closedir(d);
+ }
+ return r;
+}
+
+char **getFiles(const char *dir){
+ /*int i = 0, n = getNEntries(dir);
+ if(n > 0){
+ char **r = calloc(n, sizeof(char*));
+ DIR *d = opendir(dir);
+ readdir(d); readdir(d); // . and ..
+ struct dirent *t;
+ while((t = readdir(d)) != NULL){
+ int l = len(t->d_name);
+ r[i] = calloc(l+1, sizeof(char));
+ memmove(r[i++], t->d_name, l);
+ }
+ closedir(d);
+ return r;
+ }*/
+ return NULL;
+}
+
--- /dev/null
+#ifndef DIR_H
+#define DIR_H
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "str/str.h"
+
+
+struct file {
+ struct str name;
+ bool temp;
+};
+
+
+uint64_t get_fd_size(int fd);
+
+uint64_t get_fp_size(FILE *fp);
+
+uint64_t get_file_size(char *filename);
+
+struct str get_file_format(struct str filename);
+
+uint64_t getNEntries(const char *dir);
+
+char **getFiles(const char *dir);
+
+#endif
--- /dev/null
+#include "ipc.h"
+
+
+ipc_sender *setup_ipc_sender(struct str addr, int backlog){
+ ipc_sender *is = calloc(1, sizeof(ipc_sender));
+ is->addr = dup_str(addr);
+ is->ssocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(is->ssocket == -1){
+ log_error("%s: socket: %s", __FUNCTION__, strerror(errno));
+ goto error;
+ }
+ struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
+ memcpy(sockaddr.sun_path, is->addr.ptr, is->addr.len);
+ sockaddr.sun_path[is->addr.len] = '\0';
+ unlink(is->addr.ptr);
+ if(bind(is->ssocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == -1){
+ log_error("%s: bind: %s", __FUNCTION__, strerror(errno));
+ goto error;
+ }
+ if(listen(is->ssocket, backlog) == -1){
+ log_error("%s: listen: %s", __FUNCTION__, strerror(errno));
+ goto error;
+ }
+
+ if(0){
+error:
+ destroy_ipc_sender(&is);
+ }
+ return is;
+}
+
+void destroy_ipc_sender(ipc_sender **is){
+ if(*is != NULL){
+ close((*is)->ssocket);
+ (*is)->ssocket = -1;
+ unlink((*is)->addr.ptr);
+ free_str(&(*is)->addr);
+ free(*is);
+ *is = NULL;
+ }
+}
+
+ipc_listener *setup_ipc_listener(struct str saddr){
+ ipc_listener *il = calloc(1, sizeof(ipc_listener));
+ il->saddr = dup_str(saddr);
+ il->csocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(il->csocket == -1){
+ log_error("%s: socket: %s", __FUNCTION__, strerror(errno));
+ goto error;
+ }
+ struct sockaddr_un socksaddr = { .sun_family = AF_UNIX };
+ memcpy(socksaddr.sun_path, il->saddr.ptr, il->saddr.len);
+ socksaddr.sun_path[il->saddr.len] = '\0';
+ if(connect(il->csocket, (struct sockaddr *)&socksaddr, sizeof(socksaddr)) == -1){
+ log_error("%s: connect: %s", __FUNCTION__, strerror(errno));
+ goto error;
+ }
+
+ if(0){
+error:
+ destroy_ipc_listener(&il);
+ }
+ return il;
+}
+
+void destroy_ipc_listener(ipc_listener **il){
+ if(*il != NULL){
+ close((*il)->csocket);
+ (*il)->csocket = -1;
+ free_str(&(*il)->saddr);
+ free(*il);
+ *il = NULL;
+ }
+}
+
+void free_ipc_message(ipc_message im){
+ free_str(&im.key);
+ free_str(&im.val);
+}
+
+static inline struct str ipc_message_to_str(ipc_message msg){
+ struct str smsg = nstr(msg.key.len + msg.val.len + 2*sizeof(u32));
+ memcpy(smsg.ptr+smsg.len, &msg.key.len, sizeof(msg.key.len));
+ smsg.len += sizeof(msg.key.len);
+ copy_str(smsg, msg.key);
+ memcpy(smsg.ptr+smsg.len, &msg.val.len, sizeof(msg.val.len));
+ smsg.len += sizeof(msg.val.len);
+ copy_str(smsg, msg.val);
+ return smsg;
+}
+
+int send_ipc_message(int to, ipc_message msg){
+ struct str smsg = ipc_message_to_str(msg);
+ if(send(to, smsg.ptr, smsg.len, 0) == -1){
+ log_error("cant send message to socket %d: %s", to, strerror(errno));
+ free_str(&smsg);
+ return 1;
+ }
+ free_str(&smsg);
+ char buf[2];
+ if(recv(to, buf, 2, 0) == -1){
+ log_error("receiving OK from listener");
+ return 1;
+ }
+ if(strncmp(buf, "OK", 2) != 0){
+ log_error("received '%s' from listener instead of 'OK'", buf);
+ return 1;
+ }
+ return 0;
+}
+
+static inline ipc_message str_to_ipc_message(struct str smsg){
+ struct ipc_message msg;
+ u32 l;
+ memcpy(&l, smsg.ptr, sizeof(l));
+ smsg.ptr += sizeof(l);
+ msg.key = nstr(l);
+ msg.key.len = l;
+ memcpy(msg.key.ptr, smsg.ptr, l);
+ smsg.ptr += l;
+ memcpy(&l, smsg.ptr, sizeof(l));
+ smsg.ptr += sizeof(l);
+ msg.val = nstr(l);
+ msg.val.len = l;
+ memcpy(msg.val.ptr, smsg.ptr, l);
+ return msg;
+}
+
+ipc_message receive_ipc_message(ipc_listener *il){
+ struct str smsg = nstr(MAX_IPC_MSG_LEN); // we are gonna have to poll btw
+ ipc_message msg = {0};
+ smsg.len = recv(il->csocket, smsg.ptr, smsg.cap, 0);
+ if(smsg.len == -1){
+ log_error("cant receive message from socket %d: %s", il->csocket, strerror(errno));
+ }else{
+ msg = str_to_ipc_message(smsg);
+ }
+ if(send(il->csocket, "OK", slen("OK"), 0) == -1){
+ log_error("sending 'OK' to sender");
+ }
+ return msg;
+}
+
--- /dev/null
+#ifndef IPC_H
+#define IPC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "str/str.h"
+#include "log/log.h"
+#include "types/types.h"
+
+
+typedef struct ipc_sender {
+ struct str addr;
+ int ssocket;
+} ipc_sender;
+
+typedef struct ipc_listener {
+ struct str saddr;
+ int csocket;
+} ipc_listener;
+
+#define MAX_IPC_MSG_LEN 1024
+typedef struct ipc_message {
+ struct str key;
+ struct str val;
+} ipc_message;
+
+ipc_sender *setup_ipc_sender(struct str addr, int backlog);
+void destroy_ipc_sender(ipc_sender **is);
+
+ipc_listener *setup_ipc_listener(struct str saddr);
+void destroy_ipc_listener(ipc_listener **il);
+
+void free_ipc_message(ipc_message im);
+int send_ipc_message(int to, ipc_message msg);
+ipc_message receive_ipc_message(ipc_listener *il);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <time.h>
+#include "str/str.h"
+#include "list/list.h"
+#include "net/net.h"
+#include "log/log.h"
+#include "ipc/ipc.h"
+
+#define BACKLOG 15
+
+struct str ipcaddr = sstr(".ipcserver");
+struct str port;
+http_server *server;
+ipc_sender *sender;
+struct worker {
+ pid_t pid;
+ int wsocket;
+} *workers;
+
+
+void worker_undertaker(int sig, siginfo_t *info, void *ucontext){
+ if(sig == SIGCHLD && list_entry_exists(workers, info->si_pid)){
+ list_remove_entry(workers, info->si_pid);
+ log_warn("worker %d is dead. now workers size is %d", info->si_pid, list_size(workers));
+ }
+}
+
+int init(char *argv[]){
+ port = snstr(argv[1], len(argv[1])); // error check
+ if(port.len <= 0 || port.len > 5){
+ log_error("wrong server port: '%.*s'", port.len, port.ptr);
+ return 1;
+ }
+ // decouple so the whole net.c doesnt get linked?
+ server = setup_http_server(port, BACKLOG);
+ if(server == NULL){
+ log_error("Setting up socket server");
+ return 1;
+ }
+ // configurable name?
+ sender = setup_ipc_sender(ipcaddr, BACKLOG);
+ if(sender == NULL){
+ log_error("setting up ipc sender");
+ return 1;
+ }
+ init_list(workers);
+ struct sigaction chld = { .sa_sigaction = worker_undertaker, .sa_flags = SA_SIGINFO };
+ if(sigaction(SIGCHLD, &chld, NULL) == -1){
+ log_error("init: SIGCHLD: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+void deinit(void){
+ destroy_http_server(&server);
+ destroy_ipc_sender(&sender);
+ list_free(workers);
+}
+
+void print_usage(void){
+ printf("server [port]\n");
+}
+
+void show_commands(void){
+ printf(
+ "(case insensitive)\n"
+ "f: fork\n"
+ "s: signal\n"
+ "l: list\n"
+ "c: clear\n"
+ "[0-9]: turn off ssl for worker\n"
+ "h: help\n"
+ "q: quit\n"
+ );
+}
+
+int main(int argc, char *argv[]){
+
+ if(argc < 2){
+ print_usage();
+ return 1;
+ }
+
+ int return_value = 0;
+
+ if(init(argv) != 0){
+ return_value = 1;
+ goto DEINIT;
+ }
+ log_info("Config done");
+
+#ifdef SHOW_IP
+ system("curl -s http://ipinfo.io/ip && echo");
+#endif
+
+/*
+ sqlite3 *db = setupDatabase("src/db/db.db");
+ if(db == NULL){
+ fprintf(stderr, "error setting up database\n");
+ return 1;
+ }
+
+ if(getNEntries("archive") != getCount(db, FILE_TABLE)){
+ int n = getNEntries("archive");
+ char **entries = getFiles("archive"); // getFiles deprecated btw
+ for(int i = 0; i < n; ++i){
+ insertName(db, FILE_TABLE, entries[i]);
+ }
+ printf("%d, %d\n", getNEntries("archive"), getCount(db, FILE_TABLE));
+ }
+*/
+
+
+ printf("press h for help\n");
+ bool end = false;
+ while(!end){
+ char c = getchar();
+ switch(c){
+ case 'f': case 'F':
+ pid_t nw = fork();
+ if(nw == 0){
+ char *args[] = {"./worker.exe", ipcaddr.ptr, NULL};
+ execv("./worker.exe", args);
+ log_error("Cannot exec worker: %s", strerror(errno));
+ return 1;
+ }
+ struct worker w = { .pid = nw, .wsocket = accept(sender->ssocket, NULL, NULL) };
+ list_push(workers, w);
+ ipc_message msg = { .key = sstr("socket"), .val = utostr(server->ssocket, 10) };
+ send_ipc_message(w.wsocket, msg);
+ free_ipc_message(msg);
+ msg = (ipc_message){ .key = sstr("rewrites"), .val = sstr("urirewrites") };
+ send_ipc_message(w.wsocket, msg);
+ break;
+ case 's': case 'S':
+ kill(0, SIGUSR1);
+ break;
+ case 'l': case 'L':
+ printf("|-%3d workers working for us rn-|\n", list_size(workers));
+ char *faces[] = {
+ "(^__^)", "(·__·)", "(>__>)", "(~ _~)", "(T__T)", "(º__º)"
+ };
+ for(int i = 0; i < list_size(workers); i++){
+ int index = rand()%sizeof(faces)/sizeof(faces[0]);
+ printf("| %d %s\t\t\t|\n", workers[i].pid, faces[index]);
+ }
+ printf("|-------------------------------|\n");
+ break;
+ case 'c': case 'C':
+ system("clear");
+ break;
+ case 'h': case 'H':
+ show_commands();
+ break;
+ case 'q': case 'Q':
+ while(list_size(workers) > 0){
+ kill(workers[0].pid, SIGKILL); // redo this PLEASE
+ waitpid(workers[0].pid, NULL, 0);
+ }
+ while(wait(NULL) > 0);
+ close(server->ssocket);
+ end = true;
+ log_info("%d children remaining alive (lie)\n", list_size(workers));
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if(list_size(workers) > c-'0'){
+ log_info("signaling worker[%d] %d to turn off ssl\n", c-'0', workers[c-'0'].pid);
+ sigqueue(workers[c-'0'].pid, SIGRTMIN, (union sigval){.sival_int = 0});
+ }
+ break;
+ }
+ }
+
+DEINIT:
+ deinit();
+
+ return return_value;
+}
+
+// inspiration: https://www.youtube.com/watch?v=cEH_ipqHbUw (https://github.com/infraredCoding/cerveur)
+// thanks to
+/*
+ https://beej.us/guide/bgnet/
+ https://dev.to/lloyds-digital/how-you-can-host-websites-from-home-4pke
+ https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
+ https://www.favicon.cc
+ https://www.cssscript.com/demo/animated-snowfall-effect/
+ https://www.cssscript.com/demo/animated-snowfall-effect/PureSnow.js
+ https://www.tutorialspoint.com/http/http_requests.htm
+ https://codepen.io/afarrar/pen/JRaEjP
+*/
+
--- /dev/null
+#include "net.h"
+
+
+struct str response_headers[] = {
+ sstr("Content-Length"),
+ sstr("Content-Type"),
+ sstr("Transfer-Encoding"),
+};
+
+struct {
+ struct str fmt;
+ struct str type;
+} mime_types[] = {
+ {.fmt = sstr("avif"), .type = sstr("image/avif")},
+ {.fmt = sstr("bmp"), .type = sstr("image/bmp")},
+ {.fmt = sstr("css"), .type = sstr("text/css")},
+ {.fmt = sstr("csv"), .type = sstr("text/csv")},
+ {.fmt = sstr("gz"), .type = sstr("application/gzip")},
+ {.fmt = sstr("gif"), .type = sstr("image/gif")},
+ {.fmt = sstr("html"), .type = sstr("text/html")},
+ {.fmt = sstr("ico"), .type = sstr("image/vnd.microsoft.icon")},
+ {.fmt = sstr("jpg"), .type = sstr("image/jpeg")},
+ {.fmt = sstr("jpeg"), .type = sstr("image/jpeg")},
+ {.fmt = sstr("js"), .type = sstr("text/javascript")},
+ {.fmt = sstr("json"), .type = sstr("application/json")},
+ {.fmt = sstr("midi"), .type = sstr("audio/midi")},
+ {.fmt = sstr("mp3"), .type = sstr("audio/mpeg")},
+ {.fmt = sstr("mp4"), .type = sstr("video/mp4")},
+ {.fmt = sstr("mpeg"), .type = sstr("video/mpeg")},
+ {.fmt = sstr("png"), .type = sstr("image/png")},
+ {.fmt = sstr("pdf"), .type = sstr("application/pdf")},
+ {.fmt = sstr("php"), .type = sstr("application/x-httpd-php")},
+ {.fmt = sstr("rar"), .type = sstr("application/vnd.rar")},
+ {.fmt = sstr("tiff"), .type = sstr("image/tiff")},
+ {.fmt = sstr("ts"), .type = sstr("video/mp2t")},
+ {.fmt = sstr("txt"), .type = sstr("text/plain")},
+ {.fmt = sstr("wav"), .type = sstr("audio/wav")},
+ {.fmt = sstr("weba"), .type = sstr("audio/webm")},
+ {.fmt = sstr("webm"), .type = sstr("video/webm")},
+ {.fmt = sstr("webp"), .type = sstr("image/webp")},
+ {.fmt = sstr("xml"), .type = sstr("application/xml")},
+ {.fmt = sstr("zip"), .type = sstr("application/zip")},
+ {.fmt = sstr("7z"), .type = sstr("application/x-7z-compressed")},
+};
+
+LIST(struct uri_mod) uri_rewrites = NULL;
+
+static int pleasesslgivemetheerror(int ssl_get_error){
+ char *error;
+ switch(ssl_get_error){
+ case SSL_ERROR_NONE: error = "SSL_ERROR_NONE"; break;
+ case SSL_ERROR_ZERO_RETURN: error = "SSL_ERROR_ZERO_RETURN"; break;
+ case SSL_ERROR_WANT_READ: error = "SSL_ERROR_WANT_READ"; break;
+ case SSL_ERROR_WANT_WRITE: error = "SSL_ERROR_WANT_WRITE"; break;
+ case SSL_ERROR_WANT_CONNECT: error = "SSL_ERROR_WANT_CONNECT"; break;
+ case SSL_ERROR_WANT_ACCEPT: error = "SSL_ERROR_WANT_ACCEPT"; break;
+ case SSL_ERROR_WANT_X509_LOOKUP: error = "SSL_ERROR_WANT_X509_LOOKUP"; break;
+ case SSL_ERROR_WANT_ASYNC: error = "SSL_ERROR_WANT_ASYNC"; break;
+ case SSL_ERROR_WANT_ASYNC_JOB: error = "SSL_ERROR_WANT_ASYNC_JOB"; break;
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB: error = "SSL_ERROR_WANT_CLIENT_HELLO_CB"; break;
+ case SSL_ERROR_SYSCALL: error = "SSL_ERROR_SYSCALL"; break;
+ case SSL_ERROR_SSL: error = "SSL_ERROR_SSL"; break;
+ }
+ int err = ERR_get_error();
+ log_error("%s (%d): %s", error, err, ERR_error_string(err, NULL));
+ return ssl_get_error;
+}
+
+http_server *setup_http_server(struct str port, int backlog){
+ http_server *hs = calloc(1, sizeof(http_server));
+ hs->port = dup_str(port);
+ hs->backlog = backlog;
+ hs->ssocket = -1;
+
+ int ec, val = 1;
+ struct addrinfo *res, hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE };
+
+ if((ec = getaddrinfo(NULL, hs->port.ptr, &hints, &res)) != 0){
+ log_error("getaddrinfo: %s", gai_strerror(ec));
+ goto error;
+ }
+ if((hs->ssocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){
+ log_error("socket: %s", strerror(errno));
+ goto error;
+ }
+
+ if(setsockopt(hs->ssocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))){
+ log_error("server: SO_REUSEADDR: %s", strerror(errno));
+ goto error;
+ }
+
+ if(bind(hs->ssocket, res->ai_addr, res->ai_addrlen) == -1){
+ log_error("server: bind: %s", strerror(errno));
+ goto error;
+ }
+
+ if(listen(hs->ssocket, backlog) == -1){
+ log_error("server: listen: %s", strerror(errno));
+ goto error;
+ }
+
+ if(0){
+error:
+ destroy_http_server(&hs);
+ }
+ freeaddrinfo(res);
+
+ return hs;
+}
+
+void destroy_http_server(http_server **hs){
+ if(*hs != NULL){
+ free_str(&(*hs)->port);
+ (*hs)->backlog = 0;
+ close((*hs)->ssocket);
+ free(*hs);
+ *hs = NULL;
+ }
+}
+
+http_worker *setup_http_worker(int ssocket, int secure, struct str certfile, struct str keyfile){
+ http_worker *hw = calloc(1, sizeof(http_worker));
+ hw->ssocket = ssocket;
+ hw->csocket = -1;
+ hw->secure = secure;
+ hw->receive = recv;
+ hw->ssl_receive = SSL_read;
+
+ SSL_library_init();
+ hw->ssl_ctx = SSL_CTX_new(TLS_server_method());
+ // need to compile openssl with ktls on for this (v) to work
+ SSL_CTX_set_options(hw->ssl_ctx, SSL_OP_ENABLE_KTLS);
+ if(hw->ssl_ctx == NULL){
+ goto error;
+ }
+ if(SSL_CTX_use_certificate_file(hw->ssl_ctx, certfile.ptr, SSL_FILETYPE_PEM) <= 0){
+ log_error("worker_cert");
+ goto error;
+ }
+ if(SSL_CTX_use_PrivateKey_file(hw->ssl_ctx, keyfile.ptr, SSL_FILETYPE_PEM) <= 0 ){
+ log_error("worker: key");
+ goto error;
+ }
+ hw->ssl = SSL_new(hw->ssl_ctx);
+ SSL_set_accept_state(hw->ssl);
+ if(hw->ssl == NULL){
+ goto error;
+ }
+
+ if(0){
+error:
+ destroy_http_worker(&hw);
+ }
+
+ return hw;
+}
+
+void destroy_http_worker(http_worker **hw){
+ if(*hw != NULL){
+ (*hw)->ssocket = -1;
+ close((*hw)->csocket);
+ (*hw)->secure = 0;
+ SSL_free((*hw)->ssl);
+ SSL_CTX_free((*hw)->ssl_ctx);
+ (*hw)->receive = NULL;
+ (*hw)->ssl_receive = NULL;
+ free(*hw);
+ *hw = NULL;
+ }
+}
+
+void reset_worker_ssl(http_worker *hw){
+ close(hw->csocket);
+ SSL_free(hw->ssl);
+ hw->ssl = SSL_new(hw->ssl_ctx);
+ SSL_set_accept_state(hw->ssl);
+}
+
+int accept_connection(http_worker *hw, char ip[INET_ADDRSTRLEN]){
+ struct sockaddr_storage caddr;
+ int casize = sizeof(caddr);
+ log_info("waiting...");
+ if((hw->csocket = accept(hw->ssocket, (struct sockaddr *)&caddr, (socklen_t*)&casize)) == -1){
+ log_error("accept_socket() -> accept(): %s", strerror(errno));
+ return -1;
+ }
+ log_info("accepted");
+ if(hw->secure){
+ int err = SSL_set_fd(hw->ssl, hw->csocket);
+ if(err != 1){
+ log_error("setting fd %d", hw->csocket);
+ return pleasesslgivemetheerror(SSL_get_error(hw->ssl, err));
+ }
+ if((err = SSL_accept(hw->ssl)) != 1){
+ log_error("couldnt accept");
+ return pleasesslgivemetheerror(SSL_get_error(hw->ssl, err));
+ }
+ }
+ inet_ntop(caddr.ss_family, &(((struct sockaddr_in*)&caddr)->sin_addr), ip, INET_ADDRSTRLEN);
+ return 0;
+}
+
+static inline int worker_read(http_worker *hw, struct str *buf){
+ return hw->secure ?
+ SSL_read(hw->ssl, buf->ptr+buf->len, buf->cap-buf->len) :
+ recv(hw->csocket, buf->ptr+buf->len, buf->cap-buf->len, 0);
+}
+
+int receive_request(http_worker *hw, struct str *request){
+ // for some reason SSL_has_pending can return 0 but we can still read data
+ struct pollfd pfd[1] = { {.fd = hw->csocket, .events = POLLIN } };
+ while((hw->secure && SSL_has_pending(hw->ssl)) || poll(pfd, 1, 0)){
+ int new = worker_read(hw, request);
+ if(new < 0 || (hw->secure && new == 0)){
+ int error = new;
+ if(hw->secure) error = pleasesslgivemetheerror(SSL_get_error(hw->ssl, new));
+ else log_error("http (no s) error: %s", strerror(errno));
+ return error;
+ }
+ request->len += new;
+ if(request->len == request->cap){
+ log_info("gotta resize buffer");
+ if(resize_str(request, request->cap*2) != 0){
+ log_error("Not enough memory in reply str");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+struct file generate_resource(struct uri resource, struct str url){
+ struct file file = {0};
+ /*
+ generate if both of these are true
+ 1) no file specified (aka we need index.html)
+ 2) theres an index.html.php
+ 3) index.html isnt cached (future)
+ */
+ struct str phpfile = nstr(resource.path.len + slen(".php"));
+ copy_strs(phpfile, resource.path, sstr(".php"));
+ if(access(phpfile.ptr, F_OK) == 0){
+ // we need a str_copy or something
+ struct str command = nstr(
+ slen("REQUEST_URI='") + resource.path.len + slen("' QUERY_STRING='") + resource.query.len +
+ slen("' URL='") + url.len +
+ slen("' php -c ./ ") + phpfile.len + slen(" > ") + resource.path.len
+ );
+ copy_strs(command,
+ sstr("REQUEST_URI='"), resource.path, sstr("' QUERY_STRING='"), resource.query,
+ sstr("' URL='"), url,
+ sstr("' php -c ./ "), phpfile, sstr(" > "), resource.path
+ );
+ log_warn("command: %s", command.ptr);
+ system(command.ptr);
+ free_str(&command);
+ }
+ free_str(&phpfile);
+ file.name = dup_str(resource.path);
+/*
+ if(uri.query.len > 0){
+ if(access(uri.path.ptr, F_OK) != 0){
+ file.name.ptr = calloc(slen("localc/404/index.html")+1, sizeof(char));
+ copy_str(file.name, sstr("localc/404/index.html"));
+ return file;
+ }
+
+ struct str pid = utostr(getpid(), 10);
+ file.name.ptr = calloc(uri.path.len + pid.len + 1, sizeof(char));
+ copy_strs(file.name, uri.path, pid);
+
+ struct str command = {0};
+ command.ptr = calloc(
+ slen("REQUEST_URI='") + uri.path.len + slen("' REQUEST_QUERY='") + uri.query.len +
+ slen("' php -c ./ ") + uri.path.len + slen(" > ") + file.name.len,
+ sizeof(char)
+ );
+ copy_strs(command,
+ sstr("REQUEST_URI='"), uri.path, sstr("' REQUEST_QUERY='"), uri.query,
+ sstr("' php -c ./ "), uri.path, sstr(" > "), file.name
+ );
+ log_warn("command: %s", command.ptr);
+ system(command.ptr);
+ file.temp = true;
+
+ free_str(&pid);
+ free_str(&command);
+ }else{
+ file.name.ptr = calloc(uri.path.len+1, sizeof(char));
+ copy_strs(file.name, uri.path);
+ }*/
+
+ return file;
+}
+
+// TODO: REVISE; return 201/204
+char *handlePOST(char *request/*struct str uri, struct str body*/){
+/*
+ char *resource = malloc(1), *uri = getURI(request), *params = getPOSTParams(request);
+
+ if(isPost(uri)){
+ rlen = attachBlock(&resource, rlen, HOMEFOLDER, strlen(HOMEFOLDER));
+ rlen = attachBlock(&resource, rlen, uri, ulen);
+ if(uri[ulen-1] != '/'){
+ rlen = attachBlock(&resource, rlen, "/", len("/"));
+ }
+ generatePagefile(resource, rlen, uri, ulen, params, prlen);
+
+ rlen = attachBlock(&resource, rlen, PAGEFILE, len(PAGEFILE));
+ }
+ resource[rlen] = '\0';
+ free(uri);
+ free(params);
+ return resource;
+*/
+ return NULL;
+}
+
+static uint32_t request_line_len(char *request){
+ uint32_t len = 0;
+ while(request[len] && request[len] != '\r') len++;
+ return len;
+}
+
+// why is this done like this??
+void build_http_message(char *request, int rlen, struct http_message *hm){
+ char *end = request + rlen;
+ memset(hm, 0, sizeof(struct http_message));
+
+ hm->method.ptr = request;
+ while(request < end && *request != ' ') hm->method.len++, request++;
+ if(++request >= end) return;
+
+ hm->uri.ptr = request;
+ while(request < end && *request != ' ') hm->uri.len++, request++;
+ if(++request >= end) return;
+
+ hm->req_ver.ptr = request;
+ while(request < end && request[hm->req_ver.len] != '\r') hm->req_ver.len++;
+ request += hm->req_ver.len + 2;
+ if(request > end) return;
+
+ uint32_t llen = 0;
+ while((llen = request_line_len(request)) != 0){
+ hm->headers[hm->hlen].name.ptr = request;
+ while(
+ hm->headers[hm->hlen].name.len < llen &&
+ request[hm->headers[hm->hlen].name.len] != ':'
+ ) hm->headers[hm->hlen].name.len++;
+
+ request += hm->headers[hm->hlen].name.len + 2;
+ if(request > end) return;
+ hm->headers[hm->hlen].value.ptr = request;
+ hm->headers[hm->hlen].value.len = llen - hm->headers[hm->hlen].name.len - 2;
+ request += hm->headers[hm->hlen].value.len + 2;
+ hm->hlen++;
+ if(request > end) return;
+ }
+ request += 2;
+ if(request > end) return;
+
+ hm->body.ptr = request;
+ while(request < end && request[hm->body.len] != '\0') hm->body.len++;
+}
+
+// TODO: check if endianness affects this
+__attribute__ ((optimize(3))) enum http_method get_http_method(struct str method){
+ uint64_t m;
+ memmove(&m, method.ptr, sizeof(uint64_t));
+ if(((m & 0x0000000000FFFFFF) - 0x0000000000544547) == 0){
+ return GET;
+ }else if(((m & 0x00000000FFFFFFFF) - 0x0000000054534F50) == 0){
+ return POST;
+ }else if(((m & 0x0000000000FFFFFF) - 0x0000000000545550) == 0){
+ return PUT;
+ }else if(((m & 0x0000FFFFFFFFFFFF) - 0x00004554454C4544) == 0){
+ return DELETE;
+ }
+ return GET;
+}
+
+static uint64_t http_len(struct http_message *hm){
+ uint64_t len = 0;
+ len += hm->method.len + hm->uri.len + hm->req_ver.len + 5 + 2;
+ for(int i = 0; i < hm->hlen; ++i){
+ len += hm->headers[i].name.len + hm->headers[i].value.len + 4;
+ }
+ return len;
+}
+
+/*static struct str assemble_with_body(struct http_message *hm, FILE *body, uint64_t len){
+ struct str s = {0};
+ s.ptr = calloc(http_len(hm) + len + 1, sizeof(char));
+ copy_strs(s, hm->method, sstr(" "), hm->uri, sstr(" "), hm->req_ver, sstr("\r\n"));
+
+ for(int i = 0; i < hm->hlen; i++){
+ copy_strs(s, hm->headers[i].name, sstr(": "), hm->headers[i].value, sstr("\r\n"));
+ }
+ copy_str(s, sstr("\r\n"));
+
+ fread(s.ptr+s.len, sizeof(char), len, body);
+ s.len += len;
+ //copy_str(s, sstr("\r\n"));
+
+ return s;
+}*/
+
+static struct str http_header_to_str(struct http_message *hm){
+ struct str s = {0};
+ s.ptr = calloc(http_len(hm) + 1, sizeof(char));
+ copy_strs(s, hm->method, sstr(" "), hm->uri, sstr(" "), hm->req_ver, sstr("\r\n"));
+ for(int i = 0; i < hm->hlen; i++){
+ copy_strs(s, hm->headers[i].name, sstr(": "), hm->headers[i].value, sstr("\r\n"));
+ }
+ copy_str(s, sstr("\r\n"));
+ return s;
+}
+
+/*
+ Given two strings p and u, and a uri (a struct with two strings) o
+ where p is a pattern, u is a url that is gonna checked for the pattern
+ and o is the uri output blueprint that must be returned should u follow p
+
+ There is a tokens array where tokens found in the url can be stored (up to 9 tokens)
+
+ In p:
+ <X> optionally matches a character X and stores it in the tokens array
+ ^ optionally matches any character and stores it in the tokens array
+ * optionally matches a string of character until another match is found
+ or the url to match ends, and stores it in the tokens array
+
+ In o:
+ $1 through $9 reference the tokens in the token array (I could make it 10 tokens tbh)
+ Both the strings in the uri can access these tokens
+ Referencing a token writes it to the output uri
+ If theres a token before a <X>, say $3<a> that means that the character
+ between the less than and greater than signs will only be written
+ to the output if the token number 3 in the array exists (has length > 0)
+ All other characters get outputted normally
+*/
+static struct uri uri_rewrite(struct str p, struct str u, struct uri o){
+ int i = 0, j = 0, in = 0, ti = 0;
+ struct str tokens[9] = {0};
+ for(; j < p.len; i += 1){
+ if(i < u.len && p.ptr[j+in] == '<' && p.ptr[j+in+1] == u.ptr[i]){
+ if(ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 1};
+ j += 3+in, in = 0;
+ }else if(i < u.len && (p.ptr[j+in] == '^' || p.ptr[j+in] == u.ptr[i])){
+ if(p.ptr[j] == '^' && ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 1};
+ j += 1+in, in = 0;
+ }else if(i < u.len && p.ptr[j] == '*'){
+ if(!in){
+ if(ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 0};
+ in = 1;
+ }
+ if(ti-in < 9) tokens[ti-in].len++;
+ }else{
+ if(i >= u.len && p.ptr[j] == '*') j++;
+ else if(i >= u.len && p.ptr[j] == '<') j += 3;
+ else return (struct uri){0};
+ }
+ }
+ if(i < u.len) return (struct uri){0};
+
+ struct uri r = {0};
+ struct str *no = &o.path, *nr = &r.path;
+ for(int k = 0, rlen = 0; k < 2; k++, no = &o.query, nr = &r.query){
+ for(int i = 0; i < no->len; i++, rlen++){
+ if(no->ptr[i] == '$') rlen += tokens[no->ptr[++i]-'1'].len-1;
+ }
+ nr->ptr = calloc(rlen+1, sizeof(char));
+ if(nr->ptr == NULL){
+ if(r.path.ptr != NULL) free(r.path.ptr);
+ return (struct uri){0};
+ }
+
+ for(int i = 0; i < no->len; i++){
+ if(no->ptr[i] == '$'){
+ if(++i+1 < no->len && no->ptr[i+1] == '<'){
+ if(tokens[no->ptr[i]-'1'].len > 0) i++;
+ else while(no->ptr[++i] != '>');
+ }else{
+ copy_str((*nr), tokens[no->ptr[i]-'1']);
+ }
+ }else{
+ if(no->ptr[i] != '>') nr->ptr[nr->len++] = no->ptr[i];
+ }
+ }
+ }
+
+ return r;
+}
+
+struct uri sanitize_uri(struct str uri){
+ struct str suri = {.ptr = calloc(uri.len+1, sizeof(char)), .len = 0};
+ if(suri.ptr == NULL) return (struct uri){0};
+ int i = 0, o = 0;
+ while(i+o < uri.len){
+ suri.ptr[i] = lowerchar(uri.ptr[i+o]);
+ if(i > 0 && (
+ (uri.ptr[i+o] == '/' && uri.ptr[i+o-1] == '/') ||
+ (uri.ptr[i+o] == '.' && uri.ptr[i+o-1] == '.') ||
+ (uri.ptr[i+o] == '/' && i+o+1 == uri.len) ||
+ uri.ptr[i+o] == '%')
+ ){
+ ++o;
+ }else{
+ ++i;
+ }
+ }
+ suri.ptr[suri.len = i] = '\0';
+
+ struct uri u = {0};
+ for(int i = 0; i < list_size(uri_rewrites); i++){
+ u = uri_rewrite(uri_rewrites[i].pattern, suri, uri_rewrites[i].output);
+ if(u.path.len > 0) break;
+ }
+ free_str(&suri);
+ if(u.path.len == 0){
+ u.path = str("localc/404/index.html");
+ free_str(&u.query);
+ }
+ return u;
+}
+
+static inline int worker_write(http_worker *hw, struct str buf){
+ return hw->secure ?
+ SSL_write(hw->ssl, buf.ptr, buf.len) :
+ send(hw->csocket, buf.ptr, buf.len, 0);
+}
+
+// use sendfile(2). reason:
+// "sendfile() copies data between one file descriptor and another.
+// Because this copying is done within the kernel, sendfile() is
+// more efficient than the combination of read(2) and write(2),
+// which would require transferring data to and from user space."
+void send_file(http_worker *hw, struct str filename){
+ log_info("requested '%.*s' -> ", filename.len, filename.ptr);
+ uint64_t fsize = get_file_size(filename.ptr);
+ if(fsize == 0){
+ // we should free it or something here idk maybe not
+ filename = sstr("localc/404/index.html");
+ fsize = get_file_size(filename.ptr);
+ }
+ log_info("sending '%.*s'", filename.len, filename.ptr);
+
+ enum mime_type type = TXT;
+ struct str fmt = get_file_format(filename);
+ for(int i = 0; i < sizeof(mime_types)/sizeof(mime_types[0]); i++){
+ if(strncmp(fmt.ptr, mime_types[i].fmt.ptr, fmt.len) == 0){
+ type = i;
+ break;
+ }
+ }
+ free_str(&fmt);
+
+ struct http_message hm = {
+ .resp_ver = sstr("HTTP/1.1"), .status = sstr("200"), .reason = sstr("OK"),
+ .hlen = 2, .headers = {
+ { .name = response_headers[CONTENT_TYPE], .value = mime_types[type].type },
+ { .name = response_headers[CONTENT_LENGTH], .value = utostr(fsize, 10) }
+ },
+ .body = {0},
+ };
+
+ struct str header = http_header_to_str(&hm);
+ free_str(&hm.headers[1].value);
+ int sent = worker_write(hw, header);
+ if(sent != header.len){
+ if(hw->secure){
+ pleasesslgivemetheerror(SSL_get_error(hw->ssl, sent));
+ }else{
+ log_error("send_file: %s", strerror(errno));
+ }
+ }
+ free_str(&header);
+
+ // make sure system is 64bits before compiling with O_LARGEFILE
+ // use fopen maybe?
+ int fd = open(filename.ptr, O_RDONLY | /*O_LARGEFILE |*/ O_NONBLOCK);
+ while(fsize > 0){
+ // CYGWIN DOESNT HAVE SENDFILE OR SPLICE FOR
+ // SOME REASON WHEN THIS IS A REAL PROGRAM
+ // RUNNING ON LINUX USE SENDFILE (<sys/sendfile.h>)
+ // AND COMPILE OPENSSL TO USE KTLS
+ // sent = using_ssl ?
+ // SSL_sendfile(ssl, fd, 0, fsize, 0) :
+ // sendfile(socket, fd, NULL, fsize);
+
+ // we're ignoring MAX_BODY_SIZE
+ struct str fuckcygwinineedsendfile;
+ fd_to_str(&fuckcygwinineedsendfile, fd, fsize);
+ sent = worker_write(hw, fuckcygwinineedsendfile);
+ free_str(&fuckcygwinineedsendfile);
+
+ if(sent > 0){ fsize -= sent;
+ }else if(sent == 0){ break;
+ }else{
+ if(hw->secure){
+ pleasesslgivemetheerror(SSL_get_error(hw->ssl, sent));
+ }else{
+ perror("send_file (fuck cygwin btw)");
+ }
+ }
+ log_info("sent %d more bytes", sent);
+ }
+ close(fd);
+}
+
+// this shouldnt be here
+int read_uri_rewrites(char *map, uint64_t size){
+ uint64_t i = 0;
+ if(strncmp(map, "rewrites", slen("rewrites")) != 0) return 1;
+ i += slen("rewrites");
+ while(charisspace(map[i])) i++;
+ if(map[i++] != '{') return 1;
+ list_free(uri_rewrites);
+ init_nlist(uri_rewrites);
+ while(i < size && map[i] != '}'){
+ struct uri_mod um = {0};
+ i += ({sread_delim_f(map+i, charisspace, false);}).len;
+ i += ({um.pattern = read_delim_f(map+i, charisspace, true);}).len;
+ i += ({sread_delim_f(map+i, charisblank, false);}).len;
+ i += ({um.output.path = read_delim_f(map+i, charisspace, true);}).len;
+ i += ({sread_delim_f(map+i, charisblank, false);}).len;
+ i += ({um.output.query = read_delim_f(map+i, charisspace, true);}).len;
+ i += ({sread_delim(map+i, '\n');}).len + 1;
+ list_push(uri_rewrites, um);
+ }
+ return 0;
+}
+
+void free_uri_rewrites(void){
+ for(uint32_t i = 0; i < list_size(uri_rewrites); i++){
+ free_str(&uri_rewrites[i].pattern);
+ free_str(&uri_rewrites[i].output.path);
+ free_str(&uri_rewrites[i].output.query);
+ }
+ list_free(uri_rewrites);
+}
+
--- /dev/null
+#ifndef NET_H
+#define NET_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+//#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <time.h>
+#include "str/str.h"
+#include "list/list.h"
+#include "dir/dir.h"
+#include "log/log.h"
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <poll.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/sendfile.h>
+#endif
+
+
+enum http_method {
+ GET,
+ POST,
+ PUT,
+ DELETE
+};
+
+enum response_header {
+ CONTENT_LENGTH,
+ CONTENT_TYPE,
+ TRANSFER_ENCODING,
+};
+
+enum mime_type {
+ AVIF, BMP, CSS, CSV, GZ, GIF, HTML,
+ ICO, JPG, JPEG, JS, JSON, MIDI, MP3,
+ MP4, MPEG, PNG, PDF, PHP, RAR, TIFF, TS,
+ TXT, WAV, WEBA, WEBM, WEBP, XML, ZIP,
+ _7Z,
+};
+
+
+struct uri {
+ struct str path;
+ struct str query;
+};
+
+struct uri_mod {
+ struct str pattern;
+ struct uri output;
+};
+
+struct header {
+ struct str name;
+ struct str value;
+};
+
+#define MAX_HEADERS 16
+
+
+struct http_message {
+ union {
+ struct str method;
+ struct str resp_ver;
+ };
+ union {
+ struct str uri;
+ struct str status;
+ };
+ union {
+ struct str req_ver;
+ struct str reason;
+ };
+ int hlen;
+ struct header headers[MAX_HEADERS];
+ struct str body;
+};
+
+typedef struct http_server {
+ struct str port;
+ int backlog;
+ int ssocket;
+} http_server;
+
+typedef ssize_t (*recv_func)(int,void*,size_t,int);
+typedef int (*ssl_recv_func)(SSL*,void*,int);
+typedef struct http_worker {
+ int ssocket;
+ int csocket;
+ int secure;
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ recv_func receive;
+ ssl_recv_func ssl_receive;
+} http_worker;
+
+#define MAX_RESPONSE_SIZE 0x0FFFFFFF
+#define MAX_BODY_SIZE (MAX_RESPONSE_SIZE - 0x0FFF)
+
+#define insert_header(hm, h) \
+ (hm).headers[(hm).hlen++] = (h)
+
+
+http_server *setup_http_server(struct str port, int backlog);
+
+void destroy_http_server(http_server **hs);
+
+http_worker *setup_http_worker(int ssocket, int secure, struct str certfile, struct str keyfile);
+
+void destroy_http_worker(http_worker **hw);
+
+void reset_worker_ssl(http_worker *hw);
+
+int accept_connection(http_worker *hw, char ip[INET_ADDRSTRLEN]);
+
+int receive_request(http_worker *hw, struct str *request);
+
+struct file generate_resource(struct uri resource, struct str url);
+
+char *handlePOST(char *request);
+
+void build_http_message(char *request, int len, struct http_message *hm);
+
+enum http_method get_http_method(struct str method);
+
+struct uri sanitize_uri(struct str uri);
+
+void send_file(http_worker *hw, struct str filename);
+
+int read_uri_rewrites(char *map, uint64_t size);
+
+void free_uri_rewrites(void);
+
+#endif
--- /dev/null
+#ifndef UTIL_H
+#define UTIL_H
+
+#define ARRAY_LEN(first, ...) \
+ (sizeof(((typeof(first) []){first, __VA_ARGS__}))/(sizeof(typeof(first))))
+
+#define bool_equal(a,b) ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ ((_a) ? (_b) : (!(_b))); \
+})
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include "str/str.h"
+#include "ipc/ipc.h"
+#include "net/net.h"
+
+
+ipc_listener *listener;
+http_worker *worker;
+struct str rewritesfile;
+
+
+int init(char *argv[]){
+ // replace signals with unix sockets
+ // reinit
+ // finish
+ // toggle ssl
+ //log_set_level(LOG_DEBUG, 0);
+ //log_set_level(LOG_INFO, 0);
+ //log_set_level(LOG_WARN, 0);
+
+ struct str saddr = str(argv[1]);
+ listener = setup_ipc_listener(saddr);
+ free_str(&saddr);
+ if(listener == NULL){
+ log_error("cant set up ipc listener on worker %d", getpid());
+ return 1;
+ }
+
+ // check key for value maybe idk
+ ipc_message msg = receive_ipc_message(listener);
+ int ssocket = strtou(msg.val);
+ free_ipc_message(msg); // v configurable certificate locations?
+ worker = setup_http_worker(ssocket, 1, sstr("ssl/cert.pem"), sstr("ssl/key.pem"));
+ if(worker == NULL){
+ log_error("setting up http worker");
+ return 1;
+ }
+ // this is disgusting and should be done elsewhere
+ msg = receive_ipc_message(listener); // check for value
+ rewritesfile = dup_str(msg.val);
+ free_ipc_message(msg);
+ int fsize = get_file_size(rewritesfile.ptr);
+ int fd = open(rewritesfile.ptr, O_RDONLY | O_NONBLOCK);
+ char *rewrites = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
+ if(rewrites == (void*)-1){
+ log_error("cant mmap rewrites: %s", strerror(errno));
+ return 1;
+ }
+ if(read_uri_rewrites(rewrites, fsize) != 0){
+ log_error("init: read_uri_rewrites: %s", strerror(errno));
+ return 1;
+ }
+ munmap(rewritesfile.ptr, fsize);
+
+ return 0;
+}
+
+void deinit(void){
+ destroy_ipc_listener(&listener);
+ destroy_http_worker(&worker);
+ free_str(&rewritesfile);
+ free_uri_rewrites();
+}
+
+
+int main(int argc, char **argv){
+
+ int return_value = 0;
+
+ if(init(argv) != 0){
+ return_value = 1;
+ goto DEINIT;
+ }
+ log_info("init'd");
+
+ bool end = false;
+ struct str request = {.cap = 8192, .len = 0, .ptr = alloca(8192)};
+ while(!end){
+ char cip[INET_ADDRSTRLEN] = {0};
+ return_value = accept_connection(worker, cip);
+ switch(return_value){
+ case -1: // couldnt accept, do something ig
+ continue;
+ case SSL_ERROR_SSL:
+ reset_worker_ssl(worker);
+ log_info("continuing\n");
+ continue;
+ }
+ log_info("socket %d accepted with ip %s", worker->csocket, cip);
+ return_value = receive_request(worker, &request);
+ switch(return_value){
+ case -1: // couldnt accept, do something ig
+ continue;
+ case SSL_ERROR_SSL:
+ reset_worker_ssl(worker);
+ log_info("continuing\n");
+ continue;
+ }
+
+ printf("'%.*s'\n", request.len, request.ptr);
+
+ struct http_message hm = {0};
+ build_http_message(request.ptr, request.len, &hm);
+ log_info("uri before: %.*s", hm.uri.len, hm.uri.ptr);
+ struct uri suri = sanitize_uri(hm.uri);
+ log_info("uri after: %.*s", suri.path.len, suri.path.ptr);
+ enum http_method method = get_http_method(hm.method);
+
+ switch(method){
+ case GET:
+ struct file resource = generate_resource(suri, hm.uri);
+ send_file(worker, resource.name);
+ //if(resource.temp == true) remove(resource.name.ptr);
+ free_str(&resource.name);
+ break;
+ case POST:
+ //handlePOST(request);
+ send(worker->csocket, "HTTP/1.1 201 Created\r\n\r\n", len("HTTP/1.1 201 Created\r\n\r\n"), 0);
+ break;
+ case PUT:
+ break;
+ case DELETE:
+ break;
+ }
+ free_str(&suri.path);
+ free_str(&suri.query);
+ request.len = 0;
+
+ SSL_clear(worker->ssl);
+ close(worker->csocket);
+
+ //SSL_shutdown(config.ssl); // look into SSL_clear()
+ }
+
+DEINIT:
+ log_info("dieing :(");
+ deinit();
+
+ return return_value;
+}
+
+/*
+struct timespec t_start, t_end;
+
+#define start_timing() clock_gettime(CLOCK_MONOTONIC, &t_start)
+#define end_timing() clock_gettime(CLOCK_MONOTONIC, &t_end)
+
+double get_timing(void){
+ uint64_t diff = (t_end.tv_sec*1000000000 + t_end.tv_nsec) - (t_start.tv_sec*1000000000 + t_start.tv_nsec);
+ return diff/1000000000.0;
+}
+*/