Initial commit
This commit is contained in:
commit
7f3fab3aab
40
Makefile
Executable file
40
Makefile
Executable file
@ -0,0 +1,40 @@
|
||||
CC = cyg
|
||||
CFLAGS = -O0 -Wall -ggdb3
|
||||
LDFLAGS = -lssl -lcrypto
|
||||
|
||||
SRCDIR = src
|
||||
OBJDIR = obj
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LIBDIR = C:/Users/quique/Documents/Programming/libs
|
||||
RUNCMD = server.exe
|
||||
else
|
||||
LIBDIR = /mnt/c/Users/quique/Documents/Programming/libs
|
||||
RUNCMD = ./server.exe
|
||||
endif
|
||||
|
||||
OBJS = $(addprefix $(OBJDIR)/, bit.o str.o log.o list.o crc64.o dir.o net.o ipc.o)
|
||||
|
||||
INCL = -I$(LIBDIR) -I$(SRCDIR)
|
||||
|
||||
|
||||
|
||||
all: server.exe worker.exe
|
||||
|
||||
server.exe: $(SRCDIR)/main.c $(OBJS)
|
||||
$(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o server.exe $(LDFLAGS)
|
||||
|
||||
worker.exe: $(SRCDIR)/worker.c $(OBJS)
|
||||
$(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o worker.exe $(LDFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%/*.c $(SRCDIR)/%/*.h
|
||||
$(CC) -c $(SRCDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: $(LIBDIR)/%/*.c $(LIBDIR)/%/*.h
|
||||
$(CC) -c $(LIBDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f worker.exe server.exe $(OBJDIR)/*.o
|
||||
|
||||
memcheck:
|
||||
sudo valgrind --show-leak-kinds=all --leak-check=full --track-origins=yes --trace-children=yes -s $(RUNCMD) 443
|
||||
|
191
src/crc64/crc64.c
Executable file
191
src/crc64/crc64.c
Executable file
@ -0,0 +1,191 @@
|
||||
/* 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
|
11
src/crc64/crc64.h
Executable file
11
src/crc64/crc64.h
Executable file
@ -0,0 +1,11 @@
|
||||
#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
|
72
src/dir/dir.c
Executable file
72
src/dir/dir.c
Executable file
@ -0,0 +1,72 @@
|
||||
#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;
|
||||
}
|
||||
|
36
src/dir/dir.h
Executable file
36
src/dir/dir.h
Executable file
@ -0,0 +1,36 @@
|
||||
#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
|
143
src/ipc/ipc.c
Executable file
143
src/ipc/ipc.c
Executable file
@ -0,0 +1,143 @@
|
||||
#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;
|
||||
}
|
||||
|
42
src/ipc/ipc.h
Executable file
42
src/ipc/ipc.h
Executable file
@ -0,0 +1,42 @@
|
||||
#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
|
196
src/main.c
Executable file
196
src/main.c
Executable file
@ -0,0 +1,196 @@
|
||||
#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
|
||||
*/
|
||||
|
641
src/net/net.c
Executable file
641
src/net/net.c
Executable file
@ -0,0 +1,641 @@
|
||||
#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);
|
||||
}
|
||||
|
144
src/net/net.h
Executable file
144
src/net/net.h
Executable file
@ -0,0 +1,144 @@
|
||||
#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
|
13
src/util/util.h
Executable file
13
src/util/util.h
Executable file
@ -0,0 +1,13 @@
|
||||
#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
|
154
src/worker.c
Executable file
154
src/worker.c
Executable file
@ -0,0 +1,154 @@
|
||||
#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;
|
||||
}
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user