Compare commits
	
		
			35 Commits
		
	
	
		
			dev
			...
			c8b605aa90
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c8b605aa90 | |||
| 59b9bfd53e | |||
| e8655102b9 | |||
| add135b3ad | |||
| 2d250621c8 | |||
| f96dc46ea7 | |||
| 5138dafb8e | |||
| 4be5b0d195 | |||
| a84aa591d4 | |||
| d7db1f0be9 | |||
| 116f7035ed | |||
| 8abc0b8bfa | |||
| 410d692372 | |||
| f42ac83323 | |||
| 7d576b2466 | |||
| 5acab8144d | |||
| 4814035b62 | |||
| 15f400f376 | |||
| 32638fdd91 | |||
| 07d6280864 | |||
| 820ce8dac9 | |||
| 5bd9b3fb33 | |||
| a3dcddf69d | |||
| 487386158d | |||
| 32f00175e4 | |||
| 581b4dc5aa | |||
| 611ceabfb2 | |||
| 6b295cef95 | |||
| 764b754a6f | |||
| 7699583c5f | |||
| 8b92d4ff91 | |||
| e8d8abdc5a | |||
| 15c6b7ddaa | |||
| e9a065f8aa | |||
| 0730429041 | 
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
|  |  | ||||||
| CC=gcc | CC=gcc | ||||||
| CFLAGS=-std=gnu11 -Wno-unused-but-set-variable -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809L | CFLAGS=-std=gnu11 -Wno-unused-but-set-variable -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809L | ||||||
| LDFLAGS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc | LDFLAGS=-pthread -lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc | ||||||
|  |  | ||||||
| DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\"" | DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\"" | ||||||
|  |  | ||||||
| @@ -36,7 +36,7 @@ bin/res: | |||||||
| 	mkdir -p bin/res | 	mkdir -p bin/res | ||||||
|  |  | ||||||
| bin/test: test/mock_*.c test/test_*.c \ | bin/test: test/mock_*.c test/test_*.c \ | ||||||
|           src/lib/utils.c src/lib/sock.c src/lib/list.c src/lib/http.c src/lib/http_static.c src/logger.c |           src/lib/utils.c src/lib/sock.c src/lib/list.c src/lib/http.c src/lib/http_static.c src/logger.c src/lib/error.c | ||||||
| 	$(CC) -o $@ $(CFLAGS) $^ -lcriterion | 	$(CC) -o $@ $(CFLAGS) $^ -lcriterion | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -54,7 +54,7 @@ bin/res/%.o: bin/res/%.txt | |||||||
|  |  | ||||||
| bin/res/%.txt: res/%.* | bin/res/%.txt: res/%.* | ||||||
| 	cp $^ $@ | 	cp $^ $@ | ||||||
| 	echo -ne "\x00" >> $@ | 	/bin/echo -ne "\x00" >> $@ | ||||||
|  |  | ||||||
| bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/workers.o \ | bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/workers.o \ | ||||||
|              bin/worker/request_handler.o bin/worker/tcp_acceptor.o \ |              bin/worker/request_handler.o bin/worker/tcp_acceptor.o \ | ||||||
|   | |||||||
| @@ -212,8 +212,9 @@ void async_thread(void) { | |||||||
|     int num_fds; |     int num_fds; | ||||||
|     long ts, min_ts, cur_ts; |     long ts, min_ts, cur_ts; | ||||||
|     listen_queue_t *l; |     listen_queue_t *l; | ||||||
|     evt_listen_t **local = list_create(sizeof(evt_listen_t *), 16); |     evt_listen_t **local; | ||||||
|     if (local == NULL) { |  | ||||||
|  |     if ((local = list_create(sizeof(evt_listen_t *), 16)) == NULL) { | ||||||
|         critical("Unable to create async local list"); |         critical("Unable to create async local list"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -246,6 +247,7 @@ void async_thread(void) { | |||||||
|         // reset size of queue |         // reset size of queue | ||||||
|         l->n = 0; |         l->n = 0; | ||||||
|  |  | ||||||
|  |         // TODO timeout calculation = O(n) | ||||||
|         // calculate wait timeout |         // calculate wait timeout | ||||||
|         min_ts = -1000, cur_ts = clock_micros();; |         min_ts = -1000, cur_ts = clock_micros();; | ||||||
|         for (int i = 0; i < list_size(local); i++) { |         for (int i = 0; i < list_size(local); i++) { | ||||||
| @@ -270,6 +272,8 @@ void async_thread(void) { | |||||||
|  |  | ||||||
|         for (int i = 0; i < num_fds; i++) { |         for (int i = 0; i < num_fds; i++) { | ||||||
|             evt_listen_t *evt = events[i].data.ptr; |             evt_listen_t *evt = events[i].data.ptr; | ||||||
|  |             if (!list_contains(local, &evt)) continue; | ||||||
|  |  | ||||||
|             if (async_exec(evt, async_e2a(events[i].events)) == 0) { |             if (async_exec(evt, async_e2a(events[i].events)) == 0) { | ||||||
|                 if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) { |                 if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) { | ||||||
|                     if (errno == EBADF) { |                     if (errno == EBADF) { | ||||||
|   | |||||||
| @@ -25,10 +25,9 @@ | |||||||
|  |  | ||||||
| #define CACHE_BUF_SIZE 16 | #define CACHE_BUF_SIZE 16 | ||||||
|  |  | ||||||
|  |  | ||||||
| static magic_t magic; | static magic_t magic; | ||||||
| static pthread_t thread; | static pthread_t thread; | ||||||
| static sem_t sem_free, sem_used, sem_lock; | static sem_t sem_free, sem_used, sem_lock, sem_cache, sem_magic; | ||||||
| volatile sig_atomic_t alive = 1; | volatile sig_atomic_t alive = 1; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
| @@ -53,6 +52,54 @@ static int magic_init(void) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void magic_mime_type(const char *restrict filename, char *buf) { | ||||||
|  |     retry: | ||||||
|  |     if (sem_wait(&sem_magic) != 0) { | ||||||
|  |         if (errno == EINTR) { | ||||||
|  |             errno = 0; | ||||||
|  |             goto retry; | ||||||
|  |         } else { | ||||||
|  |             critical("Unable to lock magic semaphore"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     magic_setflags(magic, MAGIC_MIME_TYPE); | ||||||
|  |     const char *type = magic_file(magic, filename); | ||||||
|  |  | ||||||
|  |     if (strstarts(type, "text/")) { | ||||||
|  |         if (strends(filename, ".css")) { | ||||||
|  |             strcpy(buf, "text/css"); | ||||||
|  |             sem_post(&sem_magic); | ||||||
|  |             return; | ||||||
|  |         } else if (strends(filename, ".js")) { | ||||||
|  |             strcpy(buf, "application/javascript"); | ||||||
|  |             sem_post(&sem_magic); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     strcpy(buf, type); | ||||||
|  |     sem_post(&sem_magic); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void magic_charset(const char *restrict filename, char *buf) { | ||||||
|  |     retry: | ||||||
|  |     if (sem_wait(&sem_magic) != 0) { | ||||||
|  |         if (errno == EINTR) { | ||||||
|  |             errno = 0; | ||||||
|  |             goto retry; | ||||||
|  |         } else { | ||||||
|  |             critical("Unable to lock magic semaphore"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     magic_setflags(magic, MAGIC_MIME_ENCODING); | ||||||
|  |     strcpy(buf, magic_file(magic, filename)); | ||||||
|  |     sem_post(&sem_magic); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void cache_free(void) { | static void cache_free(void) { | ||||||
|     for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { |     for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { | ||||||
|         host_config_t *hc = &config.hosts[i]; |         host_config_t *hc = &config.hosts[i]; | ||||||
| @@ -61,6 +108,14 @@ static void cache_free(void) { | |||||||
|  |  | ||||||
|         munmap(hc->cache, sizeof(cache_t)); |         munmap(hc->cache, sizeof(cache_t)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     magic_close(magic); | ||||||
|  |  | ||||||
|  |     sem_destroy(&sem_lock); | ||||||
|  |     sem_destroy(&sem_free); | ||||||
|  |     sem_destroy(&sem_used); | ||||||
|  |     sem_destroy(&sem_cache); | ||||||
|  |     sem_destroy(&sem_magic); | ||||||
| } | } | ||||||
|  |  | ||||||
| static cache_entry_t *cache_get_entry(cache_t *cache, const char *filename) { | static cache_entry_t *cache_get_entry(cache_t *cache, const char *filename) { | ||||||
| @@ -69,7 +124,7 @@ static cache_entry_t *cache_get_entry(cache_t *cache, const char *filename) { | |||||||
|     for (int i = 0; i < CACHE_ENTRIES; i++) { |     for (int i = 0; i < CACHE_ENTRIES; i++) { | ||||||
|         entry = &cache->entries[i]; |         entry = &cache->entries[i]; | ||||||
|         if (entry->filename[0] == 0) break; |         if (entry->filename[0] == 0) break; | ||||||
|         if (strcmp(entry->filename, filename) == 0) { |         if (streq(entry->filename, filename)) { | ||||||
|             // found |             // found | ||||||
|             return entry; |             return entry; | ||||||
|         } |         } | ||||||
| @@ -80,13 +135,29 @@ static cache_entry_t *cache_get_entry(cache_t *cache, const char *filename) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static cache_entry_t *cache_get_new_entry(cache_t *cache) { | static cache_entry_t *cache_get_new_entry(cache_t *cache) { | ||||||
|  |     // globally lock cache | ||||||
|  |     retry: | ||||||
|  |     if (sem_wait(&sem_cache) != 0) { | ||||||
|  |         if (errno == EINTR) { | ||||||
|  |             goto retry; | ||||||
|  |         } else { | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // search empty slot |     // search empty slot | ||||||
|     cache_entry_t *entry; |     cache_entry_t *entry; | ||||||
|     for (int i = 0; i < CACHE_ENTRIES; i++) { |     for (int i = 0; i < CACHE_ENTRIES; i++) { | ||||||
|         entry = &cache->entries[i]; |         entry = &cache->entries[i]; | ||||||
|         if (entry->filename[0] == 0) |         if (entry->filename[0] == 0) { | ||||||
|  |             // unlock cache | ||||||
|  |             sem_post(&sem_cache); | ||||||
|             return entry; |             return entry; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // unlock cache | ||||||
|  |     sem_post(&sem_cache); | ||||||
|  |  | ||||||
|     // not found |     // not found | ||||||
|     return NULL; |     return NULL; | ||||||
| @@ -98,7 +169,7 @@ static void cache_process_entry(cache_entry_t *entry) { | |||||||
|     info("Hashing file %s", entry->filename); |     info("Hashing file %s", entry->filename); | ||||||
|  |  | ||||||
|     EVP_MD_CTX *ctx = EVP_MD_CTX_new(); |     EVP_MD_CTX *ctx = EVP_MD_CTX_new(); | ||||||
|     EVP_DigestInit(ctx, EVP_sha1()); |     EVP_DigestInit(ctx, EVP_sha256()); | ||||||
|     FILE *file = fopen(entry->filename, "rb"); |     FILE *file = fopen(entry->filename, "rb"); | ||||||
|     int compress = mime_is_compressible(entry->meta.type); |     int compress = mime_is_compressible(entry->meta.type); | ||||||
|  |  | ||||||
| @@ -120,9 +191,7 @@ static void cache_process_entry(cache_entry_t *entry) { | |||||||
|  |  | ||||||
|         char *rel_path = entry->filename + entry->webroot_len + 1; |         char *rel_path = entry->filename + entry->webroot_len + 1; | ||||||
|         for (int j = 0; j < strlen(rel_path); j++) { |         for (int j = 0; j < strlen(rel_path); j++) { | ||||||
|             char ch = rel_path[j]; |             buf[j] = (char) ((rel_path[j] == '/') ? '_' : rel_path[j]); | ||||||
|             if (ch == '/') ch = '_'; |  | ||||||
|             buf[j] = ch; |  | ||||||
|         } |         } | ||||||
|         buf[strlen(rel_path)] = 0; |         buf[strlen(rel_path)] = 0; | ||||||
|  |  | ||||||
| @@ -146,7 +215,7 @@ static void cache_process_entry(cache_entry_t *entry) { | |||||||
|             comp_err: |             comp_err: | ||||||
|             compress = 0; |             compress = 0; | ||||||
|         } else { |         } else { | ||||||
|             if ((compress_init(&comp_ctx, COMPRESS_GZ | COMPRESS_BR)) != 0) { |             if ((compress_init(&comp_ctx, COMPRESS_GZ | COMPRESS_BR | (mime_is_text(entry->meta.type) ? COMPRESS_UTF8 : 0))) != 0) { | ||||||
|                 error("Unable to init compression"); |                 error("Unable to init compression"); | ||||||
|                 compress = 0; |                 compress = 0; | ||||||
|                 fclose(comp_file_gz); |                 fclose(comp_file_gz); | ||||||
| @@ -155,11 +224,9 @@ static void cache_process_entry(cache_entry_t *entry) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     unsigned long read; |     for (unsigned long read, avail_in, avail_out; (read = fread(buf, 1, sizeof(buf), file)) > 0;) { | ||||||
|     while ((read = fread(buf, 1, sizeof(buf), file)) > 0) { |  | ||||||
|         EVP_DigestUpdate(ctx, buf, read); |         EVP_DigestUpdate(ctx, buf, read); | ||||||
|         if (compress) { |         if (compress) { | ||||||
|             unsigned long avail_in, avail_out; |  | ||||||
|             avail_in = read; |             avail_in = read; | ||||||
|             do { |             do { | ||||||
|                 avail_out = sizeof(comp_buf); |                 avail_out = sizeof(comp_buf); | ||||||
| @@ -243,6 +310,13 @@ int cache_init(void) { | |||||||
|         if (hc->type == CONFIG_TYPE_UNSET) break; |         if (hc->type == CONFIG_TYPE_UNSET) break; | ||||||
|         if (hc->type != CONFIG_TYPE_LOCAL) continue; |         if (hc->type != CONFIG_TYPE_LOCAL) continue; | ||||||
|  |  | ||||||
|  |         sprintf(buf, "%s/.sesimos", hc->local.webroot); | ||||||
|  |         if (mkdir(buf, 0755) != 0 && errno != EEXIST) { | ||||||
|  |             critical("Unable to create directory %s", buf); | ||||||
|  |             return 1; | ||||||
|  |         } | ||||||
|  |         errno = 0; | ||||||
|  |  | ||||||
|         sprintf(buf, "%s/.sesimos/metadata", hc->local.webroot); |         sprintf(buf, "%s/.sesimos/metadata", hc->local.webroot); | ||||||
|         if ((fd = open(buf, O_CREAT | O_RDWR, 0600)) == -1) { |         if ((fd = open(buf, O_CREAT | O_RDWR, 0600)) == -1) { | ||||||
|             critical("Unable to open file %s", buf); |             critical("Unable to open file %s", buf); | ||||||
| @@ -267,7 +341,9 @@ int cache_init(void) { | |||||||
|     // try to initialize all three semaphores |     // try to initialize all three semaphores | ||||||
|     if (sem_init(&sem_lock, 0, 1) != 0 || |     if (sem_init(&sem_lock, 0, 1) != 0 || | ||||||
|         sem_init(&sem_free, 0, 1) != 0 || |         sem_init(&sem_free, 0, 1) != 0 || | ||||||
|         sem_init(&sem_used, 0, 0) != 0) |         sem_init(&sem_used, 0, 0) != 0 || | ||||||
|  |         sem_init(&sem_cache, 0, 1) != 0 || | ||||||
|  |         sem_init(&sem_magic, 0, 1) != 0) | ||||||
|     { |     { | ||||||
|         critical("Unable to initialize semaphore"); |         critical("Unable to initialize semaphore"); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -295,10 +371,10 @@ static void cache_mark_entry_dirty(cache_entry_t *entry) { | |||||||
|     if (entry->flags & CACHE_DIRTY) |     if (entry->flags & CACHE_DIRTY) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     entry->flags |= CACHE_DIRTY; | ||||||
|     memset(entry->meta.etag, 0, sizeof(entry->meta.etag)); |     memset(entry->meta.etag, 0, sizeof(entry->meta.etag)); | ||||||
|     memset(entry->meta.filename_comp_gz, 0, sizeof(entry->meta.filename_comp_gz)); |     memset(entry->meta.filename_comp_gz, 0, sizeof(entry->meta.filename_comp_gz)); | ||||||
|     memset(entry->meta.filename_comp_br, 0, sizeof(entry->meta.filename_comp_br)); |     memset(entry->meta.filename_comp_br, 0, sizeof(entry->meta.filename_comp_br)); | ||||||
|     entry->flags |= CACHE_DIRTY; |  | ||||||
|  |  | ||||||
|     try_again_free: |     try_again_free: | ||||||
|     if (sem_wait(&sem_free) != 0) { |     if (sem_wait(&sem_free) != 0) { | ||||||
| @@ -337,28 +413,12 @@ static void cache_mark_entry_dirty(cache_entry_t *entry) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static void cache_update_entry(cache_entry_t *entry, const char *filename, const char *webroot) { | static void cache_update_entry(cache_entry_t *entry, const char *filename, const char *webroot) { | ||||||
|     struct stat statbuf; |     entry->meta.mtime = stat_mtime(filename); | ||||||
|     stat(filename, &statbuf); |  | ||||||
|     memcpy(&entry->meta.stat, &statbuf, sizeof(statbuf)); |  | ||||||
|  |  | ||||||
|     entry->webroot_len = (unsigned char) strlen(webroot); |     entry->webroot_len = (unsigned char) strlen(webroot); | ||||||
|     strcpy(entry->filename, filename); |     strcpy(entry->filename, filename); | ||||||
|  |  | ||||||
|     magic_setflags(magic, MAGIC_MIME_TYPE); |     magic_mime_type(filename, entry->meta.type); | ||||||
|     const char *type = magic_file(magic, filename); |     magic_charset(filename, entry->meta.charset); | ||||||
|     char type_new[URI_TYPE_SIZE]; |  | ||||||
|     sprintf(type_new, "%s", type); |  | ||||||
|     if (strncmp(type, "text/", 5) == 0) { |  | ||||||
|         if (strcmp(filename + strlen(filename) - 4, ".css") == 0) { |  | ||||||
|             sprintf(type_new, "text/css"); |  | ||||||
|         } else if (strcmp(filename + strlen(filename) - 3, ".js") == 0) { |  | ||||||
|             sprintf(type_new, "application/javascript"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     strcpy(entry->meta.type, type_new); |  | ||||||
|  |  | ||||||
|     magic_setflags(magic, MAGIC_MIME_ENCODING); |  | ||||||
|     strcpy(entry->meta.charset, magic_file(magic, filename)); |  | ||||||
|  |  | ||||||
|     cache_mark_entry_dirty(entry); |     cache_mark_entry_dirty(entry); | ||||||
| } | } | ||||||
| @@ -387,9 +447,7 @@ void cache_init_uri(cache_t *cache, http_uri *uri) { | |||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         // check, if file has changed |         // check, if file has changed | ||||||
|         struct stat statbuf; |         if (uri->meta->mtime != stat_mtime(uri->filename)) { | ||||||
|         stat(uri->filename, &statbuf); |  | ||||||
|         if (memcmp(&uri->meta->stat.st_mtime, &statbuf.st_mtime, sizeof(statbuf.st_mtime)) != 0) { |  | ||||||
|             // modify time has changed |             // modify time has changed | ||||||
|             cache_update_entry(entry, uri->filename, uri->webroot); |             cache_update_entry(entry, uri->filename, uri->webroot); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
|  |  | ||||||
| #define CHUNK_SIZE 8192 | #define CHUNK_SIZE 8192 | ||||||
| #define MAX_PROXY_CNX_PER_HOST 16 | #define MAX_PROXY_CNX_PER_HOST 16 | ||||||
|  | #define ADDRSTRLEN 39 | ||||||
|  |  | ||||||
| #ifndef DEFAULT_HOST | #ifndef DEFAULT_HOST | ||||||
| #   define DEFAULT_HOST "www.necronda.net" | #   define DEFAULT_HOST "www.necronda.net" | ||||||
|   | |||||||
| @@ -13,23 +13,25 @@ | |||||||
|  |  | ||||||
| int compress_init(compress_ctx *ctx, int mode) { | int compress_init(compress_ctx *ctx, int mode) { | ||||||
|     ctx->brotli = NULL; |     ctx->brotli = NULL; | ||||||
|     ctx->mode = 0; |  | ||||||
|     int ret; |  | ||||||
|     if (mode & COMPRESS_GZ) { |     if (mode & COMPRESS_GZ) { | ||||||
|         ctx->mode |= COMPRESS_GZ; |         ctx->mode |= COMPRESS_GZ; | ||||||
|         ctx->gzip.zalloc = Z_NULL; |         ctx->gzip.zalloc = Z_NULL; | ||||||
|         ctx->gzip.zfree = Z_NULL; |         ctx->gzip.zfree = Z_NULL; | ||||||
|         ctx->gzip.opaque = Z_NULL; |         ctx->gzip.opaque = Z_NULL; | ||||||
|         ret = deflateInit2(&ctx->gzip, COMPRESS_LEVEL_GZIP, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY); |         ctx->gzip.data_type = (mode & COMPRESS_UTF8) ? Z_TEXT : Z_UNKNOWN; | ||||||
|         if (ret != Z_OK) return -1; |         if (deflateInit2(&ctx->gzip, COMPRESS_LEVEL_GZIP, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK) | ||||||
|  |             return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (mode & COMPRESS_BR) { |     if (mode & COMPRESS_BR) { | ||||||
|         ctx->mode |= COMPRESS_BR; |         ctx->mode |= COMPRESS_BR; | ||||||
|         ctx->brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL); |         if ((ctx->brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL)) == NULL) | ||||||
|         if (ctx->brotli == NULL) return -1; |             return -1; | ||||||
|         BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC); |         BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_MODE, (mode & COMPRESS_UTF8) ? BROTLI_MODE_TEXT : ((mode & COMPRESS_WOFF) ? BROTLI_MODE_FONT : BROTLI_MODE_GENERIC)); | ||||||
|         BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_QUALITY, COMPRESS_LEVEL_BROTLI); |         BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_QUALITY, COMPRESS_LEVEL_BROTLI); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -41,26 +43,35 @@ int compress_compress(compress_ctx *ctx, const char *in, unsigned long *in_len, | |||||||
|     return compress_compress_mode(ctx, ctx->mode, in, in_len, out, out_len, finish); |     return compress_compress_mode(ctx, ctx->mode, in, in_len, out, out_len, finish); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int compress_brotli(BrotliEncoderState *ctx, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { | ||||||
|  |     int ret = BrotliEncoderCompressStream( | ||||||
|  |             ctx, finish ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, | ||||||
|  |             in_len, (const unsigned char**) &in, out_len, (unsigned char **) &out, NULL); | ||||||
|  |     return (ret == BROTLI_TRUE) ? 0 : -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int compress_gzip(z_stream *gzip, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { | ||||||
|  |     gzip->next_in = (unsigned char*) in; | ||||||
|  |     gzip->avail_in = *in_len; | ||||||
|  |     gzip->next_out = (unsigned char*) out; | ||||||
|  |     gzip->avail_out = *out_len; | ||||||
|  |     int ret = deflate(gzip, finish ? Z_FINISH : Z_NO_FLUSH); | ||||||
|  |     *in_len = gzip->avail_in; | ||||||
|  |     *out_len = gzip->avail_out; | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { | int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { | ||||||
|     if ((mode & COMPRESS_GZ) && (mode & COMPRESS_BR)) { |     if ((mode & COMPRESS_GZ) && (mode & COMPRESS_BR)) { | ||||||
|         errno = EINVAL; |         errno = EINVAL; | ||||||
|         return -1; |         return -1; | ||||||
|     } else if (mode & COMPRESS_GZ) { |     } else if (mode & COMPRESS_GZ) { | ||||||
|         ctx->gzip.next_in = (unsigned char*) in; |         return compress_gzip(&ctx->gzip, in, in_len, out, out_len, finish); | ||||||
|         ctx->gzip.avail_in = *in_len; |  | ||||||
|         ctx->gzip.next_out = (unsigned char*) out; |  | ||||||
|         ctx->gzip.avail_out = *out_len; |  | ||||||
|         int ret = deflate(&ctx->gzip, finish ? Z_FINISH : Z_NO_FLUSH); |  | ||||||
|         *in_len = ctx->gzip.avail_in; |  | ||||||
|         *out_len = ctx->gzip.avail_out; |  | ||||||
|         return ret; |  | ||||||
|     } else if (mode & COMPRESS_BR) { |     } else if (mode & COMPRESS_BR) { | ||||||
|         int ret = BrotliEncoderCompressStream(ctx->brotli, finish ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, |         return compress_brotli(ctx->brotli, in, in_len, out, out_len, finish); | ||||||
|                                               in_len, (const unsigned char**) &in, out_len, (unsigned char **) &out, NULL); |  | ||||||
|         return (ret == BROTLI_TRUE) ? 0 : -1; |  | ||||||
|     } else { |     } else { | ||||||
|         errno = EINVAL; |         errno = EINVAL; | ||||||
|         return -2; |         return -1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -69,6 +80,9 @@ int compress_free(compress_ctx *ctx) { | |||||||
|         BrotliEncoderDestroyInstance(ctx->brotli); |         BrotliEncoderDestroyInstance(ctx->brotli); | ||||||
|         ctx->brotli = NULL; |         ctx->brotli = NULL; | ||||||
|     } |     } | ||||||
|  |     if (ctx->mode & COMPRESS_GZ) { | ||||||
|  |         deflateEnd(&ctx->gzip); | ||||||
|  |     } | ||||||
|     ctx->mode = 0; |     ctx->mode = 0; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,6 +18,8 @@ | |||||||
| #define COMPRESS_GZ 1 | #define COMPRESS_GZ 1 | ||||||
| #define COMPRESS_BR 2 | #define COMPRESS_BR 2 | ||||||
| #define COMPRESS 3 | #define COMPRESS 3 | ||||||
|  | #define COMPRESS_UTF8 4 | ||||||
|  | #define COMPRESS_WOFF 8 | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     int mode; |     int mode; | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|  |  | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "config.h" | #include "config.h" | ||||||
|  | #include "utils.h" | ||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| @@ -41,7 +42,7 @@ int config_load(const char *filename) { | |||||||
|         if (comment != NULL) comment[0] = 0; |         if (comment != NULL) comment[0] = 0; | ||||||
|  |  | ||||||
|         unsigned long len = strlen(ptr); |         unsigned long len = strlen(ptr); | ||||||
|         char *end_ptr = ptr + len - 1; |         char *end_ptr = (len > 0) ? ptr + len - 1 : ptr; | ||||||
|         while (end_ptr[0] == ' ' || end_ptr[0] == '\t') { |         while (end_ptr[0] == ' ' || end_ptr[0] == '\t') { | ||||||
|             end_ptr[0] = 0; |             end_ptr[0] = 0; | ||||||
|             end_ptr--; |             end_ptr--; | ||||||
| @@ -133,7 +134,7 @@ int config_load(const char *filename) { | |||||||
|                 } else { |                 } else { | ||||||
|                     hc->type = CONFIG_TYPE_REVERSE_PROXY; |                     hc->type = CONFIG_TYPE_REVERSE_PROXY; | ||||||
|                 } |                 } | ||||||
|             } else if (strcmp(ptr, "http") == 0) { |             } else if (streq(ptr, "http")) { | ||||||
|                 if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) { |                 if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) { | ||||||
|                     goto err; |                     goto err; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -141,7 +142,7 @@ int config_load(const char *filename) { | |||||||
|                     hc->proxy.enc = 0; |                     hc->proxy.enc = 0; | ||||||
|                 } |                 } | ||||||
|                 continue; |                 continue; | ||||||
|             } else if (strcmp(ptr, "https") == 0) { |             } else if (streq(ptr, "https")) { | ||||||
|                 if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) { |                 if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) { | ||||||
|                     goto err; |                     goto err; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -160,17 +161,19 @@ int config_load(const char *filename) { | |||||||
|         if (strlen(source) == 0) { |         if (strlen(source) == 0) { | ||||||
|             err: |             err: | ||||||
|             critical("Unable to parse config file (line %i)", line); |             critical("Unable to parse config file (line %i)", line); | ||||||
|  |             free(line); | ||||||
|  |             fclose(file); | ||||||
|             return -2; |             return -2; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (target != NULL) { |         if (target != NULL) { | ||||||
|             strcpy(target, source); |             strcpy(target, source); | ||||||
|         } else if (mode == 1) { |         } else if (mode == 1) { | ||||||
|             if (strcmp(source, "forbidden") == 0) { |             if (streq(source, "forbidden")) { | ||||||
|                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_FORBIDDEN; |                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_FORBIDDEN; | ||||||
|             } else if (strcmp(source, "info") == 0) { |             } else if (streq(source, "info")) { | ||||||
|                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_INFO; |                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_INFO; | ||||||
|             } else if (strcmp(source, "list") == 0) { |             } else if (streq(source, "list")) { | ||||||
|                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_LIST; |                 config.hosts[i - 1].local.dir_mode = URI_DIR_MODE_LIST; | ||||||
|             } else { |             } else { | ||||||
|                 goto err; |                 goto err; | ||||||
| @@ -181,6 +184,7 @@ int config_load(const char *filename) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(line); |     free(line); | ||||||
|  |     fclose(file); | ||||||
|  |  | ||||||
|     for (int k = 0; k < i; k++) { |     for (int k = 0; k < i; k++) { | ||||||
|         host_config_t *hc = &config.hosts[k]; |         host_config_t *hc = &config.hosts[k]; | ||||||
| @@ -193,7 +197,7 @@ int config_load(const char *filename) { | |||||||
|         if (hc->cert_name[0] == 0) goto err2; |         if (hc->cert_name[0] == 0) goto err2; | ||||||
|         int found = 0; |         int found = 0; | ||||||
|         for (int m = 0; m < j; m++) { |         for (int m = 0; m < j; m++) { | ||||||
|             if (strcmp(config.certs[m].name, hc->cert_name) == 0) { |             if (streq(config.certs[m].name, hc->cert_name)) { | ||||||
|                 hc->cert = m; |                 hc->cert = m; | ||||||
|                 found = 1; |                 found = 1; | ||||||
|                 break; |                 break; | ||||||
| @@ -213,7 +217,7 @@ host_config_t *get_host_config(const char *host) { | |||||||
|     for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { |     for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { | ||||||
|         host_config_t *hc = &config.hosts[i]; |         host_config_t *hc = &config.hosts[i]; | ||||||
|         if (hc->type == CONFIG_TYPE_UNSET) break; |         if (hc->type == CONFIG_TYPE_UNSET) break; | ||||||
|         if (strcmp(hc->name, host) == 0) return hc; |         if (streq(hc->name, host)) return hc; | ||||||
|         if (hc->name[0] == '*' && hc->name[1] == '.') { |         if (hc->name[0] == '*' && hc->name[1] == '.') { | ||||||
|             const char *pos = strstr(host, hc->name + 1); |             const char *pos = strstr(host, hc->name + 1); | ||||||
|             if (pos != NULL && strlen(pos) == strlen(hc->name + 1)) return hc; |             if (pos != NULL && strlen(pos) == strlen(hc->name + 1)) return hc; | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								src/lib/error.c
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								src/lib/error.c
									
									
									
									
									
								
							| @@ -7,58 +7,24 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "error.h" | #include "error.h" | ||||||
| #include "http.h" | #include "../logger.h" | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <openssl/ssl.h> | #include <string.h> | ||||||
| #include <openssl/err.h> |  | ||||||
| #include <maxminddb.h> |  | ||||||
|  |  | ||||||
| static const char *error_ssl_strerror(int err) { | extern const char *sock_error_str(unsigned long err); | ||||||
|     switch (err) { | extern const char *http_error_str(int err); | ||||||
|         case SSL_ERROR_ZERO_RETURN: | extern const char *MMDB_strerror(int err); | ||||||
|             return "closed"; | extern const char *ERR_reason_error_string(unsigned long err); | ||||||
|         case SSL_ERROR_WANT_READ: |  | ||||||
|             return "want read"; | static int error_compress(unsigned long err) { | ||||||
|         case SSL_ERROR_WANT_WRITE: |     int comp = ((int) err & 0xFFFF) | (((int) err >> 8) & 0xFF0000); | ||||||
|             return "want write"; |     if (err & 0xFF0000) warning("Lossy error code compression! (%08lX -> %08X)", err, comp); | ||||||
|         case SSL_ERROR_WANT_CONNECT: |     return comp; | ||||||
|             return "want connect"; |  | ||||||
|         case SSL_ERROR_WANT_ACCEPT: |  | ||||||
|             return "want accept"; |  | ||||||
|         case SSL_ERROR_WANT_X509_LOOKUP: |  | ||||||
|             return "want x509 lookup"; |  | ||||||
|         case SSL_ERROR_WANT_ASYNC: |  | ||||||
|             return "want async"; |  | ||||||
|         case SSL_ERROR_WANT_ASYNC_JOB: |  | ||||||
|             return "want async job"; |  | ||||||
|         case SSL_ERROR_WANT_CLIENT_HELLO_CB: |  | ||||||
|             return "want client hello callback"; |  | ||||||
|         case SSL_ERROR_WANT_RETRY_VERIFY: |  | ||||||
|             return "want retry verify"; |  | ||||||
|         case SSL_ERROR_SSL: |  | ||||||
|             return ERR_reason_error_string(ERR_get_error()); |  | ||||||
|         default: |  | ||||||
|             return "unknown error"; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const char *error_http_strerror(int err) { | static unsigned long error_decompress(int err) { | ||||||
|     switch (err) { |     return (err & 0xFFFF) | ((err << 8) & 0xFF000000); | ||||||
|         case HTTP_ERROR_TOO_MANY_HEADER_FIELDS: |  | ||||||
|             return "too many header fields"; |  | ||||||
|         case HTTP_ERROR_EOH_NOT_FOUND: |  | ||||||
|             return "end of http header not found"; |  | ||||||
|         case HTTP_ERROR_HEADER_MALFORMED: |  | ||||||
|             return "http header malformed"; |  | ||||||
|         case HTTP_ERROR_INVALID_VERSION: |  | ||||||
|             return "invalid http version"; |  | ||||||
|         case HTTP_ERROR_URI_TOO_LONG: |  | ||||||
|             return "uri too long"; |  | ||||||
|         case HTTP_ERROR_GENERAL: |  | ||||||
|         default: |  | ||||||
|             return "unknown error"; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const char *error_str(int err_no, char *buf, int buf_len) { | const char *error_str(int err_no, char *buf, int buf_len) { | ||||||
| @@ -67,50 +33,37 @@ const char *error_str(int err_no, char *buf, int buf_len) { | |||||||
|     int e = err_no & 0x00FFFFFF; |     int e = err_no & 0x00FFFFFF; | ||||||
|     if (mode == 0x00) { |     if (mode == 0x00) { | ||||||
|         // normal |         // normal | ||||||
|         strerror_r(e, buf, buf_len); |         return strerror_r(e, buf, buf_len); | ||||||
|         return buf; |  | ||||||
|     } else if (mode == 0x01) { |     } else if (mode == 0x01) { | ||||||
|         // ssl |         // ssl | ||||||
|         return error_ssl_strerror(e); |         return sock_error_str(error_decompress(e)); | ||||||
|     } else if (mode == 0x02) { |     } else if (mode == 0x02) { | ||||||
|  |         // ssl err | ||||||
|  |         return ERR_reason_error_string(error_decompress(e)); | ||||||
|  |     } else if (mode == 0x03) { | ||||||
|         // mmdb |         // mmdb | ||||||
|         return MMDB_strerror(e); |         return MMDB_strerror(e); | ||||||
|     } else if (mode == 0x03) { |     } else if (mode == 0x04) { | ||||||
|         // http |         // http | ||||||
|         return error_http_strerror(e); |         return http_error_str(e); | ||||||
|     } |     } | ||||||
|     return buf; |     return buf; | ||||||
| } | } | ||||||
|  |  | ||||||
| void error_ssl(int err) { | void error_ssl(unsigned long err) { | ||||||
|     if (err == SSL_ERROR_NONE) { |     errno = 0x01000000 | error_compress(err); | ||||||
|         errno = 0; |  | ||||||
|     } else if (err == SSL_ERROR_SYSCALL) { |  | ||||||
|         // errno already set |  | ||||||
|     } else { |  | ||||||
|         errno = 0x01000000 | err; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void error_ssl_err(unsigned long err) { | ||||||
|  |     errno = 0x02000000 | error_compress(err); | ||||||
| } | } | ||||||
|  |  | ||||||
| void error_mmdb(int err) { | void error_mmdb(int err) { | ||||||
|     if (err == MMDB_SUCCESS) { |  | ||||||
|         errno = 0; |  | ||||||
|     } else if (err == MMDB_IO_ERROR) { |  | ||||||
|         // errno already set |  | ||||||
|     } else { |  | ||||||
|         errno = 0x02000000 | err; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int error_http(int err) { |  | ||||||
|     if (err == 0) { |  | ||||||
|         errno = 0; |  | ||||||
|     } else if (err == HTTP_ERROR_SYSCALL) { |  | ||||||
|         // errno already set |  | ||||||
|     } else { |  | ||||||
|     errno = 0x03000000 | err; |     errno = 0x03000000 | err; | ||||||
| } | } | ||||||
|     return -1; |  | ||||||
|  | void error_http(int err) { | ||||||
|  |     errno = 0x04000000 | err; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int error_get(unsigned char prefix) { | static int error_get(unsigned char prefix) { | ||||||
| @@ -125,10 +78,14 @@ int error_get_ssl() { | |||||||
|     return error_get(0x01); |     return error_get(0x01); | ||||||
| } | } | ||||||
|  |  | ||||||
| int error_get_mmdb() { | int error_get_ssl_err() { | ||||||
|     return error_get(0x02); |     return error_get(0x02); | ||||||
| } | } | ||||||
|  |  | ||||||
| int error_get_http() { | int error_get_mmdb() { | ||||||
|     return error_get(0x03); |     return error_get(0x03); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int error_get_http() { | ||||||
|  |     return error_get(0x04); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -11,11 +11,13 @@ | |||||||
|  |  | ||||||
| const char *error_str(int err_no, char *buf, int buf_len); | const char *error_str(int err_no, char *buf, int buf_len); | ||||||
|  |  | ||||||
| void error_ssl(int err); | void error_ssl(unsigned long err); | ||||||
|  |  | ||||||
|  | void error_ssl_err(unsigned long err); | ||||||
|  |  | ||||||
| void error_mmdb(int err); | void error_mmdb(int err); | ||||||
|  |  | ||||||
| int error_http(int err); | void error_http(int err); | ||||||
|  |  | ||||||
| int error_get_sys(); | int error_get_sys(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,9 +6,9 @@ | |||||||
|  * @date 2020-12-26 |  * @date 2020-12-26 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include "../defs.h" | ||||||
| #include "fastcgi.h" | #include "fastcgi.h" | ||||||
| #include "utils.h" | #include "utils.h" | ||||||
| #include "compress.h" |  | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "list.h" | #include "list.h" | ||||||
|  |  | ||||||
| @@ -16,42 +16,63 @@ | |||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|  | // TODO use pipes for stdin, stdout, stderr in FastCGI | ||||||
|  |  | ||||||
| char *fastcgi_add_param(char *buf, const char *key, const char *value) { | char *fastcgi_add_param(char *buf, const char *key, const char *value) { | ||||||
|     char *ptr = buf; |     char *ptr = buf; | ||||||
|     unsigned long key_len = strlen(key); |     unsigned long key_len = strlen(key); | ||||||
|     unsigned long val_len = strlen(value); |     unsigned long val_len = strlen(value); | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (key_len <= 127) { |     if (key_len <= 127) { | ||||||
|         ptr[0] = (char) (key_len & 0x7F); |         ptr[0] = (char) (key_len & 0x7F); | ||||||
|         ptr++; |         ptr++; | ||||||
|     } else { |     } else { | ||||||
|         ptr[0] = (char) (0x80 | (key_len >> 24)); |         *((int *) ptr) = htonl(0x80000000 | key_len); | ||||||
|         ptr[1] = (char) ((key_len >> 16) & 0xFF); |  | ||||||
|         ptr[2] = (char) ((key_len >> 8) & 0xFF); |  | ||||||
|         ptr[3] = (char) (key_len & 0xFF); |  | ||||||
|         ptr += 4; |         ptr += 4; | ||||||
|     } |     } | ||||||
|     if (val_len <= 127) { |     if (val_len <= 127) { | ||||||
|         ptr[0] = (char) (val_len & 0x7F); |         ptr[0] = (char) (val_len & 0x7F); | ||||||
|         ptr++; |         ptr++; | ||||||
|     } else { |     } else { | ||||||
|         ptr[0] = (char) (0x80 | (val_len >> 24)); |         *((int *) ptr) = htonl(0x80000000 | val_len); | ||||||
|         ptr[1] = (char) ((val_len >> 16) & 0xFF); |  | ||||||
|         ptr[2] = (char) ((val_len >> 8) & 0xFF); |  | ||||||
|         ptr[3] = (char) (val_len & 0xFF); |  | ||||||
|         ptr += 4; |         ptr += 4; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     strcpy(ptr, key); |     memcpy(ptr, key, key_len); | ||||||
|     ptr += key_len; |     ptr += key_len; | ||||||
|     strcpy(ptr, value); |     memcpy(ptr, value, val_len); | ||||||
|     ptr += val_len; |     ptr += val_len; | ||||||
|  |  | ||||||
|     return ptr; |     return ptr; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int fastcgi_send_data(fastcgi_cnx_t *cnx, unsigned char type, unsigned short len, void *data) { | ||||||
|  |     // build header | ||||||
|  |     FCGI_Header header = { | ||||||
|  |             .version = FCGI_VERSION_1, | ||||||
|  |             .type = type, | ||||||
|  |             .requestId = htons(cnx->req_id), | ||||||
|  |             .contentLength = htons(len), | ||||||
|  |             .paddingLength = 0, | ||||||
|  |             .reserved = 0, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // send FastCGI header with MSG_MORE flag | ||||||
|  |     if (sock_send_x(&cnx->socket, &header, sizeof(header), (len != 0) ? MSG_MORE : 0) == -1) { | ||||||
|  |         error("Unable to send to FastCGI socket"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // send data (if available) | ||||||
|  |     if (sock_send_x(&cnx->socket, data, len, 0) == -1) { | ||||||
|  |         error("Unable to send to FastCGI socket"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // return bytes sent totally | ||||||
|  |     return len + (int) sizeof(header); | ||||||
|  | } | ||||||
|  |  | ||||||
| int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) { | int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) { | ||||||
|     conn->mode = mode; |     conn->mode = mode; | ||||||
|     conn->req_id = (req_num + 1) & 0xFFFF; |     conn->req_id = (req_num + 1) & 0xFFFF; | ||||||
| @@ -59,49 +80,32 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock | |||||||
|     conn->out_off = 0; |     conn->out_off = 0; | ||||||
|     conn->webroot = uri->webroot; |     conn->webroot = uri->webroot; | ||||||
|  |  | ||||||
|     int fcgi_sock = socket(AF_UNIX, SOCK_STREAM, 0); |     conn->socket.enc = 0; | ||||||
|     if (fcgi_sock < 0) { |     if ((conn->socket.socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { | ||||||
|         error("Unable to create unix socket"); |         error("Unable to create unix socket"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     conn->socket = fcgi_sock; |  | ||||||
|  |  | ||||||
|     struct sockaddr_un sock_addr = { AF_UNIX }; |     struct sockaddr_un sock_addr = { AF_UNIX }; | ||||||
|     if (conn->mode == FASTCGI_SESIMOS) { |     if (conn->mode == FASTCGI_BACKEND_PHP) { | ||||||
|         snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", SESIMOS_BACKEND_SOCKET); |         strcpy(sock_addr.sun_path, PHP_FPM_SOCKET); | ||||||
|     } else if (conn->mode == FASTCGI_PHP) { |  | ||||||
|         snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", PHP_FPM_SOCKET); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (connect(conn->socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { |     if (connect(conn->socket.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) != 0) { | ||||||
|         error("Unable to connect to unix socket of FastCGI socket"); |         error("Unable to connect to unix socket of FastCGI socket"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FCGI_Header header = { |     FCGI_BeginRequestBody begin = { | ||||||
|             .version = FCGI_VERSION_1, |             .role = htons(FCGI_RESPONDER), | ||||||
|             .requestIdB1 = conn->req_id >> 8, |             .flags = 0, | ||||||
|             .requestIdB0 = conn->req_id & 0xFF, |             .reserved = {0}, | ||||||
|             .paddingLength = 0, |  | ||||||
|             .reserved = 0 |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     header.type = FCGI_BEGIN_REQUEST; |     if (fastcgi_send_data(conn, FCGI_BEGIN_REQUEST, sizeof(begin), &begin) == -1) | ||||||
|     header.contentLengthB1 = 0; |         return -1; | ||||||
|     header.contentLengthB0 = sizeof(FCGI_BeginRequestBody); |  | ||||||
|     FCGI_BeginRequestRecord begin = { |  | ||||||
|             header, |  | ||||||
|             {.roleB1 = (FCGI_RESPONDER >> 8) & 0xFF, .roleB0 = FCGI_RESPONDER & 0xFF, .flags = 0} |  | ||||||
|     }; |  | ||||||
|     if (send(conn->socket, &begin, sizeof(begin), 0) != sizeof(begin)) { |  | ||||||
|         error("Unable to send to FastCGI socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char param_buf[4096]; |  | ||||||
|     char buf0[256]; |  | ||||||
|     char *param_ptr = param_buf + sizeof(header); |  | ||||||
|  |  | ||||||
|  |     char param_buf[4096], buf0[256], *param_ptr = param_buf; | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "REDIRECT_STATUS", "CGI"); |     param_ptr = fastcgi_add_param(param_ptr, "REDIRECT_STATUS", "CGI"); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "DOCUMENT_ROOT", uri->webroot); |     param_ptr = fastcgi_add_param(param_ptr, "DOCUMENT_ROOT", uri->webroot); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "GATEWAY_INTERFACE", "CGI/1.1"); |     param_ptr = fastcgi_add_param(param_ptr, "GATEWAY_INTERFACE", "CGI/1.1"); | ||||||
| @@ -117,13 +121,13 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock | |||||||
|     socklen_t len = sizeof(addr_storage); |     socklen_t len = sizeof(addr_storage); | ||||||
|     getsockname(client->socket, (struct sockaddr *) &addr_storage, &len); |     getsockname(client->socket, (struct sockaddr *) &addr_storage, &len); | ||||||
|     addr = (struct sockaddr_in6 *) &addr_storage; |     addr = (struct sockaddr_in6 *) &addr_storage; | ||||||
|     sprintf(buf0, "%i", addr->sin6_port); |     sprintf(buf0, "%i", ntohs(addr->sin6_port)); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "SERVER_PORT", buf0); |     param_ptr = fastcgi_add_param(param_ptr, "SERVER_PORT", buf0); | ||||||
|  |  | ||||||
|     len = sizeof(addr_storage); |     len = sizeof(addr_storage); | ||||||
|     getpeername(client->socket, (struct sockaddr *) &addr_storage, &len); |     getpeername(client->socket, (struct sockaddr *) &addr_storage, &len); | ||||||
|     addr = (struct sockaddr_in6 *) &addr_storage; |     addr = (struct sockaddr_in6 *) &addr_storage; | ||||||
|     sprintf(buf0, "%i", addr->sin6_port); |     sprintf(buf0, "%i", ntohs(addr->sin6_port)); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0); |     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", conn->r_addr); |     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", conn->r_addr); | ||||||
|     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", conn->r_host != NULL ? conn->r_host : conn->r_addr); |     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", conn->r_host != NULL ? conn->r_host : conn->r_addr); | ||||||
| @@ -153,7 +157,7 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock | |||||||
|     //    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", conn->ctx->geoip); |     //    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", conn->ctx->geoip); | ||||||
|     //} |     //} | ||||||
|  |  | ||||||
|     for (int i = 0; i < list_size(&req->hdr); i++) { |     for (int i = 0; i < list_size(req->hdr.fields); i++) { | ||||||
|         const http_field *f = &req->hdr.fields[i]; |         const http_field *f = &req->hdr.fields[i]; | ||||||
|         const char *name = http_field_get_name(f); |         const char *name = http_field_get_name(f); | ||||||
|         char *ptr = buf0; |         char *ptr = buf0; | ||||||
| @@ -173,43 +177,18 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock | |||||||
|         param_ptr = fastcgi_add_param(param_ptr, buf0, http_field_get_value(f)); |         param_ptr = fastcgi_add_param(param_ptr, buf0, http_field_get_value(f)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     unsigned short param_len = param_ptr - param_buf - sizeof(header); |     if (fastcgi_send_data(conn, FCGI_PARAMS, param_ptr - param_buf, param_buf) == -1) | ||||||
|     header.type = FCGI_PARAMS; |         return -1; | ||||||
|     header.contentLengthB1 = param_len >> 8; |  | ||||||
|     header.contentLengthB0 = param_len & 0xFF; |  | ||||||
|     memcpy(param_buf, &header, sizeof(header)); |  | ||||||
|     if (send(conn->socket, param_buf, param_len + sizeof(header), 0) != param_len + sizeof(header)) { |  | ||||||
|         error("Unable to send to FastCGI socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     header.type = FCGI_PARAMS; |     if (fastcgi_send_data(conn, FCGI_PARAMS, 0, NULL) == -1) | ||||||
|     header.contentLengthB1 = 0; |         return -1; | ||||||
|     header.contentLengthB0 = 0; |  | ||||||
|     if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) { |  | ||||||
|         error("Unable to send to FastCGI socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int fastcgi_close_stdin(fastcgi_cnx_t *conn) { | int fastcgi_close_stdin(fastcgi_cnx_t *conn) { | ||||||
|     FCGI_Header header = { |     if (fastcgi_send_data(conn, FCGI_STDIN, 0, NULL) == -1) | ||||||
|             .version = FCGI_VERSION_1, |         return -1; | ||||||
|             .type = FCGI_STDIN, |  | ||||||
|             .requestIdB1 = conn->req_id >> 8, |  | ||||||
|             .requestIdB0 = conn->req_id & 0xFF, |  | ||||||
|             .contentLengthB1 = 0, |  | ||||||
|             .contentLengthB0 = 0, |  | ||||||
|             .paddingLength = 0, |  | ||||||
|             .reserved = 0 |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) { |  | ||||||
|         error("Unable to send to FastCGI socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -220,8 +199,8 @@ int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, c | |||||||
|     memcpy(msg_str, msg, msg_len); |     memcpy(msg_str, msg, msg_len); | ||||||
|     msg_str[msg_len] = 0; |     msg_str[msg_len] = 0; | ||||||
|     char *ptr1 = NULL; |     char *ptr1 = NULL; | ||||||
|     int len; |     int len, err = 0; | ||||||
|     int err = 0; |  | ||||||
|     // FIXME *msg is part of a stream, handle fragmented lines |     // FIXME *msg is part of a stream, handle fragmented lines | ||||||
|     while (1) { |     while (1) { | ||||||
|         log_lvl_t msg_type = LOG_INFO; |         log_lvl_t msg_type = LOG_INFO; | ||||||
| @@ -237,16 +216,16 @@ int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, c | |||||||
|             goto next; |             goto next; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (len >= 14 && strncmp(ptr0, "PHP Warning:  ", 14) == 0) { |         if (len >= 14 && strstarts(ptr0, "PHP Warning:  ")) { | ||||||
|             msg_type = LOG_WARNING; |             msg_type = LOG_WARNING; | ||||||
|             msg_pre_len = 14; |             msg_pre_len = 14; | ||||||
|         } else if (len >= 18 && strncmp(ptr0, "PHP Fatal error:  ", 18) == 0) { |         } else if (len >= 18 && strstarts(ptr0, "PHP Fatal error:  ")) { | ||||||
|             msg_type = LOG_ERROR; |             msg_type = LOG_ERROR; | ||||||
|             msg_pre_len = 18; |             msg_pre_len = 18; | ||||||
|         } else if (len >= 18 && strncmp(ptr0, "PHP Parse error:  ", 18) == 0) { |         } else if (len >= 18 && strstarts(ptr0, "PHP Parse error:  ")) { | ||||||
|             msg_type = LOG_ERROR; |             msg_type = LOG_ERROR; | ||||||
|             msg_pre_len = 18; |             msg_pre_len = 18; | ||||||
|         } else if (len >= 18 && strncmp(ptr0, "PHP Notice:  ", 13) == 0) { |         } else if (len >= 18 && strstarts(ptr0, "PHP Notice:  ")) { | ||||||
|             msg_type = LOG_NOTICE; |             msg_type = LOG_NOTICE; | ||||||
|             msg_pre_len = 13; |             msg_pre_len = 13; | ||||||
|         } |         } | ||||||
| @@ -289,29 +268,16 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) { | |||||||
|     int err = 0; |     int err = 0; | ||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         ret = recv(conn->socket, &header, sizeof(header), 0); |         if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) { | ||||||
|         if (ret < 0) { |  | ||||||
|             res->status = http_get_status(500); |  | ||||||
|             sprintf(err_msg, "Unable to communicate with FastCGI socket."); |  | ||||||
|             error("Unable to receive from FastCGI socket"); |  | ||||||
|             return 1; |  | ||||||
|         } else if (ret != sizeof(header)) { |  | ||||||
|             res->status = http_get_status(500); |             res->status = http_get_status(500); | ||||||
|             sprintf(err_msg, "Unable to communicate with FastCGI socket."); |             sprintf(err_msg, "Unable to communicate with FastCGI socket."); | ||||||
|             error("Unable to receive from FastCGI socket"); |             error("Unable to receive from FastCGI socket"); | ||||||
|             return 1; |             return 1; | ||||||
|         } |         } | ||||||
|         req_id = (header.requestIdB1 << 8) | header.requestIdB0; |         req_id = ntohs(header.requestId); | ||||||
|         content_len = (header.contentLengthB1 << 8) | header.contentLengthB0; |         content_len = ntohs(header.contentLength); | ||||||
|         content = malloc(content_len + header.paddingLength); |         content = malloc(content_len + header.paddingLength); | ||||||
|         ret = recv(conn->socket, content, content_len + header.paddingLength, 0); |         if (sock_recv_x(&conn->socket, content, content_len + header.paddingLength, 0) == -1) { | ||||||
|         if (ret < 0) { |  | ||||||
|             res->status = http_get_status(500); |  | ||||||
|             sprintf(err_msg, "Unable to communicate with FastCGI socket."); |  | ||||||
|             error("Unable to receive from FastCGI socket"); |  | ||||||
|             free(content); |  | ||||||
|             return 1; |  | ||||||
|         } else if (ret != (content_len + header.paddingLength)) { |  | ||||||
|             res->status = http_get_status(500); |             res->status = http_get_status(500); | ||||||
|             sprintf(err_msg, "Unable to communicate with FastCGI socket."); |             sprintf(err_msg, "Unable to communicate with FastCGI socket."); | ||||||
|             error("Unable to receive from FastCGI socket"); |             error("Unable to receive from FastCGI socket"); | ||||||
| @@ -325,21 +291,18 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) { | |||||||
|  |  | ||||||
|         if (header.type == FCGI_END_REQUEST) { |         if (header.type == FCGI_END_REQUEST) { | ||||||
|             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; |             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; | ||||||
|             int app_status = (body->appStatusB3 << 24) | (body->appStatusB2 << 16) | (body->appStatusB1 << 8) | |             int app_status = ntohl(body->appStatus); | ||||||
|                              body->appStatusB0; |  | ||||||
|             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { |             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { | ||||||
|                 error("FastCGI protocol error: %i", body->protocolStatus); |                 error("FastCGI protocol error: %i", body->protocolStatus); | ||||||
|             } |             } | ||||||
|             if (app_status != 0) { |             if (app_status != 0) { | ||||||
|                 error("FastCGI app terminated with exit code %i", app_status); |                 error("FastCGI app terminated with exit code %i", app_status); | ||||||
|             } |             } | ||||||
|             close(conn->socket); |             sock_close(&conn->socket); | ||||||
|             conn->socket = 0; |  | ||||||
|             free(content); |             free(content); | ||||||
|             return 1; |             return 1; | ||||||
|         } else if (header.type == FCGI_STDERR) { |         } else if (header.type == FCGI_STDERR) { | ||||||
|             // TODO implement Sesimos backend error handling |             if (conn->mode == FASTCGI_BACKEND_PHP) { | ||||||
|             if (conn->mode == FASTCGI_PHP) { |  | ||||||
|                 err = err || fastcgi_php_error(conn, content, content_len, err_msg); |                 err = err || fastcgi_php_error(conn, content, content_len, err_msg); | ||||||
|             } |             } | ||||||
|         } else if (header.type == FCGI_STDOUT) { |         } else if (header.type == FCGI_STDOUT) { | ||||||
| @@ -396,27 +359,9 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) { | |||||||
| int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) { | int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) { | ||||||
|     FCGI_Header header; |     FCGI_Header header; | ||||||
|     long ret; |     long ret; | ||||||
|     char buf0[256]; |     char buf0[256], *content, *ptr; | ||||||
|     int len; |     int len; | ||||||
|     char *content, *ptr; |     unsigned short content_len; | ||||||
|     unsigned short req_id, content_len; |  | ||||||
|     char comp_out[4096]; |  | ||||||
|     int finish_comp = 0; |  | ||||||
|  |  | ||||||
|     compress_ctx comp_ctx; |  | ||||||
|     if (flags & FASTCGI_COMPRESS_BR) { |  | ||||||
|         flags &= ~FASTCGI_COMPRESS_GZ; |  | ||||||
|         if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { |  | ||||||
|             error("Unable to init brotli"); |  | ||||||
|             flags &= ~FASTCGI_COMPRESS_BR; |  | ||||||
|         } |  | ||||||
|     } else if (flags & FASTCGI_COMPRESS_GZ) { |  | ||||||
|         flags &= ~FASTCGI_COMPRESS_BR; |  | ||||||
|         if (compress_init(&comp_ctx, COMPRESS_GZ) != 0) { |  | ||||||
|             error("Unable to init gzip"); |  | ||||||
|             flags &= ~FASTCGI_COMPRESS_GZ; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (conn->out_buf != NULL && conn->out_len > conn->out_off) { |     if (conn->out_buf != NULL && conn->out_len > conn->out_off) { | ||||||
|         content = conn->out_buf; |         content = conn->out_buf; | ||||||
| @@ -426,23 +371,18 @@ int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         ret = recv(conn->socket, &header, sizeof(header), 0); |         if ((sock_recv_x(&conn->socket, &header, sizeof(header), 0)) == -1) { | ||||||
|         if (ret < 0) { |  | ||||||
|             error("Unable to receive from FastCGI socket"); |             error("Unable to receive from FastCGI socket"); | ||||||
|             return -1; |             return -1; | ||||||
|         } else if (ret != sizeof(header)) { |  | ||||||
|             error("Unable to receive from FastCGI socket: received len (%li) != header len (%li)", ret, sizeof(header)); |  | ||||||
|             return -1; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         req_id = (header.requestIdB1 << 8) | header.requestIdB0; |         content_len = ntohs(header.contentLength); | ||||||
|         content_len = (header.contentLengthB1 << 8) | header.contentLengthB0; |  | ||||||
|         content = malloc(content_len + header.paddingLength); |         content = malloc(content_len + header.paddingLength); | ||||||
|         ptr = content; |         ptr = content; | ||||||
|  |  | ||||||
|         long rcv_len = 0; |         long rcv_len = 0; | ||||||
|         while (rcv_len < content_len + header.paddingLength) { |         while (rcv_len < content_len + header.paddingLength) { | ||||||
|             ret = recv(conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); |             ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 error("Unable to receive from FastCGI socket"); |                 error("Unable to receive from FastCGI socket"); | ||||||
|                 free(content); |                 free(content); | ||||||
| @@ -453,57 +393,33 @@ int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) { | |||||||
|  |  | ||||||
|         if (header.type == FCGI_END_REQUEST) { |         if (header.type == FCGI_END_REQUEST) { | ||||||
|             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; |             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; | ||||||
|             int app_status = (body->appStatusB3 << 24) | (body->appStatusB2 << 16) | (body->appStatusB1 << 8) | |             int app_status = ntohl(body->appStatus); | ||||||
|                              body->appStatusB0; |  | ||||||
|             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { |             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { | ||||||
|                 error("FastCGI protocol error: %i", body->protocolStatus); |                 error("FastCGI protocol error: %i", body->protocolStatus); | ||||||
|             } |             } | ||||||
|             if (app_status != 0) { |             if (app_status != 0) { | ||||||
|                 error("FastCGI app terminated with exit code %i", app_status); |                 error("FastCGI app terminated with exit code %i", app_status); | ||||||
|             } |             } | ||||||
|             close(conn->socket); |             sock_close(&conn->socket); | ||||||
|             conn->socket = 0; |  | ||||||
|             free(content); |             free(content); | ||||||
|  |  | ||||||
|             if (flags & FASTCGI_COMPRESS) { |  | ||||||
|                 finish_comp = 1; |  | ||||||
|                 content_len = 0; |  | ||||||
|                 goto out; |  | ||||||
|                 finish: |  | ||||||
|                 compress_free(&comp_ctx); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (flags & FASTCGI_CHUNKED) { |             if (flags & FASTCGI_CHUNKED) { | ||||||
|                 sock_send(client, "0\r\n\r\n", 5, 0); |                 sock_send_x(client, "0\r\n\r\n", 5, 0); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return 0; |             return 0; | ||||||
|         } else if (header.type == FCGI_STDERR) { |         } else if (header.type == FCGI_STDERR) { | ||||||
|             // TODO implement Sesimos backend error handling |             if (conn->mode == FASTCGI_BACKEND_PHP) { | ||||||
|             if (conn->mode == FASTCGI_PHP) { |  | ||||||
|                 fastcgi_php_error(conn, content, content_len, buf0); |                 fastcgi_php_error(conn, content, content_len, buf0); | ||||||
|             } |             } | ||||||
|         } else if (header.type == FCGI_STDOUT) { |         } else if (header.type == FCGI_STDOUT) { | ||||||
|             unsigned long avail_in, avail_out; |  | ||||||
|             out: |             out: | ||||||
|             avail_in = content_len; |             if (content_len != 0) { | ||||||
|             char *next_in = ptr; |                 len = sprintf(buf0, "%X\r\n", content_len); | ||||||
|             do { |                 if (flags & FASTCGI_CHUNKED) sock_send_x(client, buf0, len, 0); | ||||||
|                 int buf_len = content_len; |                 sock_send_x(client, ptr, content_len, 0); | ||||||
|                 if (flags & FASTCGI_COMPRESS) { |                 if (flags & FASTCGI_CHUNKED) sock_send_x(client, "\r\n", 2, 0); | ||||||
|                     avail_out = sizeof(comp_out); |  | ||||||
|                     compress_compress(&comp_ctx, next_in + content_len - avail_in, &avail_in, comp_out, &avail_out, finish_comp); |  | ||||||
|                     ptr = comp_out; |  | ||||||
|                     buf_len = (int) (sizeof(comp_out) - avail_out); |  | ||||||
|             } |             } | ||||||
|                 if (buf_len != 0) { |  | ||||||
|                     len = sprintf(buf0, "%X\r\n", buf_len); |  | ||||||
|                     if (flags & FASTCGI_CHUNKED) sock_send(client, buf0, len, 0); |  | ||||||
|                     sock_send(client, ptr, buf_len, 0); |  | ||||||
|                     if (flags & FASTCGI_CHUNKED) sock_send(client, "\r\n", 2, 0); |  | ||||||
|                 } |  | ||||||
|             } while ((flags & FASTCGI_COMPRESS) && (avail_in != 0 || avail_out != sizeof(comp_out))); |  | ||||||
|             if (finish_comp) goto finish; |  | ||||||
|         } else { |         } else { | ||||||
|             error("Unknown FastCGI type: %i", header.type); |             error("Unknown FastCGI type: %i", header.type); | ||||||
|         } |         } | ||||||
| @@ -516,29 +432,24 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) { | |||||||
|     long ret; |     long ret; | ||||||
|     char buf0[256]; |     char buf0[256]; | ||||||
|     char *content, *ptr = buf; |     char *content, *ptr = buf; | ||||||
|     unsigned short req_id, content_len; |     unsigned short content_len; | ||||||
|  |  | ||||||
|     if (conn->out_buf != NULL && conn->out_len > conn->out_off) { |     if (conn->out_buf != NULL && conn->out_len > conn->out_off) { | ||||||
|         ptr += snprintf(ptr, len, "%.*s", conn->out_len - conn->out_off, conn->out_buf + conn->out_off); |         ptr += snprintf(ptr, len, "%.*s", conn->out_len - conn->out_off, conn->out_buf + conn->out_off); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         ret = recv(conn->socket, &header, sizeof(header), 0); |         if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) { | ||||||
|         if (ret < 0) { |  | ||||||
|             error("Unable to receive from FastCGI socket"); |             error("Unable to receive from FastCGI socket"); | ||||||
|             return -1; |             return -1; | ||||||
|         } else if (ret != sizeof(header)) { |  | ||||||
|             error("Unable to receive from FastCGI socket: received len (%li) != header len (%li)", ret, sizeof(header)); |  | ||||||
|             return -1; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         req_id = (header.requestIdB1 << 8) | header.requestIdB0; |         content_len = ntohs(header.contentLength); | ||||||
|         content_len = (header.contentLengthB1 << 8) | header.contentLengthB0; |  | ||||||
|         content = malloc(content_len + header.paddingLength); |         content = malloc(content_len + header.paddingLength); | ||||||
|  |  | ||||||
|         long rcv_len = 0; |         long rcv_len = 0; | ||||||
|         while (rcv_len < content_len + header.paddingLength) { |         while (rcv_len < content_len + header.paddingLength) { | ||||||
|             ret = recv(conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); |             ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 error("Unable to receive from FastCGI socket"); |                 error("Unable to receive from FastCGI socket"); | ||||||
|                 free(content); |                 free(content); | ||||||
| @@ -549,22 +460,19 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) { | |||||||
|  |  | ||||||
|         if (header.type == FCGI_END_REQUEST) { |         if (header.type == FCGI_END_REQUEST) { | ||||||
|             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; |             FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; | ||||||
|             int app_status = (body->appStatusB3 << 24) | (body->appStatusB2 << 16) | (body->appStatusB1 << 8) | |             int app_status = ntohl(body->appStatus); | ||||||
|                              body->appStatusB0; |  | ||||||
|             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { |             if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { | ||||||
|                 error("FastCGI protocol error: %i", body->protocolStatus); |                 error("FastCGI protocol error: %i", body->protocolStatus); | ||||||
|             } |             } | ||||||
|             if (app_status != 0) { |             if (app_status != 0) { | ||||||
|                 error("FastCGI app terminated with exit code %i", app_status); |                 error("FastCGI app terminated with exit code %i", app_status); | ||||||
|             } |             } | ||||||
|             close(conn->socket); |             sock_close(&conn->socket); | ||||||
|             conn->socket = 0; |  | ||||||
|             free(content); |             free(content); | ||||||
|  |  | ||||||
|             return 0; |             return 0; | ||||||
|         } else if (header.type == FCGI_STDERR) { |         } else if (header.type == FCGI_STDERR) { | ||||||
|             // TODO implement Sesimos backend error handling |             if (conn->mode == FASTCGI_BACKEND_PHP) { | ||||||
|             if (conn->mode == FASTCGI_PHP) { |  | ||||||
|                 fastcgi_php_error(conn, content, content_len, buf0); |                 fastcgi_php_error(conn, content, content_len, buf0); | ||||||
|             } |             } | ||||||
|         } else if (header.type == FCGI_STDOUT) { |         } else if (header.type == FCGI_STDOUT) { | ||||||
| @@ -577,53 +485,31 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) { | int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) { | ||||||
|     unsigned long rcv_len = 0; |  | ||||||
|     char *buf[16384]; |     char *buf[16384]; | ||||||
|     long ret; |  | ||||||
|     FCGI_Header header = { |  | ||||||
|             .version = FCGI_VERSION_1, |  | ||||||
|             .type = FCGI_STDIN, |  | ||||||
|             .requestIdB1 = conn->req_id >> 8, |  | ||||||
|             .requestIdB0 = conn->req_id & 0xFF, |  | ||||||
|             .contentLengthB1 = 0, |  | ||||||
|             .contentLengthB0 = 0, |  | ||||||
|             .paddingLength = 0, |  | ||||||
|             .reserved = 0 |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     while (rcv_len < len) { |     for (long to_send = (long) len, ret; to_send > 0; to_send -= ret) { | ||||||
|         ret = sock_recv(client, buf, sizeof(buf), 0); |         if ((ret = sock_recv(client, buf, (to_send > sizeof(buf)) ? sizeof(buf) : to_send, 0)) <= 0) { | ||||||
|         if (ret <= 0) { |  | ||||||
|             error("Unable to receive"); |             error("Unable to receive"); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         rcv_len += ret; |         if (fastcgi_send_data(conn, FCGI_STDIN, ret, buf) == -1) | ||||||
|         header.contentLengthB1 = (ret >> 8) & 0xFF; |             return -1; | ||||||
|         header.contentLengthB0 = ret & 0xFF; |  | ||||||
|         if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) goto err; |  | ||||||
|         if (send(conn->socket, buf, ret, 0) != ret) { |  | ||||||
|             err: |  | ||||||
|             error("Unable to send to FastCGI socket"); |  | ||||||
|             return -2; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) { | int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) { | ||||||
|     long ret; |     for (long ret;;) { | ||||||
|     unsigned long next_len; |         if ((ret = sock_get_chunk_header(client)) < 0) { | ||||||
|  |             return (int) ret; | ||||||
|  |         } else if (ret == 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|     while (1) { |         if ((ret = fastcgi_receive(conn, client, ret)) < 0) | ||||||
|         ret = sock_get_chunk_header(client); |             return (int) ret; | ||||||
|         if (ret < 0) return (int) ret; |  | ||||||
|  |  | ||||||
|         next_len = ret; |  | ||||||
|         if (next_len <= 0) break; |  | ||||||
|  |  | ||||||
|         ret = fastcgi_receive(conn, client, next_len); |  | ||||||
|         if (ret < 0) return (int) ret; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
| @@ -14,28 +14,21 @@ | |||||||
| #include "uri.h" | #include "uri.h" | ||||||
|  |  | ||||||
| #define FASTCGI_CHUNKED 1 | #define FASTCGI_CHUNKED 1 | ||||||
| #define FASTCGI_COMPRESS_GZ 2 |  | ||||||
| #define FASTCGI_COMPRESS_BR 4 |  | ||||||
| #define FASTCGI_COMPRESS 6 |  | ||||||
| #define FASTCGI_COMPRESS_HOLD 8 |  | ||||||
|  |  | ||||||
| #define FASTCGI_PHP 1 | #define FASTCGI_BACKEND_PHP 1 | ||||||
| #define FASTCGI_SESIMOS 2 |  | ||||||
|  |  | ||||||
| #ifndef PHP_FPM_SOCKET | #ifndef PHP_FPM_SOCKET | ||||||
| #   define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock" | #   define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define SESIMOS_BACKEND_SOCKET "/var/run/sesimos/backend.sock" |  | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     int mode; |     int mode; | ||||||
|     int socket; |     sock socket; | ||||||
|  |     int in, out, err; | ||||||
|     unsigned short req_id; |     unsigned short req_id; | ||||||
|     char *out_buf; |  | ||||||
|     const char *webroot; |     const char *webroot; | ||||||
|     unsigned short out_len; |     char *out_buf; | ||||||
|     unsigned short out_off; |     unsigned short out_len, out_off; | ||||||
|     char *r_addr; |     char *r_addr; | ||||||
|     char *r_host; |     char *r_host; | ||||||
| } fastcgi_cnx_t; | } fastcgi_cnx_t; | ||||||
|   | |||||||
| @@ -9,12 +9,24 @@ | |||||||
| #include "geoip.h" | #include "geoip.h" | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "error.h" | #include "error.h" | ||||||
|  | #include "utils.h" | ||||||
|  |  | ||||||
| #include <memory.h> | #include <memory.h> | ||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
| static MMDB_s mmdbs[GEOIP_MAX_MMDB]; | static MMDB_s mmdbs[GEOIP_MAX_MMDB]; | ||||||
|  |  | ||||||
|  | static void mmdb_error(int err) { | ||||||
|  |     if (err == MMDB_SUCCESS) { | ||||||
|  |         errno = 0; | ||||||
|  |     } else if (err == MMDB_IO_ERROR) { | ||||||
|  |         // errno already set | ||||||
|  |     } else { | ||||||
|  |         error_mmdb(err); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static MMDB_entry_data_list_s *geoip_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len) { | static MMDB_entry_data_list_s *geoip_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len) { | ||||||
|     switch (list->entry_data.type) { |     switch (list->entry_data.type) { | ||||||
|         case MMDB_DATA_TYPE_MAP: |         case MMDB_DATA_TYPE_MAP: | ||||||
| @@ -91,7 +103,7 @@ int geoip_init(const char *directory) { | |||||||
|     struct dirent *entry; |     struct dirent *entry; | ||||||
|     int i = 0, status; |     int i = 0, status; | ||||||
|     while ((entry = readdir(geoip_dir)) != NULL) { |     while ((entry = readdir(geoip_dir)) != NULL) { | ||||||
|         if (strcmp(entry->d_name + strlen(entry->d_name) - 5, ".mmdb") != 0) |         if (!strends(entry->d_name, ".mmdb")) | ||||||
|             continue; |             continue; | ||||||
|  |  | ||||||
|         if (i >= GEOIP_MAX_MMDB) { |         if (i >= GEOIP_MAX_MMDB) { | ||||||
| @@ -102,7 +114,7 @@ int geoip_init(const char *directory) { | |||||||
|  |  | ||||||
|         sprintf(buf, "%s/%s", directory, entry->d_name); |         sprintf(buf, "%s/%s", directory, entry->d_name); | ||||||
|         if ((status = MMDB_open(buf, 0, &mmdbs[i])) != MMDB_SUCCESS) { |         if ((status = MMDB_open(buf, 0, &mmdbs[i])) != MMDB_SUCCESS) { | ||||||
|             error_mmdb(status); |             mmdb_error(status); | ||||||
|             critical("Unable to initialize geoip: Unable to open .mmdb file"); |             critical("Unable to initialize geoip: Unable to open .mmdb file"); | ||||||
|             closedir(geoip_dir); |             closedir(geoip_dir); | ||||||
|             return 1; |             return 1; | ||||||
| @@ -166,7 +178,7 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) { | |||||||
|         int mmdb_res; |         int mmdb_res; | ||||||
|         MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdbs[i], addr, &mmdb_res); |         MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdbs[i], addr, &mmdb_res); | ||||||
|         if (mmdb_res != MMDB_SUCCESS) { |         if (mmdb_res != MMDB_SUCCESS) { | ||||||
|             error_mmdb(mmdb_res); |             mmdb_error(mmdb_res); | ||||||
|             error("Unable to lookup geoip info"); |             error("Unable to lookup geoip info"); | ||||||
|             continue; |             continue; | ||||||
|         } else if (!result.found_entry) { |         } else if (!result.found_entry) { | ||||||
| @@ -175,7 +187,7 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) { | |||||||
|  |  | ||||||
|         MMDB_entry_data_list_s *list; |         MMDB_entry_data_list_s *list; | ||||||
|         if ((mmdb_res = MMDB_get_entry_data_list(&result.entry, &list)) != MMDB_SUCCESS) { |         if ((mmdb_res = MMDB_get_entry_data_list(&result.entry, &list)) != MMDB_SUCCESS) { | ||||||
|             error_mmdb(mmdb_res); |             mmdb_error(mmdb_res); | ||||||
|             error("Unable to lookup geoip info"); |             error("Unable to lookup geoip info"); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|   | |||||||
							
								
								
									
										150
									
								
								src/lib/http.c
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								src/lib/http.c
									
									
									
									
									
								
							| @@ -13,7 +13,36 @@ | |||||||
| #include "error.h" | #include "error.h" | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
|  | static int http_error(int err) { | ||||||
|  |     if (err == 0) { | ||||||
|  |         errno = 0; | ||||||
|  |     } else if (err == HTTP_ERROR_SYSCALL) { | ||||||
|  |         // errno already set | ||||||
|  |     } else { | ||||||
|  |         error_http(err); | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *http_error_str(int err) { | ||||||
|  |     switch (err) { | ||||||
|  |         case HTTP_ERROR_TOO_MANY_HEADER_FIELDS: | ||||||
|  |             return "too many header fields"; | ||||||
|  |         case HTTP_ERROR_EOH_NOT_FOUND: | ||||||
|  |             return "end of http header not found"; | ||||||
|  |         case HTTP_ERROR_HEADER_MALFORMED: | ||||||
|  |             return "http header malformed"; | ||||||
|  |         case HTTP_ERROR_INVALID_VERSION: | ||||||
|  |             return "invalid http version"; | ||||||
|  |         case HTTP_ERROR_URI_TOO_LONG: | ||||||
|  |             return "uri too long"; | ||||||
|  |         case HTTP_ERROR_GENERAL: | ||||||
|  |         default: | ||||||
|  |             return "unknown error"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| void http_to_camel_case(char *str, int mode) { | void http_to_camel_case(char *str, int mode) { | ||||||
|     if (mode == HTTP_PRESERVE) |     if (mode == HTTP_PRESERVE) | ||||||
| @@ -88,21 +117,22 @@ void http_free_res(http_res *res) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int http_init_hdr(http_hdr *hdr) { | int http_init_hdr(http_hdr *hdr) { | ||||||
|  |     hdr->last_field_num = -1; | ||||||
|     hdr->fields = list_create(sizeof(http_field), HTTP_INIT_HEADER_FIELD_NUM); |     hdr->fields = list_create(sizeof(http_field), HTTP_INIT_HEADER_FIELD_NUM); | ||||||
|     if (hdr->fields == NULL) |     if (hdr->fields == NULL) | ||||||
|         return error_http(HTTP_ERROR_SYSCALL); |         return http_error(HTTP_ERROR_SYSCALL); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) { | int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) { | ||||||
|     if (hdr->last_field_num > list_size(hdr->fields)) |     if (hdr->last_field_num > list_size(hdr->fields)) | ||||||
|         return error_http(HTTP_ERROR_GENERAL); |         return http_error(HTTP_ERROR_GENERAL); | ||||||
|  |  | ||||||
|     char *pos1 = (char *) buf, *pos2 = (char *) end_ptr; |     char *pos1 = (char *) buf, *pos2 = (char *) end_ptr; | ||||||
|     if (buf[0] == ' ' || buf[0] == '\t') { |     if (buf[0] == ' ' || buf[0] == '\t') { | ||||||
|         if (hdr->last_field_num == -1) |         if (hdr->last_field_num == -1) | ||||||
|             return error_http(HTTP_ERROR_GENERAL); |             return http_error(HTTP_ERROR_GENERAL); | ||||||
|  |  | ||||||
|         http_field *f = &hdr->fields[(int) hdr->last_field_num]; |         http_field *f = &hdr->fields[(int) hdr->last_field_num]; | ||||||
|  |  | ||||||
| @@ -114,7 +144,7 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, | |||||||
|  |  | ||||||
|     pos1 = memchr(buf, ':', end_ptr - buf); |     pos1 = memchr(buf, ':', end_ptr - buf); | ||||||
|     if (pos1 == NULL) |     if (pos1 == NULL) | ||||||
|         return error_http(HTTP_ERROR_GENERAL); |         return http_error(HTTP_ERROR_GENERAL); | ||||||
|  |  | ||||||
|     long len1 = pos1 - buf; |     long len1 = pos1 - buf; | ||||||
|  |  | ||||||
| @@ -122,11 +152,14 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, | |||||||
|     str_trim_lws(&pos1, &pos2); |     str_trim_lws(&pos1, &pos2); | ||||||
|     long len2 = pos2 - pos1; |     long len2 = pos2 - pos1; | ||||||
|  |  | ||||||
|  |     char header_name[256]; | ||||||
|  |     sprintf(header_name, "%.*s", (int) len1, buf); | ||||||
|  |  | ||||||
|     int field_num = list_size(hdr->fields); |     int field_num = list_size(hdr->fields); | ||||||
|     int found = http_get_header_field_num_len(hdr, buf, len1); |     int found = http_get_header_field_num(hdr, header_name); | ||||||
|     if (!(flags & HTTP_MERGE_FIELDS) || found == -1) { |     if (!(flags & HTTP_MERGE_FIELDS) || found == -1) { | ||||||
|         if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) |         if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) | ||||||
|             return error_http(HTTP_ERROR_TOO_MANY_HEADER_FIELDS); |             return http_error(HTTP_ERROR_TOO_MANY_HEADER_FIELDS); | ||||||
|     } else { |     } else { | ||||||
|         field_num = found; |         field_num = found; | ||||||
|         http_append_to_header_field(&hdr->fields[found], ", ", 2); |         http_append_to_header_field(&hdr->fields[found], ", ", 2); | ||||||
| @@ -143,44 +176,44 @@ int http_parse_request(char *buf, http_req *req) { | |||||||
|  |  | ||||||
|     unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4; |     unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4; | ||||||
|     if (header_len <= 0) |     if (header_len <= 0) | ||||||
|         return error_http(HTTP_ERROR_EOH_NOT_FOUND); |         return http_error(HTTP_ERROR_EOH_NOT_FOUND); | ||||||
|  |  | ||||||
|     for (int i = 0; i < header_len; i++) { |     for (int i = 0; i < header_len; i++) { | ||||||
|         if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) |         if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) | ||||||
|             return error_http(HTTP_ERROR_HEADER_MALFORMED); |             return http_error(HTTP_ERROR_HEADER_MALFORMED); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ptr = buf; |     ptr = buf; | ||||||
|     while (header_len > (ptr - buf + 2)) { |     while (header_len > (ptr - buf + 2)) { | ||||||
|         pos0 = strstr(ptr, "\r\n"); |         pos0 = strstr(ptr, "\r\n"); | ||||||
|         if (pos0 == NULL) |         if (pos0 == NULL) | ||||||
|             return error_http(HTTP_ERROR_HEADER_MALFORMED); |             return http_error(HTTP_ERROR_HEADER_MALFORMED); | ||||||
|  |  | ||||||
|         if (req->version[0] == 0) { |         if (req->version[0] == 0) { | ||||||
|             pos1 = (char *) strchr(ptr, ' ') + 1; |             pos1 = (char *) strchr(ptr, ' ') + 1; | ||||||
|             if (pos1 == NULL) goto err_hdr_fmt; |             if (pos1 == NULL) goto err_hdr_fmt; | ||||||
|  |  | ||||||
|             if (pos1 - ptr - 1 >= sizeof(req->method)) |             if (pos1 - ptr - 1 >= sizeof(req->method)) | ||||||
|                 return error_http(HTTP_ERROR_HEADER_MALFORMED); |                 return http_error(HTTP_ERROR_HEADER_MALFORMED); | ||||||
|  |  | ||||||
|             for (int i = 0; i < (pos1 - ptr - 1); i++) { |             for (int i = 0; i < (pos1 - ptr - 1); i++) { | ||||||
|                 if (ptr[i] < 'A' || ptr[i] > 'Z') |                 if (ptr[i] < 'A' || ptr[i] > 'Z') | ||||||
|                     return error_http(HTTP_ERROR_HEADER_MALFORMED); |                     return http_error(HTTP_ERROR_HEADER_MALFORMED); | ||||||
|             } |             } | ||||||
|             snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr); |             snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr); | ||||||
|  |  | ||||||
|             pos2 = (char *) strchr(pos1, ' ') + 1; |             pos2 = (char *) strchr(pos1, ' ') + 1; | ||||||
|             if (pos2 == NULL) { |             if (pos2 == NULL) { | ||||||
|                 err_hdr_fmt: |                 err_hdr_fmt: | ||||||
|                 return error_http(HTTP_ERROR_HEADER_MALFORMED); |                 return http_error(HTTP_ERROR_HEADER_MALFORMED); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) |             if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) | ||||||
|                 return error_http(HTTP_ERROR_INVALID_VERSION); |                 return http_error(HTTP_ERROR_INVALID_VERSION); | ||||||
|  |  | ||||||
|             len = pos2 - pos1 - 1; |             len = pos2 - pos1 - 1; | ||||||
|             if (len >= 2048) |             if (len >= 2048) | ||||||
|                 return error_http(HTTP_ERROR_URI_TOO_LONG); |                 return http_error(HTTP_ERROR_URI_TOO_LONG); | ||||||
|  |  | ||||||
|             req->uri = malloc(len + 1); |             req->uri = malloc(len + 1); | ||||||
|             sprintf(req->uri, "%.*s", (int) len, pos1); |             sprintf(req->uri, "%.*s", (int) len, pos1); | ||||||
| @@ -196,7 +229,7 @@ int http_parse_request(char *buf, http_req *req) { | |||||||
|         return (int) header_len; |         return (int) header_len; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return error_http(HTTP_ERROR_GENERAL); |     return http_error(HTTP_ERROR_GENERAL); | ||||||
| } | } | ||||||
|  |  | ||||||
| int http_receive_request(sock *client, http_req *req) { | int http_receive_request(sock *client, http_req *req) { | ||||||
| @@ -206,7 +239,6 @@ int http_receive_request(sock *client, http_req *req) { | |||||||
|     memset(req->method, 0, sizeof(req->method)); |     memset(req->method, 0, sizeof(req->method)); | ||||||
|     memset(req->version, 0, sizeof(req->version)); |     memset(req->version, 0, sizeof(req->version)); | ||||||
|     req->uri = NULL; |     req->uri = NULL; | ||||||
|     req->hdr.last_field_num = -1; |  | ||||||
|     http_init_hdr(&req->hdr); |     http_init_hdr(&req->hdr); | ||||||
|  |  | ||||||
|     rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE - 1, MSG_PEEK); |     rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE - 1, MSG_PEEK); | ||||||
| @@ -219,29 +251,20 @@ int http_receive_request(sock *client, http_req *req) { | |||||||
|     if (header_len < 0) |     if (header_len < 0) | ||||||
|         return (int) -header_len; |         return (int) -header_len; | ||||||
|  |  | ||||||
|     rcv_len = sock_recv(client, buf, header_len, 0); |     if (sock_recv_x(client, buf, header_len, 0) == -1) | ||||||
|     if (rcv_len != header_len) |  | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| const char *http_get_header_field(const http_hdr *hdr, const char *field_name) { | const char *http_get_header_field(const http_hdr *hdr, const char *field_name) { | ||||||
|     return http_get_header_field_len(hdr, field_name, strlen(field_name)); |     int num = http_get_header_field_num(hdr, field_name); | ||||||
| } |  | ||||||
|  |  | ||||||
| const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len) { |  | ||||||
|     int num = http_get_header_field_num_len(hdr, field_name, len); |  | ||||||
|     return (num >= 0 && num < list_size(hdr->fields)) ? http_field_get_value(&hdr->fields[num]) : NULL; |     return (num >= 0 && num < list_size(hdr->fields)) ? http_field_get_value(&hdr->fields[num]) : NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| int http_get_header_field_num(const http_hdr *hdr, const char *field_name) { | int http_get_header_field_num(const http_hdr *hdr, const char *field_name) { | ||||||
|     return http_get_header_field_num_len(hdr, field_name, strlen(field_name)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len) { |  | ||||||
|     for (int i = 0; i < list_size(hdr->fields); i++) { |     for (int i = 0; i < list_size(hdr->fields); i++) { | ||||||
|         if (strncasecmp(field_name, http_field_get_name(&hdr->fields[i]), len) == 0) |         if (strcasecmp(field_name, http_field_get_name(&hdr->fields[i])) == 0) | ||||||
|             return i; |             return i; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -334,7 +357,7 @@ int http_send_response(sock *client, http_res *res) { | |||||||
|         off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); |         off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); | ||||||
|     } |     } | ||||||
|     off += sprintf(buf + off, "\r\n"); |     off += sprintf(buf + off, "\r\n"); | ||||||
|     if (sock_send(client, buf, off, 0) != off) |     if (sock_send_x(client, buf, off, 0) != off) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -348,13 +371,13 @@ int http_send_request(sock *server, http_req *req) { | |||||||
|         off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); |         off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); | ||||||
|     } |     } | ||||||
|     off += sprintf(buf + off, "\r\n"); |     off += sprintf(buf + off, "\r\n"); | ||||||
|     if (sock_send(server, buf, off, 0) != off) |     if (sock_send_x(server, buf, off, 0) != off) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| const http_status *http_get_status(unsigned short status_code) { | const http_status *http_get_status(status_code_t status_code) { | ||||||
|     for (int i = 0; i < http_statuses_size; i++) { |     for (int i = 0; i < http_statuses_size; i++) { | ||||||
|         if (http_statuses[i].code == status_code) { |         if (http_statuses[i].code == status_code) { | ||||||
|             return &http_statuses[i]; |             return &http_statuses[i]; | ||||||
| @@ -363,62 +386,55 @@ const http_status *http_get_status(unsigned short status_code) { | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| const http_status_msg *http_get_error_msg(const http_status *status) { | const http_status_msg *http_get_error_msg(status_code_t status_code) { | ||||||
|     unsigned short code = status->code; |  | ||||||
|     for (int i = 0; i < http_status_messages_size; i++) { |     for (int i = 0; i < http_status_messages_size; i++) { | ||||||
|         if (http_status_messages[i].code == code) { |         if (http_status_messages[i].code == status_code) { | ||||||
|             return &http_status_messages[i]; |             return &http_status_messages[i]; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| const char *http_get_status_color(const http_status *status) { | const char *http_get_status_color(status_code_t status_code) { | ||||||
|     unsigned short code = status->code; |     if (status_code == 304) return HTTP_2XX_STR; | ||||||
|     if (code >= 100 && code < 200) { |     switch (status_code / 100) { | ||||||
|         return HTTP_1XX_STR; |         case 1: return HTTP_1XX_STR; | ||||||
|     } else if ((code >= 200 && code < 300) || code == 304) { |         case 2: return HTTP_2XX_STR; | ||||||
|         return HTTP_2XX_STR; |         case 3: return HTTP_3XX_STR; | ||||||
|     } else if (code >= 300 && code < 400) { |         case 4: return HTTP_4XX_STR; | ||||||
|         return HTTP_3XX_STR; |         case 5: return HTTP_5XX_STR; | ||||||
|     } else if (code >= 400 && code < 500) { |         default: return ""; | ||||||
|         return HTTP_4XX_STR; |  | ||||||
|     } else if (code >= 500 && code < 600) { |  | ||||||
|         return HTTP_5XX_STR; |  | ||||||
|     } |     } | ||||||
|     return ""; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| char *http_format_date(time_t time, char *buf, size_t size) { | char *http_format_date(time_t ts, char *buf, size_t size) { | ||||||
|     struct tm timeinfo; |     struct tm time_info; | ||||||
|     strftime(buf, size, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&time, &timeinfo)); |     strftime(buf, size, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&ts, &time_info)); | ||||||
|     return buf; |     return buf; | ||||||
| } | } | ||||||
|  |  | ||||||
| char *http_get_date(char *buf, size_t size) { | char *http_get_date(char *buf, size_t size) { | ||||||
|     time_t rawtime; |     time_t raw_time; | ||||||
|     time(&rawtime); |     time(&raw_time); | ||||||
|     return http_format_date(rawtime, buf, size); |     return http_format_date(raw_time, buf, size); | ||||||
| } | } | ||||||
|  |  | ||||||
| const http_doc_info *http_get_status_info(const http_status *status) { | const http_doc_info *http_get_status_info(status_code_t status_code) { | ||||||
|     unsigned short code = status->code; |     static const http_doc_info info[] = { | ||||||
|     static http_doc_info info[] = { |  | ||||||
|             {"info",    HTTP_COLOR_INFO,    "/.sesimos/res/icon-info.svg",    http_info_doc}, |             {"info",    HTTP_COLOR_INFO,    "/.sesimos/res/icon-info.svg",    http_info_doc}, | ||||||
|             {"success", HTTP_COLOR_SUCCESS, "/.sesimos/res/icon-success.svg", http_success_doc}, |             {"success", HTTP_COLOR_SUCCESS, "/.sesimos/res/icon-success.svg", http_success_doc}, | ||||||
|             {"warning", HTTP_COLOR_WARNING, "/.sesimos/res/icon-warning.svg", http_warning_doc}, |             {"warning", HTTP_COLOR_WARNING, "/.sesimos/res/icon-warning.svg", http_warning_doc}, | ||||||
|             {"error",   HTTP_COLOR_ERROR,   "/.sesimos/res/icon-error.svg",   http_error_doc} |             {"error",   HTTP_COLOR_ERROR,   "/.sesimos/res/icon-error.svg",   http_error_doc} | ||||||
|     }; |     }; | ||||||
|     if (code >= 100 && code < 200) { |     if (status_code == 304) return &info[1]; | ||||||
|         return &info[0]; |     switch (status_code / 100) { | ||||||
|     } else if ((code >= 200 && code < 300) || code == 304) { |         case 1: return &info[0]; | ||||||
|         return &info[1]; |         case 2: return &info[1]; | ||||||
|     } else if (code >= 300 && code < 400) { |         case 3: return &info[2]; | ||||||
|         return &info[2]; |         case 4: // see case 5 | ||||||
|     } else if (code >= 400 && code < 600) { |         case 5: return &info[3]; | ||||||
|         return &info[3]; |         default: return NULL; | ||||||
|     } |     } | ||||||
|     return NULL; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int http_get_compression(const http_req *req, const http_res *res) { | int http_get_compression(const http_req *req, const http_res *res) { | ||||||
| @@ -426,9 +442,9 @@ int http_get_compression(const http_req *req, const http_res *res) { | |||||||
|     const char *content_type = http_get_header_field(&res->hdr, "Content-Type"); |     const char *content_type = http_get_header_field(&res->hdr, "Content-Type"); | ||||||
|     const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); |     const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); | ||||||
|     if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) { |     if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) { | ||||||
|         if (strstr(accept_encoding, "br") != NULL) { |         if (strcontains(accept_encoding, "br")) { | ||||||
|             return COMPRESS_BR; |             return COMPRESS_BR; | ||||||
|         } else if (strstr(accept_encoding, "gzip") != NULL) { |         } else if (strcontains(accept_encoding, "gzip")) { | ||||||
|             return COMPRESS_GZ; |             return COMPRESS_GZ; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -52,7 +52,6 @@ | |||||||
| #define HTTP_ERROR_HEADER_MALFORMED 5 | #define HTTP_ERROR_HEADER_MALFORMED 5 | ||||||
| #define HTTP_ERROR_INVALID_VERSION 6 | #define HTTP_ERROR_INVALID_VERSION 6 | ||||||
| #define HTTP_ERROR_URI_TOO_LONG 7 | #define HTTP_ERROR_URI_TOO_LONG 7 | ||||||
| #define HTTP_ERROR_ |  | ||||||
|  |  | ||||||
| #ifndef SERVER_STR | #ifndef SERVER_STR | ||||||
| #   define SERVER_STR "sesimos" | #   define SERVER_STR "sesimos" | ||||||
| @@ -62,14 +61,16 @@ | |||||||
| #   define SERVER_STR_HTML "sesimos web server" | #   define SERVER_STR_HTML "sesimos web server" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | typedef unsigned short status_code_t; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned short code:10; |     status_code_t code:10; | ||||||
|     unsigned char type:3; |     unsigned char type:3; | ||||||
|     char msg[64]; |     char msg[64]; | ||||||
| } http_status; | } http_status; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned short code:10; |     status_code_t code:10; | ||||||
|     const char *msg; |     const char *msg; | ||||||
| } http_status_msg; | } http_status_msg; | ||||||
|  |  | ||||||
| @@ -121,7 +122,7 @@ typedef enum { | |||||||
| } http_error_origin; | } http_error_origin; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned short status; |     status_code_t status; | ||||||
|     http_error_origin origin; |     http_error_origin origin; | ||||||
|     const char* ws_key; |     const char* ws_key; | ||||||
| } http_status_ctx; | } http_status_ctx; | ||||||
| @@ -133,6 +134,8 @@ extern const int http_status_messages_size; | |||||||
|  |  | ||||||
| extern const char http_error_doc[], http_warning_doc[], http_success_doc[], http_info_doc[]; | extern const char http_error_doc[], http_warning_doc[], http_success_doc[], http_info_doc[]; | ||||||
|  |  | ||||||
|  | const char *http_error_str(int err); | ||||||
|  |  | ||||||
| void http_to_camel_case(char *str, int mode); | void http_to_camel_case(char *str, int mode); | ||||||
|  |  | ||||||
| const char *http_field_get_name(const http_field *field); | const char *http_field_get_name(const http_field *field); | ||||||
| @@ -157,12 +160,8 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, | |||||||
|  |  | ||||||
| const char *http_get_header_field(const http_hdr *hdr, const char *field_name); | const char *http_get_header_field(const http_hdr *hdr, const char *field_name); | ||||||
|  |  | ||||||
| const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len); |  | ||||||
|  |  | ||||||
| int http_get_header_field_num(const http_hdr *hdr, const char *field_name); | int http_get_header_field_num(const http_hdr *hdr, const char *field_name); | ||||||
|  |  | ||||||
| int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len); |  | ||||||
|  |  | ||||||
| int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); | int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); | ||||||
|  |  | ||||||
| int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len); | int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len); | ||||||
| @@ -175,17 +174,17 @@ int http_send_response(sock *client, http_res *res); | |||||||
|  |  | ||||||
| int http_send_request(sock *server, http_req *req); | int http_send_request(sock *server, http_req *req); | ||||||
|  |  | ||||||
| const http_status *http_get_status(unsigned short status_code); | const http_status *http_get_status(status_code_t status_code); | ||||||
|  |  | ||||||
| const http_status_msg *http_get_error_msg(const http_status *status); | const http_status_msg *http_get_error_msg(status_code_t status_code); | ||||||
|  |  | ||||||
| const char *http_get_status_color(const http_status *status); | const char *http_get_status_color(status_code_t status_code); | ||||||
|  |  | ||||||
| char *http_format_date(time_t time, char *buf, size_t size); | char *http_format_date(time_t time, char *buf, size_t size); | ||||||
|  |  | ||||||
| char *http_get_date(char *buf, size_t size); | char *http_get_date(char *buf, size_t size); | ||||||
|  |  | ||||||
| const http_doc_info *http_get_status_info(const http_status *status); | const http_doc_info *http_get_status_info(status_code_t status_code); | ||||||
|  |  | ||||||
| int http_get_compression(const http_req *req, const http_res *res); | int http_get_compression(const http_req *req, const http_res *res); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,10 +15,8 @@ | |||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned char version; |     unsigned char version; | ||||||
|     unsigned char type; |     unsigned char type; | ||||||
|     unsigned char requestIdB1; |     unsigned short requestId; | ||||||
|     unsigned char requestIdB0; |     unsigned short contentLength; | ||||||
|     unsigned char contentLengthB1; |  | ||||||
|     unsigned char contentLengthB0; |  | ||||||
|     unsigned char paddingLength; |     unsigned char paddingLength; | ||||||
|     unsigned char reserved; |     unsigned char reserved; | ||||||
| } FCGI_Header; | } FCGI_Header; | ||||||
| @@ -56,8 +54,7 @@ typedef struct { | |||||||
| #define FCGI_NULL_REQUEST_ID     0 | #define FCGI_NULL_REQUEST_ID     0 | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned char roleB1; |     unsigned short role; | ||||||
|     unsigned char roleB0; |  | ||||||
|     unsigned char flags; |     unsigned char flags; | ||||||
|     unsigned char reserved[5]; |     unsigned char reserved[5]; | ||||||
| } FCGI_BeginRequestBody; | } FCGI_BeginRequestBody; | ||||||
| @@ -80,10 +77,7 @@ typedef struct { | |||||||
| #define FCGI_FILTER     3 | #define FCGI_FILTER     3 | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned char appStatusB3; |     unsigned int appStatus; | ||||||
|     unsigned char appStatusB2; |  | ||||||
|     unsigned char appStatusB1; |  | ||||||
|     unsigned char appStatusB0; |  | ||||||
|     unsigned char protocolStatus; |     unsigned char protocolStatus; | ||||||
|     unsigned char reserved[3]; |     unsigned char reserved[3]; | ||||||
| } FCGI_EndRequestBody; | } FCGI_EndRequestBody; | ||||||
|   | |||||||
| @@ -6,14 +6,19 @@ | |||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  |  | ||||||
| #define FACTOR 4 | #define FACTOR 4 | ||||||
|  | #define meta(ptr) ((list_meta_t *) ((unsigned char *) (ptr) - sizeof(list_meta_t))) | ||||||
|  | #define data(ptr) ((unsigned char *) (ptr) + sizeof(list_meta_t)) | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     int init_size, elem_size, max_size, size; |     int init_size, elem_size, max_size, size; | ||||||
| } list_meta_t; | } list_meta_t; | ||||||
|  |  | ||||||
| static void *list_resize(list_meta_t *list, int new_size) { | static void *list_resize(list_meta_t *list, int new_size) { | ||||||
|     if (new_size <= 0) |     if (new_size <= 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  |     } else if (new_size == list->max_size) { | ||||||
|  |         return list; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     list_meta_t *new_ptr = realloc(list, sizeof(list_meta_t) + list->elem_size * new_size); |     list_meta_t *new_ptr = realloc(list, sizeof(list_meta_t) + list->elem_size * new_size); | ||||||
|     if (new_ptr == NULL) |     if (new_ptr == NULL) | ||||||
| @@ -30,21 +35,24 @@ void *list_create(int elem_size, int init_elem_n) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     void *list_ptr = malloc(sizeof(list_meta_t) + elem_size * init_elem_n); |     void *list_ptr = malloc(sizeof(list_meta_t) + elem_size * init_elem_n); | ||||||
|  |     if (list_ptr == NULL) | ||||||
|  |         return NULL; | ||||||
|  |  | ||||||
|  |     memset(list_ptr, 0, sizeof(list_meta_t) + elem_size * init_elem_n); | ||||||
|     list_meta_t *list = list_ptr; |     list_meta_t *list = list_ptr; | ||||||
|     list->init_size = init_elem_n; |     list->init_size = init_elem_n; | ||||||
|     list->elem_size = elem_size; |     list->elem_size = elem_size; | ||||||
|     list->max_size = init_elem_n; |     list->max_size = init_elem_n; | ||||||
|     list->size = 0; |     list->size = 0; | ||||||
|     return (unsigned char *) list_ptr + sizeof(list_meta_t); |     return data(list_ptr); | ||||||
| } | } | ||||||
|  |  | ||||||
| int list_size(const void *list_ptr) { | int list_size(const void *list_ptr) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     return meta(list_ptr)->size; | ||||||
|     return list->size; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int list_find(void *list_ptr, void *elem) { | int list_find(void *list_ptr, void *elem) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     list_meta_t *list = meta(list_ptr); | ||||||
|     unsigned char *array = list_ptr; |     unsigned char *array = list_ptr; | ||||||
|  |  | ||||||
|     for (int i = 0; i < list->size; i++) { |     for (int i = 0; i < list->size; i++) { | ||||||
| @@ -56,36 +64,39 @@ int list_find(void *list_ptr, void *elem) { | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int list_contains(void *list_ptr, void *elem) { | ||||||
|  |     return list_find(list_ptr, elem) != -1; | ||||||
|  | } | ||||||
|  |  | ||||||
| void *list_insert(void *list_ptr, void *elem, int n) { | void *list_insert(void *list_ptr, void *elem, int n) { | ||||||
|     void *ptr = NULL; |     void *ptr = NULL; | ||||||
|     list_ptr = list_insert_ptr(list_ptr, &ptr, n); |     list_ptr = list_insert_ptr(list_ptr, &ptr, n); | ||||||
|     if (list_ptr != NULL && ptr != NULL) { |     if (list_ptr != NULL && ptr != NULL) { | ||||||
|         list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |         memcpy(ptr, elem, meta(list_ptr)->elem_size); | ||||||
|         memcpy(ptr, elem, list->elem_size); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return list_ptr; |     return list_ptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| void *list_insert_ptr(void *list_ptr, void **elem, int n) { | void *list_insert_ptr(void *list_ptr, void **elem, int n) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     list_meta_t *list = meta(list_ptr); | ||||||
|     if (n < 0) |     if (n < 0) | ||||||
|         n = list->size + n + 1; |         n = list->size + n + 1; | ||||||
|  |  | ||||||
|     if (list->size >= list->max_size) { |     if (list->size >= list->max_size) { | ||||||
|         if ((list = list_resize(list, list->max_size * FACTOR)) == NULL) { |         if ((list = list_resize(list, list->max_size * FACTOR)) == NULL) | ||||||
|             return NULL; |             return NULL; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     unsigned char *array = (unsigned char *) list + sizeof(list_meta_t); |     unsigned char *array = data(list); | ||||||
|  |  | ||||||
|     if (n < list->size) |     if (n < list->size) | ||||||
|         memmove(array + (n + 1) * list->elem_size, array + n * list->elem_size, (list->size - n) * list->elem_size); |         memmove(array + (n + 1) * list->elem_size, array + n * list->elem_size, (list->size - n) * list->elem_size); | ||||||
|     *elem = array + n * list->elem_size; |     *elem = array + n * list->elem_size; | ||||||
|  |     memset(*elem, 0, list->elem_size); | ||||||
|  |  | ||||||
|     list->size++; |     list->size++; | ||||||
|     return (unsigned char *) list + sizeof(list_meta_t); |     return array; | ||||||
| } | } | ||||||
|  |  | ||||||
| void *list_append(void *list_ptr, void *elem) { | void *list_append(void *list_ptr, void *elem) { | ||||||
| @@ -97,7 +108,7 @@ void *list_append_ptr(void *list_ptr, void **elem) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void *list_remove(void *list_ptr, int n) { | void *list_remove(void *list_ptr, int n) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     list_meta_t *list = meta(list_ptr); | ||||||
|     if (n < 0) |     if (n < 0) | ||||||
|         n = list->size + n; |         n = list->size + n; | ||||||
|  |  | ||||||
| @@ -107,13 +118,15 @@ void *list_remove(void *list_ptr, int n) { | |||||||
|         memmove(array + n * list->elem_size, array + (n + 1) * list->elem_size, (list->size - n - 1) * list->elem_size); |         memmove(array + n * list->elem_size, array + (n + 1) * list->elem_size, (list->size - n - 1) * list->elem_size); | ||||||
|  |  | ||||||
|     list->size--; |     list->size--; | ||||||
|     if (list->size < list->max_size / FACTOR / 2 && list->max_size / FACTOR >= list->init_size) { |     memset(array + list->size * list->elem_size, 0, list->elem_size); | ||||||
|  |  | ||||||
|  |     if (list->size <= list->max_size / FACTOR * 3 / 4 && list->max_size / FACTOR >= list->init_size) { | ||||||
|         if ((list = list_resize(list, list->max_size / FACTOR)) == NULL) { |         if ((list = list_resize(list, list->max_size / FACTOR)) == NULL) { | ||||||
|             return NULL; |             return NULL; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return (unsigned char *) list + sizeof(list_meta_t); |     return data(list); | ||||||
| } | } | ||||||
|  |  | ||||||
| void *list_delete(void *list_ptr, void *elem) { | void *list_delete(void *list_ptr, void *elem) { | ||||||
| @@ -126,14 +139,12 @@ void *list_delete(void *list_ptr, void *elem) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void *list_clear(void *list_ptr) { | void *list_clear(void *list_ptr) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     list_meta_t *list = meta(list_ptr); | ||||||
|     list->size = 0; |     list->size = 0; | ||||||
|     memset(list_ptr, 0, list->max_size * list->elem_size); |     memset(list_ptr, 0, list->max_size * list->elem_size); | ||||||
|     list->max_size = list->init_size; |     return data(list_resize(list, list->init_size)); | ||||||
|     return (unsigned char *) list_resize(list, list->max_size * list->elem_size) + sizeof(list_meta_t); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void list_free(void *list_ptr) { | void list_free(void *list_ptr) { | ||||||
|     list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t)); |     free(meta(list_ptr)); | ||||||
|     free(list); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ int list_size(const void *list_ptr); | |||||||
|  |  | ||||||
| int list_find(void *list_ptr, void *elem); | int list_find(void *list_ptr, void *elem); | ||||||
|  |  | ||||||
|  | int list_contains(void *list_ptr, void *elem); | ||||||
|  |  | ||||||
| void *list_insert(void *list_ptr, void *elem, int n); | void *list_insert(void *list_ptr, void *elem, int n); | ||||||
|  |  | ||||||
| void *list_insert_ptr(void *list_ptr, void **elem, int n); | void *list_insert_ptr(void *list_ptr, void **elem, int n); | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								src/lib/proxy.c
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								src/lib/proxy.c
									
									
									
									
									
								
							| @@ -11,7 +11,6 @@ | |||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "proxy.h" | #include "proxy.h" | ||||||
| #include "utils.h" | #include "utils.h" | ||||||
| #include "compress.h" |  | ||||||
| #include "config.h" | #include "config.h" | ||||||
| #include "error.h" | #include "error.h" | ||||||
|  |  | ||||||
| @@ -89,8 +88,10 @@ void proxy_unload(void) { | |||||||
| void proxy_close_all(void) { | void proxy_close_all(void) { | ||||||
|     proxy_ctx_t *ptr = proxies; |     proxy_ctx_t *ptr = proxies; | ||||||
|     for (int i = 0; i < MAX_PROXY_CNX_PER_HOST * num_proxy_hosts; i++, ptr++) { |     for (int i = 0; i < MAX_PROXY_CNX_PER_HOST * num_proxy_hosts; i++, ptr++) { | ||||||
|         if (ptr->initialized) |         if (ptr->initialized) { | ||||||
|             proxy_close(ptr); |             proxy_close(ptr); | ||||||
|  |             logger_set_prefix(""); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -161,20 +162,19 @@ int proxy_request_header(http_req *req, sock *sock) { | |||||||
|         http_add_header_field(&req->hdr, "Via", buf2); |         http_add_header_field(&req->hdr, "Via", buf2); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *host = http_get_header_field(&req->hdr, "Host"); |  | ||||||
|     const char *forwarded = http_get_header_field(&req->hdr, "Forwarded"); |  | ||||||
|     int client_ipv6 = strchr(sock->addr, ':') != NULL; |     int client_ipv6 = strchr(sock->addr, ':') != NULL; | ||||||
|     int server_ipv6 = strchr(sock->s_addr, ':') != NULL; |     int server_ipv6 = strchr(sock->s_addr, ':') != NULL; | ||||||
|  |  | ||||||
|     p_len = snprintf(buf1, sizeof(buf1), "by=%s%s%s;for=%s%s%s;host=%s;proto=%s", |     p_len = snprintf(buf1, sizeof(buf1), "by=%s%s%s;for=%s%s%s;host=%s;proto=%s", | ||||||
|                      server_ipv6 ? "\"[" : "", sock->s_addr, server_ipv6 ? "]\"" : "", |                      server_ipv6 ? "\"[" : "", sock->s_addr, server_ipv6 ? "]\"" : "", | ||||||
|                      client_ipv6 ? "\"[" : "", sock->addr, client_ipv6 ? "]\"" : "", |                      client_ipv6 ? "\"[" : "", sock->addr, client_ipv6 ? "]\"" : "", | ||||||
|                      host, sock->enc ? "https" : "http"); |                      http_get_header_field(&req->hdr, "Host"), sock->enc ? "https" : "http"); | ||||||
|     if (p_len < 0 || p_len >= sizeof(buf1)) { |     if (p_len < 0 || p_len >= sizeof(buf1)) { | ||||||
|         error("Appended part of header field 'Forwarded' too long"); |         error("Appended part of header field 'Forwarded' too long"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const char *forwarded = http_get_header_field(&req->hdr, "Forwarded"); | ||||||
|     if (forwarded == NULL) { |     if (forwarded == NULL) { | ||||||
|         http_add_header_field(&req->hdr, "Forwarded", buf1); |         http_add_header_field(&req->hdr, "Forwarded", buf1); | ||||||
|     } else { |     } else { | ||||||
| @@ -197,9 +197,10 @@ int proxy_request_header(http_req *req, sock *sock) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host"); |     const char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host"); | ||||||
|  |     forwarded = http_get_header_field(&req->hdr, "Forwarded"); | ||||||
|     if (xfh == NULL) { |     if (xfh == NULL) { | ||||||
|         if (forwarded == NULL) { |         if (forwarded == NULL) { | ||||||
|             http_add_header_field(&req->hdr, "X-Forwarded-Host", host); |             http_add_header_field(&req->hdr, "X-Forwarded-Host", http_get_header_field(&req->hdr, "Host")); | ||||||
|         } else { |         } else { | ||||||
|             char *ptr = strchr(forwarded, ','); |             char *ptr = strchr(forwarded, ','); | ||||||
|             unsigned long len; |             unsigned long len; | ||||||
| @@ -218,6 +219,7 @@ int proxy_request_header(http_req *req, sock *sock) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto"); |     const char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto"); | ||||||
|  |     forwarded = http_get_header_field(&req->hdr, "Forwarded"); | ||||||
|     if (xfp == NULL) { |     if (xfp == NULL) { | ||||||
|         if (forwarded == NULL) { |         if (forwarded == NULL) { | ||||||
|             http_add_header_field(&req->hdr, "X-Forwarded-Proto", sock->enc ? "https" : "http"); |             http_add_header_field(&req->hdr, "X-Forwarded-Proto", sock->enc ? "https" : "http"); | ||||||
| @@ -307,11 +309,9 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         goto proxy; |         goto proxy; | ||||||
|  |  | ||||||
|     retry: |     retry: | ||||||
|     if (proxy->initialized) { |     if (proxy->initialized) | ||||||
|         info(BLUE_STR "Closing proxy connection"); |         proxy_close(proxy); | ||||||
|         sock_close(&proxy->proxy); |  | ||||||
|         proxy->initialized = 0; |  | ||||||
|     } |  | ||||||
|     retry = 0; |     retry = 0; | ||||||
|     tries++; |     tries++; | ||||||
|  |  | ||||||
| @@ -323,7 +323,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0) |     if (sock_set_socket_timeout(&proxy->proxy, 1) != 0 || sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0) | ||||||
|         goto proxy_timeout_err; |         goto proxy_timeout_err; | ||||||
|  |  | ||||||
|     struct hostent *host_ent = gethostbyname2(conf->proxy.hostname, AF_INET6); |     struct hostent *host_ent = gethostbyname2(conf->proxy.hostname, AF_INET6); | ||||||
| @@ -380,9 +380,9 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket); |         SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket); | ||||||
|         SSL_set_connect_state(proxy->proxy.ssl); |         SSL_set_connect_state(proxy->proxy.ssl); | ||||||
|  |  | ||||||
|         ret = SSL_do_handshake(proxy->proxy.ssl); |         if ((ret = SSL_do_handshake(proxy->proxy.ssl)) != 1) { | ||||||
|         if (ret != 1) { |             sock_error(&proxy->proxy, (int) ret); | ||||||
|             error_ssl(SSL_get_error(proxy->proxy.ssl, (int) ret)); |             SSL_free(proxy->proxy.ssl); | ||||||
|             res->status = http_get_status(502); |             res->status = http_get_status(502); | ||||||
|             ctx->origin = SERVER_REQ; |             ctx->origin = SERVER_REQ; | ||||||
|             error("Unable to perform handshake"); |             error("Unable to perform handshake"); | ||||||
| @@ -393,15 +393,16 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     proxy->initialized = 1; |     proxy->initialized = 1; | ||||||
|  |     proxy->cnx_s = clock_micros(); | ||||||
|     proxy->host = conf->name; |     proxy->host = conf->name; | ||||||
|     info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", buffer, conf->proxy.port); |     info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", buffer, conf->proxy.port); | ||||||
|  |  | ||||||
|     proxy: |     proxy: | ||||||
|     connection = http_get_header_field(&req->hdr, "Connection"); |     connection = http_get_header_field(&req->hdr, "Connection"); | ||||||
|     if (connection != NULL && (strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL)) { |     if (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) { | ||||||
|         upgrade = http_get_header_field(&req->hdr, "Upgrade"); |         upgrade = http_get_header_field(&req->hdr, "Upgrade"); | ||||||
|         ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version"); |         ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version"); | ||||||
|         if (upgrade != NULL && ws_version != NULL && strcmp(upgrade, "websocket") == 0 && strcmp(ws_version, "13") == 0) { |         if (streq(upgrade, "websocket") && streq(ws_version, "13")) { | ||||||
|             ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key"); |             ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key"); | ||||||
|         } else { |         } else { | ||||||
|             res->status = http_get_status(501); |             res->status = http_get_status(501); | ||||||
| @@ -437,7 +438,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|     ret = 0; |     ret = 0; | ||||||
|     if (content_len > 0) { |     if (content_len > 0) { | ||||||
|         ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); |         ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); | ||||||
|     } else if (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL) { |     } else if (strcontains(transfer_encoding, "chunked")) { | ||||||
|         ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer)); |         ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -462,7 +463,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer), MSG_PEEK); |     ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK); | ||||||
|     if (ret <= 0) { |     if (ret <= 0) { | ||||||
|         int e_sys = error_get_sys(), e_ssl = error_get_ssl(); |         int e_sys = error_get_sys(), e_ssl = error_get_ssl(); | ||||||
|         if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) { |         if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) { | ||||||
| @@ -477,6 +478,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         retry = tries < 4; |         retry = tries < 4; | ||||||
|         goto proxy_err; |         goto proxy_err; | ||||||
|     } |     } | ||||||
|  |     buffer[ret] = 0; | ||||||
|  |  | ||||||
|     char *buf = buffer; |     char *buf = buffer; | ||||||
|     unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4); |     unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4); | ||||||
| @@ -510,7 +512,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|             goto proxy_err; |             goto proxy_err; | ||||||
|         } |         } | ||||||
|         if (ptr == buf) { |         if (ptr == buf) { | ||||||
|             if (strncmp(ptr, "HTTP/", 5) != 0) { |             if (!strstarts(ptr, "HTTP/")) { | ||||||
|                 res->status = http_get_status(502); |                 res->status = http_get_status(502); | ||||||
|                 ctx->origin = SERVER_RES; |                 ctx->origin = SERVER_RES; | ||||||
|                 error("Unable to parse header: Invalid header format"); |                 error("Unable to parse header: Invalid header format"); | ||||||
| @@ -546,7 +548,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
|         } |         } | ||||||
|         ptr = pos0 + 2; |         ptr = pos0 + 2; | ||||||
|     } |     } | ||||||
|     sock_recv(&proxy->proxy, buffer, header_len, 0); |     sock_recv_x(&proxy->proxy, buffer, header_len, 0); | ||||||
|  |  | ||||||
|     ret = proxy_response_header(req, res, conf); |     ret = proxy_response_header(req, res, conf); | ||||||
|     if (ret != 0) { |     if (ret != 0) { | ||||||
| @@ -564,24 +566,8 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu | |||||||
| } | } | ||||||
|  |  | ||||||
| int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) { | int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) { | ||||||
|     char buffer[CHUNK_SIZE], comp_out[CHUNK_SIZE], buf[256], *ptr; |     char buffer[CHUNK_SIZE], buf[256], *ptr; | ||||||
|     long ret = 0, len, snd_len; |     long ret = 0, len, snd_len; | ||||||
|     int finish_comp = 0; |  | ||||||
|  |  | ||||||
|     compress_ctx comp_ctx; |  | ||||||
|     if (flags & PROXY_COMPRESS_BR) { |  | ||||||
|         flags &= ~PROXY_COMPRESS_GZ; |  | ||||||
|         if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { |  | ||||||
|             error("Unable to init brotli"); |  | ||||||
|             flags &= ~PROXY_COMPRESS_BR; |  | ||||||
|         } |  | ||||||
|     } else if (flags & PROXY_COMPRESS_GZ) { |  | ||||||
|         flags &= ~PROXY_COMPRESS_BR; |  | ||||||
|         if (compress_init(&comp_ctx, COMPRESS_GZ) != 0) { |  | ||||||
|             error("Unable to init gzip"); |  | ||||||
|             flags &= ~PROXY_COMPRESS_GZ; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|         snd_len = 0; |         snd_len = 0; | ||||||
| @@ -598,67 +584,40 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int | |||||||
|  |  | ||||||
|             len_to_send = ret; |             len_to_send = ret; | ||||||
|             ret = 1; |             ret = 1; | ||||||
|             if (len_to_send == 0 && (flags & PROXY_COMPRESS)) { |  | ||||||
|                 finish_comp = 1; |  | ||||||
|                 len = 0; |  | ||||||
|                 ptr = NULL; |  | ||||||
|                 goto out; |  | ||||||
|                 finish: |  | ||||||
|                 compress_free(&comp_ctx); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         while (snd_len < len_to_send) { |         while (snd_len < len_to_send) { | ||||||
|             unsigned long avail_in, avail_out; |  | ||||||
|             ret = sock_recv(&proxy->proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0); |             ret = sock_recv(&proxy->proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0); | ||||||
|             if (ret <= 0) { |             if (ret <= 0) { | ||||||
|                 error("Unable to receive from server"); |                 error("Unable to receive from server"); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             len = ret; |  | ||||||
|             ptr = buffer; |             ptr = buffer; | ||||||
|             out: |             long buf_len = ret; | ||||||
|             avail_in = len; |  | ||||||
|             char *next_in = ptr; |  | ||||||
|             do { |  | ||||||
|                 long buf_len = len; |  | ||||||
|                 if (flags & PROXY_COMPRESS) { |  | ||||||
|                     avail_out = sizeof(comp_out); |  | ||||||
|                     compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, finish_comp); |  | ||||||
|                     ptr = comp_out; |  | ||||||
|                     buf_len = (int) (sizeof(comp_out) - avail_out); |  | ||||||
|                     snd_len += (long) (len - avail_in); |  | ||||||
|                 } |  | ||||||
|                 if (buf_len != 0) { |  | ||||||
|             len = sprintf(buf, "%lX\r\n", buf_len); |             len = sprintf(buf, "%lX\r\n", buf_len); | ||||||
|             ret = 1; |             ret = 1; | ||||||
|  |  | ||||||
|                     if (flags & PROXY_CHUNKED) ret = sock_send(client, buf, len, 0); |             if (flags & PROXY_CHUNKED) ret = sock_send_x(client, buf, len, 0); | ||||||
|             if (ret <= 0) goto err; |             if (ret <= 0) goto err; | ||||||
|  |  | ||||||
|                     ret = sock_send(client, ptr, buf_len, 0); |             ret = sock_send_x(client, ptr, buf_len, 0); | ||||||
|             if (ret <= 0) goto err; |             if (ret <= 0) goto err; | ||||||
|                     if (!(flags & PROXY_COMPRESS)) snd_len += ret; |             snd_len += ret; | ||||||
|  |  | ||||||
|                     if (flags & PROXY_CHUNKED) ret = sock_send(client, "\r\n", 2, 0); |             if (flags & PROXY_CHUNKED) ret = sock_send_x(client, "\r\n", 2, 0); | ||||||
|             if (ret <= 0) { |             if (ret <= 0) { | ||||||
|                 err: |                 err: | ||||||
|                 error("Unable to send"); |                 error("Unable to send"); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|             } while ((flags & PROXY_COMPRESS) && (avail_in != 0 || avail_out != sizeof(comp_out))); |  | ||||||
|         if (ret <= 0) break; |         if (ret <= 0) break; | ||||||
|             if (finish_comp) goto finish; |         if (flags & PROXY_CHUNKED) sock_recv_x(&proxy->proxy, buffer, 2, 0); | ||||||
|         } |  | ||||||
|         if (ret <= 0) break; |  | ||||||
|         if (flags & PROXY_CHUNKED) sock_recv(&proxy->proxy, buffer, 2, 0); |  | ||||||
|     } while ((flags & PROXY_CHUNKED) && len_to_send > 0); |     } while ((flags & PROXY_CHUNKED) && len_to_send > 0); | ||||||
|  |  | ||||||
|     if (ret <= 0) return -1; |     if (ret <= 0) return -1; | ||||||
|  |  | ||||||
|     if (flags & PROXY_CHUNKED) { |     if (flags & PROXY_CHUNKED) { | ||||||
|         ret = sock_send(client, "0\r\n\r\n", 5, 0); |         if (sock_send_x(client, "0\r\n\r\n", 5, 0) == -1) { | ||||||
|         if (ret <= 0) { |  | ||||||
|             error("Unable to send"); |             error("Unable to send"); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -668,6 +627,26 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int | |||||||
| } | } | ||||||
|  |  | ||||||
| int proxy_dump(proxy_ctx_t *proxy, char *buf, long len) { | int proxy_dump(proxy_ctx_t *proxy, char *buf, long len) { | ||||||
|     sock_recv(&proxy->proxy, buf, len, 0); |     long ret = sock_recv(&proxy->proxy, buf, len, 0); | ||||||
|  |     if (ret == -1) return -1; | ||||||
|  |     buf[ret] = 0; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void proxy_close(proxy_ctx_t *ctx) { | ||||||
|  |     client_ctx_t *cctx = ctx->client; | ||||||
|  |     if (cctx) { | ||||||
|  |         logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, cctx->req_host, CLR_STR, cctx->log_prefix); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ctx->initialized) { | ||||||
|  |         ctx->cnx_e = clock_micros(); | ||||||
|  |         char buf[32]; | ||||||
|  |         info(BLUE_STR "Closing proxy connection (%s)", format_duration(ctx->cnx_e - ctx->cnx_s, buf)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sock_close(&ctx->proxy); | ||||||
|  |  | ||||||
|  |     memset(ctx, 0, sizeof(*ctx)); | ||||||
|  |     errno = 0; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -10,9 +10,6 @@ | |||||||
| #define SESIMOS_PROXY_H | #define SESIMOS_PROXY_H | ||||||
|  |  | ||||||
| #define PROXY_CHUNKED 1 | #define PROXY_CHUNKED 1 | ||||||
| #define PROXY_COMPRESS_GZ 2 |  | ||||||
| #define PROXY_COMPRESS_BR 4 |  | ||||||
| #define PROXY_COMPRESS 6 |  | ||||||
|  |  | ||||||
| #ifndef SERVER_NAME | #ifndef SERVER_NAME | ||||||
| #   define SERVER_NAME "reverse proxy" | #   define SERVER_NAME "reverse proxy" | ||||||
| @@ -24,6 +21,7 @@ | |||||||
| typedef struct { | typedef struct { | ||||||
|     unsigned char initialized:1, in_use:1; |     unsigned char initialized:1, in_use:1; | ||||||
|     sock proxy; |     sock proxy; | ||||||
|  |     long cnx_s, cnx_e; | ||||||
|     char *host; |     char *host; | ||||||
|     void *client; |     void *client; | ||||||
| } proxy_ctx_t; | } proxy_ctx_t; | ||||||
| @@ -48,4 +46,6 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int | |||||||
|  |  | ||||||
| int proxy_dump(proxy_ctx_t *proxy, char *buf, long len); | int proxy_dump(proxy_ctx_t *proxy, char *buf, long len); | ||||||
|  |  | ||||||
|  | void proxy_close(proxy_ctx_t *ctx); | ||||||
|  |  | ||||||
| #endif //SESIMOS_PROXY_H | #endif //SESIMOS_PROXY_H | ||||||
|   | |||||||
| @@ -15,6 +15,50 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | #include <openssl/err.h> | ||||||
|  |  | ||||||
|  | static void ssl_error(unsigned long err) { | ||||||
|  |     if (err == SSL_ERROR_NONE) { | ||||||
|  |         errno = 0; | ||||||
|  |     } else if (err == SSL_ERROR_SYSCALL) { | ||||||
|  |         // errno already set | ||||||
|  |     } else if (err == SSL_ERROR_SSL) { | ||||||
|  |         error_ssl_err(ERR_get_error()); | ||||||
|  |     } else { | ||||||
|  |         error_ssl(err); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void sock_error(sock *s, int ret) { | ||||||
|  |     ssl_error(SSL_get_error(s->ssl, ret)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *sock_error_str(unsigned long err) { | ||||||
|  |     switch (err) { | ||||||
|  |         case SSL_ERROR_ZERO_RETURN: | ||||||
|  |             return "closed"; | ||||||
|  |         case SSL_ERROR_WANT_READ: | ||||||
|  |             return "want read"; | ||||||
|  |         case SSL_ERROR_WANT_WRITE: | ||||||
|  |             return "want write"; | ||||||
|  |         case SSL_ERROR_WANT_CONNECT: | ||||||
|  |             return "want connect"; | ||||||
|  |         case SSL_ERROR_WANT_ACCEPT: | ||||||
|  |             return "want accept"; | ||||||
|  |         case SSL_ERROR_WANT_X509_LOOKUP: | ||||||
|  |             return "want x509 lookup"; | ||||||
|  |         case SSL_ERROR_WANT_ASYNC: | ||||||
|  |             return "want async"; | ||||||
|  |         case SSL_ERROR_WANT_ASYNC_JOB: | ||||||
|  |             return "want async job"; | ||||||
|  |         case SSL_ERROR_WANT_CLIENT_HELLO_CB: | ||||||
|  |             return "want client hello callback"; | ||||||
|  |             //case SSL_ERROR_WANT_RETRY_VERIFY: | ||||||
|  |             //    return "want retry verify"; | ||||||
|  |         default: | ||||||
|  |             return "unknown error"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) { | int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) { | ||||||
|     struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000}, |     struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000}, | ||||||
| @@ -49,7 +93,7 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) { | |||||||
|     long ret; |     long ret; | ||||||
|     if (s->enc) { |     if (s->enc) { | ||||||
|         ret = SSL_write(s->ssl, buf, (int) len); |         ret = SSL_write(s->ssl, buf, (int) len); | ||||||
|         if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret)); |         if (ret <= 0) sock_error(s, (int) ret); | ||||||
|     } else { |     } else { | ||||||
|         ret = send(s->socket, buf, len, flags); |         ret = send(s->socket, buf, len, flags); | ||||||
|     } |     } | ||||||
| @@ -62,12 +106,26 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | long sock_send_x(sock *s, void *buf, unsigned long len, int flags) { | ||||||
|  |     for (long ret, sent = 0; sent < len; sent += ret) { | ||||||
|  |         if ((ret = sock_send(s, (unsigned char *) buf + sent, len - sent, flags)) <= 0) { | ||||||
|  |             if (errno == EINTR) { | ||||||
|  |                 errno = 0, ret = 0; | ||||||
|  |                 continue; | ||||||
|  |             } else { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return (long) len; | ||||||
|  | } | ||||||
|  |  | ||||||
| long sock_recv(sock *s, void *buf, unsigned long len, int flags) { | long sock_recv(sock *s, void *buf, unsigned long len, int flags) { | ||||||
|     long ret; |     long ret; | ||||||
|     if (s->enc) { |     if (s->enc) { | ||||||
|         int (*func)(SSL*, void*, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read; |         int (*func)(SSL*, void*, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read; | ||||||
|         ret = func(s->ssl, buf, (int) len); |         ret = func(s->ssl, buf, (int) len); | ||||||
|         if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret)); |         if (ret <= 0) sock_error(s, (int) ret); | ||||||
|     } else { |     } else { | ||||||
|         ret = recv(s->socket, buf, len, flags); |         ret = recv(s->socket, buf, len, flags); | ||||||
|     } |     } | ||||||
| @@ -80,6 +138,20 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | long sock_recv_x(sock *s, void *buf, unsigned long len, int flags) { | ||||||
|  |     for (long ret, rcv = 0; rcv < len; rcv += ret) { | ||||||
|  |         if ((ret = sock_recv(s, (unsigned char *) buf + rcv, len - rcv, flags)) <= 0) { | ||||||
|  |             if (errno == EINTR) { | ||||||
|  |                 errno = 0, ret = 0; | ||||||
|  |                 continue; | ||||||
|  |             } else { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return (long) len; | ||||||
|  | } | ||||||
|  |  | ||||||
| long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) { | long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) { | ||||||
|     long ret; |     long ret; | ||||||
|     unsigned long send_len = 0; |     unsigned long send_len = 0; | ||||||
| @@ -159,16 +231,24 @@ long sock_get_chunk_header(sock *s) { | |||||||
|     char buf[16]; |     char buf[16]; | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|         ret = sock_recv(s, buf, sizeof(buf), MSG_PEEK); |         if ((ret = sock_recv(s, buf, sizeof(buf) - 1, MSG_PEEK)) <= 0) { | ||||||
|         if (ret <= 0) return -2; |             if (errno == EINTR) { | ||||||
|         else if (ret < 2) continue; |                 errno = 0; | ||||||
|  |                 continue; | ||||||
|  |             } else { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } else if (ret < 2) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         buf[ret] = 0; | ||||||
|  |  | ||||||
|         ret = sock_parse_chunk_header(buf, ret, &len); |         if ((ret = sock_parse_chunk_header(buf, ret, &len)) == -2) | ||||||
|         if (ret == -2) return -1; |             return -1; | ||||||
|     } while (ret < 0); |     } while (ret < 0); | ||||||
|  |  | ||||||
|     if (sock_recv(s, buf, len, 0) != len) |     if (sock_recv_x(s, buf, len, 0) == -1) | ||||||
|         return -2; |         return -1; | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,6 +26,10 @@ typedef struct { | |||||||
|     long ts_start, ts_last, timeout_us; |     long ts_start, ts_last, timeout_us; | ||||||
| } sock; | } sock; | ||||||
|  |  | ||||||
|  | void sock_error(sock *s, int ret); | ||||||
|  |  | ||||||
|  | const char *sock_error_str(unsigned long err); | ||||||
|  |  | ||||||
| int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros); | int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros); | ||||||
|  |  | ||||||
| int sock_set_socket_timeout(sock *s, double sec); | int sock_set_socket_timeout(sock *s, double sec); | ||||||
| @@ -36,8 +40,12 @@ int sock_set_timeout(sock *s, double sec); | |||||||
|  |  | ||||||
| long sock_send(sock *s, void *buf, unsigned long len, int flags); | long sock_send(sock *s, void *buf, unsigned long len, int flags); | ||||||
|  |  | ||||||
|  | long sock_send_x(sock *s, void *buf, unsigned long len, int flags); | ||||||
|  |  | ||||||
| long sock_recv(sock *s, void *buf, unsigned long len, int flags); | long sock_recv(sock *s, void *buf, unsigned long len, int flags); | ||||||
|  |  | ||||||
|  | long sock_recv_x(sock *s, void *buf, unsigned long len, int flags); | ||||||
|  |  | ||||||
| long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len); | long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len); | ||||||
|  |  | ||||||
| long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len); | long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len); | ||||||
|   | |||||||
| @@ -15,21 +15,26 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| int path_is_directory(const char *path) { | int path_is_directory(const char *path) { | ||||||
|     struct stat statbuf; |     int e = errno; | ||||||
|     return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0; |     struct stat stat_buf; | ||||||
|  |     int ret = stat(path, &stat_buf); | ||||||
|  |     errno = e; | ||||||
|  |     return ret == 0 && S_ISDIR(stat_buf.st_mode) != 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int path_is_file(const char *path) { | int path_is_file(const char *path) { | ||||||
|     struct stat statbuf; |     int e = errno; | ||||||
|     int ret = stat(path, &statbuf); |     struct stat stat_buf; | ||||||
|     errno = 0; |     int ret = stat(path, &stat_buf); | ||||||
|     return ret == 0 && S_ISDIR(statbuf.st_mode) == 0; |     errno = e; | ||||||
|  |     return ret == 0 && S_ISDIR(stat_buf.st_mode) == 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int path_exists(const char *path) { | int path_exists(const char *path) { | ||||||
|     struct stat statbuf; |     int e = errno; | ||||||
|     int ret = stat(path, &statbuf); |     struct stat stat_buf; | ||||||
|     errno = 0; |     int ret = stat(path, &stat_buf); | ||||||
|  |     errno = e; | ||||||
|     return ret == 0; |     return ret == 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -73,7 +78,7 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|     if (query != NULL) { |     if (query != NULL) { | ||||||
|         query[-1] = '?'; |         query[-1] = '?'; | ||||||
|     } |     } | ||||||
|     if (strstr(uri->req_path, "/../") != NULL || strstr(uri->req_path, "/./") != NULL) { |     if (strcontains(uri->req_path, "/../") || strcontains(uri->req_path, "/./")) { | ||||||
|         return 2; |         return 2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -108,18 +113,16 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         sprintf(buf0, "%s%s", uri->webroot, uri->path); |         sprintf(buf0, "%s%s", uri->webroot, uri->path); | ||||||
|         p_len = snprintf(buf1, sizeof(buf1), "%s.ncr", buf0); |         p_len = snprintf(buf1, sizeof(buf1), "%s.php", buf0); | ||||||
|         if (p_len < 0 || p_len >= sizeof(buf1)) return -1; |         if (p_len < 0 || p_len >= sizeof(buf1)) return -1; | ||||||
|         p_len = snprintf(buf2, sizeof(buf2), "%s.php", buf0); |         p_len = snprintf(buf2, sizeof(buf2), "%s.html", buf0); | ||||||
|         if (p_len < 0 || p_len >= sizeof(buf2)) return -1; |         if (p_len < 0 || p_len >= sizeof(buf2)) return -1; | ||||||
|         p_len = snprintf(buf3, sizeof(buf3), "%s.html", buf0); |  | ||||||
|         if (p_len < 0 || p_len >= sizeof(buf3)) return -1; |  | ||||||
|  |  | ||||||
|         if (strlen(uri->path) <= 1 || |         if (strlen(uri->path) <= 1 || | ||||||
|             path_exists(buf0) || |             path_exists(buf0) || | ||||||
|             path_is_file(buf1) || |             path_is_file(buf1) || | ||||||
|             path_is_file(buf2) || |             path_is_file(buf2)) | ||||||
|             path_is_file(buf3)) { |         { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -140,10 +143,10 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|         uri->filename = malloc(strlen(buf0) + 1); |         uri->filename = malloc(strlen(buf0) + 1); | ||||||
|         strcpy(uri->filename, buf0); |         strcpy(uri->filename, buf0); | ||||||
|         long len = (long) strlen(uri->path); |         long len = (long) strlen(uri->path); | ||||||
|         if (strcmp(uri->path + len - 4, ".ncr") == 0 || strcmp(uri->path + len - 4, ".php") == 0) { |         if (strends(uri->path, ".php")) { | ||||||
|             uri->path[len - 4] = 0; |             uri->path[len - 4] = 0; | ||||||
|             uri->is_static = 0; |             uri->is_static = 0; | ||||||
|         } else if (strcmp(uri->path + len - 5, ".html") == 0) { |         } else if (strends(uri->path, ".html")) { | ||||||
|             uri->path[len - 5] = 0; |             uri->path[len - 5] = 0; | ||||||
|         } |         } | ||||||
|     } else if (path_is_file(buf1)) { |     } else if (path_is_file(buf1)) { | ||||||
| @@ -160,9 +163,8 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|     } else { |     } else { | ||||||
|         uri->is_dir = 1; |         uri->is_dir = 1; | ||||||
|         strcpy(uri->path + strlen(uri->path), "/"); |         strcpy(uri->path + strlen(uri->path), "/"); | ||||||
|         sprintf(buf1, "%s%sindex.ncr", uri->webroot, uri->path); |         sprintf(buf1, "%s%sindex.php", uri->webroot, uri->path); | ||||||
|         sprintf(buf2, "%s%sindex.php", uri->webroot, uri->path); |         sprintf(buf2, "%s%sindex.html", uri->webroot, uri->path); | ||||||
|         sprintf(buf3, "%s%sindex.html", uri->webroot, uri->path); |  | ||||||
|         if (path_is_file(buf1)) { |         if (path_is_file(buf1)) { | ||||||
|             uri->filename = malloc(strlen(buf1) + 1); |             uri->filename = malloc(strlen(buf1) + 1); | ||||||
|             strcpy(uri->filename, buf1); |             strcpy(uri->filename, buf1); | ||||||
| @@ -170,10 +172,6 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|         } else if (path_is_file(buf2)) { |         } else if (path_is_file(buf2)) { | ||||||
|             uri->filename = malloc(strlen(buf2) + 1); |             uri->filename = malloc(strlen(buf2) + 1); | ||||||
|             strcpy(uri->filename, buf2); |             strcpy(uri->filename, buf2); | ||||||
|             uri->is_static = 0; |  | ||||||
|         } else if (path_is_file(buf3)) { |  | ||||||
|             uri->filename = malloc(strlen(buf3) + 1); |  | ||||||
|             strcpy(uri->filename, buf3); |  | ||||||
|         } else { |         } else { | ||||||
|             if (dir_mode == URI_DIR_MODE_FORBIDDEN) { |             if (dir_mode == URI_DIR_MODE_FORBIDDEN) { | ||||||
|                 uri->is_static = 1; |                 uri->is_static = 1; | ||||||
| @@ -190,12 +188,12 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (strcmp(uri->path + strlen(uri->path) - 5, "index") == 0) { |     if (strends(uri->path + strlen(uri->path), "index")) { | ||||||
|         uri->path[strlen(uri->path) - 5] = 0; |         uri->path[strlen(uri->path) - 5] = 0; | ||||||
|     } |     } | ||||||
|     if (strcmp(uri->pathinfo, "index.ncr") == 0 || |     if (streq(uri->pathinfo, "index.php") || | ||||||
|         strcmp(uri->pathinfo, "index.php") == 0 || |         streq(uri->pathinfo, "index.html")) | ||||||
|         strcmp(uri->pathinfo, "index.html") == 0) { |     { | ||||||
|         uri->pathinfo[0] = 0; |         uri->pathinfo[0] = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ typedef struct { | |||||||
|     char charset[URI_CHARSET_SIZE]; |     char charset[URI_CHARSET_SIZE]; | ||||||
|     char filename_comp_gz[256]; |     char filename_comp_gz[256]; | ||||||
|     char filename_comp_br[256]; |     char filename_comp_br[256]; | ||||||
|     struct stat stat; |     long mtime; | ||||||
| } metadata_t; | } metadata_t; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|   | |||||||
							
								
								
									
										211
									
								
								src/lib/utils.c
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								src/lib/utils.c
									
									
									
									
									
								
							| @@ -12,6 +12,10 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <dirent.h> | ||||||
|  |  | ||||||
|  |  | ||||||
| static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||||||
| @@ -113,34 +117,44 @@ int url_decode(const char *str, char *dec, long *size) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int mime_is_compressible(const char *type) { | int mime_is_compressible(const char *restrict type) { | ||||||
|     if (type == NULL) return 0; |     if (type == NULL) return 0; | ||||||
|     char type_parsed[64]; |     char type_parsed[64]; | ||||||
|     snprintf(type_parsed, sizeof(type_parsed), "%s", type); |     snprintf(type_parsed, sizeof(type_parsed), "%s", type); | ||||||
|     char *pos = strchr(type_parsed, ';'); |     char *pos = strchr(type_parsed, ';'); | ||||||
|     if (pos != NULL) pos[0] = 0; |     if (pos != NULL) pos[0] = 0; | ||||||
|     return |     return | ||||||
|         strncmp(type_parsed, "text/", 5) == 0 || |         mime_is_text(type) || | ||||||
|         strncmp(type_parsed, "message/", 7) == 0 || |         streq(type_parsed, "application/vnd.ms-fontobject") || | ||||||
|         strstr(type_parsed, "+xml") != NULL || |         streq(type_parsed, "application/x-font-ttf") || | ||||||
|         strstr(type_parsed, "+json") != NULL || |         streq(type_parsed, "font/eot") || | ||||||
|         strcmp(type_parsed, "application/javascript") == 0 || |         streq(type_parsed, "font/opentype") || | ||||||
|         strcmp(type_parsed, "application/json") == 0 || |         streq(type_parsed, "image/bmp") || | ||||||
|         strcmp(type_parsed, "application/xml") == 0 || |         streq(type_parsed, "image/gif") || | ||||||
|         strcmp(type_parsed, "application/x-www-form-urlencoded") == 0 || |         streq(type_parsed, "image/vnd.microsoft.icon") || | ||||||
|         strcmp(type_parsed, "application/x-tex") == 0 || |         streq(type_parsed, "image/vnd.microsoft.iconbinary") || | ||||||
|         strcmp(type_parsed, "application/x-httpd-php") == 0 || |         streq(type_parsed, "image/x-icon"); | ||||||
|         strcmp(type_parsed, "application/x-latex") == 0 || | } | ||||||
|         strcmp(type_parsed, "application/vnd.ms-fontobject") == 0 || |  | ||||||
|         strcmp(type_parsed, "application/x-font-ttf") == 0 || | int mime_is_text(const char *restrict type) { | ||||||
|         strcmp(type_parsed, "application/x-javascript") == 0 || |     if (type == NULL) return 0; | ||||||
|         strcmp(type_parsed, "font/eot") == 0 || |     char type_parsed[64]; | ||||||
|         strcmp(type_parsed, "font/opentype") == 0 || |     snprintf(type_parsed, sizeof(type_parsed), "%s", type); | ||||||
|         strcmp(type_parsed, "image/bmp") == 0 || |     char *pos = strchr(type_parsed, ';'); | ||||||
|         strcmp(type_parsed, "image/gif") == 0 || |     if (pos != NULL) pos[0] = 0; | ||||||
|         strcmp(type_parsed, "image/vnd.microsoft.icon") == 0 || |     return | ||||||
|         strcmp(type_parsed, "image/vnd.microsoft.iconbinary") == 0 || |         strstarts(type_parsed, "text/") || | ||||||
|         strcmp(type_parsed, "image/x-icon") == 0; |         strstarts(type_parsed, "message/") || | ||||||
|  |         strends(type_parsed, "+xml") || | ||||||
|  |         strends(type_parsed, "+json") || | ||||||
|  |         streq(type_parsed, "application/javascript") || | ||||||
|  |         streq(type_parsed, "application/json") || | ||||||
|  |         streq(type_parsed, "application/xml") || | ||||||
|  |         streq(type_parsed, "application/x-www-form-urlencoded") || | ||||||
|  |         streq(type_parsed, "application/x-tex") || | ||||||
|  |         streq(type_parsed, "application/x-httpd-php") || | ||||||
|  |         streq(type_parsed, "application/x-latex") || | ||||||
|  |         streq(type_parsed, "application/x-javascript"); | ||||||
| } | } | ||||||
|  |  | ||||||
| int strcpy_rem_webroot(char *dst, const char *src, long len, const char *webroot) { | int strcpy_rem_webroot(char *dst, const char *src, long len, const char *webroot) { | ||||||
| @@ -183,6 +197,26 @@ int str_trim_lws(char **start, char **end) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int streq(const char *restrict str1, const char *restrict str2) { | ||||||
|  |     return str1 != NULL && str2 != NULL && strcmp(str1, str2) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int strcontains(const char *restrict haystack, const char *restrict needle) { | ||||||
|  |     return haystack != NULL && needle != NULL && strstr(haystack, needle) != NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int strstarts(const char *restrict str, const char *restrict prefix) { | ||||||
|  |     if (str == NULL || prefix == NULL) return 0; | ||||||
|  |     unsigned long l1 = strlen(str), l2 = strlen(prefix); | ||||||
|  |     return l2 <= l1 && strncmp(str, prefix, l2) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int strends(const char *restrict str, const char *restrict suffix) { | ||||||
|  |     if (str == NULL || suffix == NULL) return 0; | ||||||
|  |     unsigned long l1 = strlen(str), l2 = strlen(suffix); | ||||||
|  |     return l2 <= l1 && strcmp(str + l1 - l2, suffix) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len) { | int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len) { | ||||||
|     unsigned long out_len = 4 * ((data_len + 2) / 3); |     unsigned long out_len = 4 * ((data_len + 2) / 3); | ||||||
|     if (output_len != NULL) *output_len = out_len; |     if (output_len != NULL) *output_len = out_len; | ||||||
| @@ -216,3 +250,136 @@ long clock_cpu(void) { | |||||||
|     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time); |     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time); | ||||||
|     return time.tv_sec * 1000000000 + time.tv_nsec; |     return time.tv_sec * 1000000000 + time.tv_nsec; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | long stat_mtime(const char *filename) { | ||||||
|  |     struct stat stat_buf; | ||||||
|  |     stat(filename, &stat_buf); | ||||||
|  |     return stat_buf.st_mtime; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int rm_rf(const char *path) { | ||||||
|  |     struct stat stat_buf; | ||||||
|  |     if (lstat(path, &stat_buf) != 0) | ||||||
|  |         return (errno == ENOENT) ? 0 : -1; | ||||||
|  |  | ||||||
|  |     if (S_ISREG(stat_buf.st_mode)) { | ||||||
|  |         // regular file | ||||||
|  |         return unlink(path); | ||||||
|  |     } else if (S_ISLNK(stat_buf.st_mode)) { | ||||||
|  |         // link | ||||||
|  |         return unlink(path); | ||||||
|  |     } else if (S_ISDIR(stat_buf.st_mode)) { | ||||||
|  |         // directory | ||||||
|  |         char buf[FILENAME_MAX]; | ||||||
|  |         DIR *dir; | ||||||
|  |  | ||||||
|  |         // open directory | ||||||
|  |         if ((dir = opendir(path)) == NULL) | ||||||
|  |             return -1; | ||||||
|  |  | ||||||
|  |         // read directory | ||||||
|  |         for (struct dirent *ent; (ent = readdir(dir)) != NULL;) { | ||||||
|  |             if (streq(ent->d_name, ".") || streq(ent->d_name, "..")) | ||||||
|  |                 continue; | ||||||
|  |  | ||||||
|  |             snprintf(buf, sizeof(buf), "%s/%s", path, ent->d_name); | ||||||
|  |             if (rm_rf(buf) != 0) { | ||||||
|  |                 closedir(dir); | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // close and remove directory | ||||||
|  |         closedir(dir); | ||||||
|  |         return rmdir(path); | ||||||
|  |     } else { | ||||||
|  |         // other - not supported | ||||||
|  |         errno = ENOTSUP; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long fsize(FILE *file) { | ||||||
|  |     long cur_pos, len; | ||||||
|  |     if ((cur_pos = ftell(file)) == -1) | ||||||
|  |         return -1; | ||||||
|  |     if (fseek(file, 0, SEEK_END) != 0) | ||||||
|  |         return -1; | ||||||
|  |     if ((len = ftell(file)) == -1) | ||||||
|  |         return -1; | ||||||
|  |     if (fseek(file, cur_pos, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long flines(FILE *file) { | ||||||
|  |     long cur_pos, lines = 0; | ||||||
|  |     if ((cur_pos = ftell(file)) == -1) | ||||||
|  |         return -1; | ||||||
|  |     if (fseek(file, 0, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     for (int ch; (ch = fgetc(file)) != EOF;) { | ||||||
|  |         if (ch == '\n') lines++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fseek(file, cur_pos, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |     return lines + 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long file_get_line_pos(FILE *file, long line_num) { | ||||||
|  |     if (line_num < 1) { | ||||||
|  |         errno = EINVAL; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     long cur_pos; | ||||||
|  |     if ((cur_pos = ftell(file)) == -1) | ||||||
|  |         return -1; | ||||||
|  |     if (fseek(file, 0, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     long lines = 0, pos = 0; | ||||||
|  |     for (int ch; lines < line_num - 1 && (ch = fgetc(file)) != EOF; pos++) { | ||||||
|  |         if (ch == '\n') lines++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fseek(file, cur_pos, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |     return pos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fseekl(FILE *file, long line_num) { | ||||||
|  |     if (line_num < 1) { | ||||||
|  |         errno = EINVAL; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fseek(file, 0, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     long lines = 0; | ||||||
|  |     for (int ch; lines < line_num - 1 && (ch = fgetc(file)) != EOF;) { | ||||||
|  |         if (ch == '\n') lines++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long ftelll(FILE *file) { | ||||||
|  |     long cur_pos; | ||||||
|  |     if ((cur_pos = ftell(file)) == -1) | ||||||
|  |         return -1; | ||||||
|  |     if (fseek(file, 0, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     long lines = 0, pos = 0; | ||||||
|  |     for (int ch; pos < cur_pos && (ch = fgetc(file)) != EOF; pos++) { | ||||||
|  |         if (ch == '\n') lines++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fseek(file, cur_pos, SEEK_SET) != 0) | ||||||
|  |         return -1; | ||||||
|  |     return lines + 1; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -27,7 +27,9 @@ int url_encode(const void *in, size_t size_in, char *out, size_t size_out); | |||||||
|  |  | ||||||
| int url_decode(const char *str, char *dec, long *size); | int url_decode(const char *str, char *dec, long *size); | ||||||
|  |  | ||||||
| int mime_is_compressible(const char *type); | int mime_is_compressible(const char *restrict type); | ||||||
|  |  | ||||||
|  | int mime_is_text(const char *restrict type); | ||||||
|  |  | ||||||
| int strcpy_rem_webroot(char *dst, const char *str, long len, const char *webroot); | int strcpy_rem_webroot(char *dst, const char *str, long len, const char *webroot); | ||||||
|  |  | ||||||
| @@ -35,10 +37,32 @@ int str_trim(char **start, char **end); | |||||||
|  |  | ||||||
| int str_trim_lws(char **start, char **end); | int str_trim_lws(char **start, char **end); | ||||||
|  |  | ||||||
|  | int streq(const char *restrict str1, const char *restrict str2); | ||||||
|  |  | ||||||
|  | int strcontains(const char *restrict haystack, const char *restrict needle); | ||||||
|  |  | ||||||
|  | int strstarts(const char *restrict str, const char *restrict prefix); | ||||||
|  |  | ||||||
|  | int strends(const char *restrict str, const char *restrict suffix); | ||||||
|  |  | ||||||
| int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len); | int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len); | ||||||
|  |  | ||||||
| long clock_micros(void); | long clock_micros(void); | ||||||
|  |  | ||||||
| long clock_cpu(void); | long clock_cpu(void); | ||||||
|  |  | ||||||
|  | long stat_mtime(const char *filename); | ||||||
|  |  | ||||||
|  | int rm_rf(const char *path); | ||||||
|  |  | ||||||
|  | long fsize(FILE *file); | ||||||
|  |  | ||||||
|  | long flines(FILE *file); | ||||||
|  |  | ||||||
|  | long file_get_line_pos(FILE *file, long line_num); | ||||||
|  |  | ||||||
|  | int fseekl(FILE *file, long line_num); | ||||||
|  |  | ||||||
|  | long ftelll(FILE *file); | ||||||
|  |  | ||||||
| #endif //SESIMOS_UTILS_H | #endif //SESIMOS_UTILS_H | ||||||
|   | |||||||
| @@ -36,13 +36,9 @@ int ws_calc_accept_key(const char *key, char *accept_key) { | |||||||
| int ws_recv_frame_header(sock *s, ws_frame *frame) { | int ws_recv_frame_header(sock *s, ws_frame *frame) { | ||||||
|     unsigned char buf[12]; |     unsigned char buf[12]; | ||||||
|  |  | ||||||
|     long ret = sock_recv(s, buf, 2, 0); |     if (sock_recv_x(s, buf, 2, 0) == -1) { | ||||||
|     if (ret < 0) { |  | ||||||
|         error("Unable to receive from socket"); |         error("Unable to receive from socket"); | ||||||
|         return -1; |         return -1; | ||||||
|     } else if (ret != 2) { |  | ||||||
|         error("Unable to receive 2 bytes from socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     unsigned short bits = (buf[0] << 8) | buf[1]; |     unsigned short bits = (buf[0] << 8) | buf[1]; | ||||||
| @@ -61,13 +57,9 @@ int ws_recv_frame_header(sock *s, ws_frame *frame) { | |||||||
|         remaining += 8; |         remaining += 8; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = sock_recv(s, buf, remaining, 0); |     if (sock_recv_x(s, buf, remaining, 0) == -1) { | ||||||
|     if (ret < 0) { |  | ||||||
|         error("Unable to receive from socket"); |         error("Unable to receive from socket"); | ||||||
|         return -1; |         return -1; | ||||||
|     } else if (ret != remaining) { |  | ||||||
|         error("Unable to receive correct number of bytes from socket"); |  | ||||||
|         return -2; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (len == 126) { |     if (len == 126) { | ||||||
| @@ -125,7 +117,7 @@ int ws_send_frame_header(sock *s, ws_frame *frame) { | |||||||
|         ptr += 4; |         ptr += 4; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long ret = sock_send(s, buf, ptr - buf, frame->len != 0 ? MSG_MORE : 0); |     long ret = sock_send_x(s, buf, ptr - buf, frame->len != 0 ? MSG_MORE : 0); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         error("Unable to send to socket"); |         error("Unable to send to socket"); | ||||||
|         return -1; |         return -1; | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								src/logger.c
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								src/logger.c
									
									
									
									
									
								
							| @@ -24,10 +24,13 @@ | |||||||
| #define LOG_NAME_LEN 12 | #define LOG_NAME_LEN 12 | ||||||
| #define LOG_PREFIX_LEN 256 | #define LOG_PREFIX_LEN 256 | ||||||
|  |  | ||||||
| #define LOG_PREFIX "[%-8s][%-6s]" | #define LOG_PREFIX "[%8s][%-8s][%-6s]" | ||||||
|  | #define LOG_TIME_BUF_SIZE 9 | ||||||
|  | #define LOG_TIME_FMT "%H:%M:%S" | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     log_lvl_t lvl; |     log_lvl_t lvl; | ||||||
|  |     long time; | ||||||
|     char name[LOG_NAME_LEN]; |     char name[LOG_NAME_LEN]; | ||||||
|     char prefix[LOG_PREFIX_LEN]; |     char prefix[LOG_PREFIX_LEN]; | ||||||
|     char txt[LOG_MAX_MSG_SIZE]; |     char txt[LOG_MAX_MSG_SIZE]; | ||||||
| @@ -58,15 +61,20 @@ static const char *level_keywords[] = { | |||||||
|         "DEBUG" |         "DEBUG" | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static const char *timestr(time_t ts, char *buf) { | ||||||
|  |     struct tm time_info; | ||||||
|  |     strftime(buf, LOG_TIME_BUF_SIZE, LOG_TIME_FMT, localtime_r(&ts, &time_info)); | ||||||
|  |     return buf; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void err(const char *restrict msg) { | static void err(const char *restrict msg) { | ||||||
|     char err_buf[64]; |     char err_buf[64], time_buf[LOG_TIME_BUF_SIZE]; | ||||||
|     fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger", |     fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", timestr(time(NULL), time_buf), "logger", | ||||||
|             level_keywords[LOG_CRITICAL], msg, error_str(errno, err_buf, sizeof(err_buf))); |             level_keywords[LOG_CRITICAL], msg, error_str(errno, err_buf, sizeof(err_buf))); | ||||||
| } | } | ||||||
|  |  | ||||||
| void logmsgf(log_lvl_t level, const char *restrict format, ...) { | void logmsgf(log_lvl_t level, const char *restrict format, ...) { | ||||||
|     char buf[256]; |     char buf[256], err_buf[256], time_buf[LOG_TIME_BUF_SIZE]; | ||||||
|     char err_buf[64]; |  | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
|  |  | ||||||
| @@ -85,7 +93,11 @@ void logmsgf(log_lvl_t level, const char *restrict format, ...) { | |||||||
|     if (!alive) { |     if (!alive) { | ||||||
|         // no logger thread running |         // no logger thread running | ||||||
|         // simply write to stdout without synchronization |         // simply write to stdout without synchronization | ||||||
|         printf("%s" LOG_PREFIX "%s%s ", color, (name != NULL) ? (char *) name : "", level_keywords[level], CLR_STR, (prefix != NULL) ? (char *) prefix : ""); |         printf("%s" LOG_PREFIX "%s%s ", color, | ||||||
|  |                timestr(time(NULL), time_buf), | ||||||
|  |                (name != NULL) ? (char *) name : "", | ||||||
|  |                level_keywords[level], CLR_STR, | ||||||
|  |                (prefix != NULL) ? (char *) prefix : ""); | ||||||
|         vprintf(buf, args); |         vprintf(buf, args); | ||||||
|         printf("\n"); |         printf("\n"); | ||||||
|     } else { |     } else { | ||||||
| @@ -126,6 +138,7 @@ void logmsgf(log_lvl_t level, const char *restrict format, ...) { | |||||||
|  |  | ||||||
|         vsnprintf(msg->txt, sizeof(msg->txt), buf, args); |         vsnprintf(msg->txt, sizeof(msg->txt), buf, args); | ||||||
|         msg->lvl = level; |         msg->lvl = level; | ||||||
|  |         msg->time = time(NULL); | ||||||
|  |  | ||||||
|         if (name != NULL) { |         if (name != NULL) { | ||||||
|             snprintf(msg->name, sizeof(msg->name), "%s", (char *) name); |             snprintf(msg->name, sizeof(msg->name), "%s", (char *) name); | ||||||
| @@ -165,14 +178,15 @@ static int logger_remaining(void) { | |||||||
| void logger_set_name(const char *restrict format, ...) { | void logger_set_name(const char *restrict format, ...) { | ||||||
|     va_list args; |     va_list args; | ||||||
|  |  | ||||||
|  |     void *ptr; | ||||||
|     if (key_name == -1) { |     if (key_name == -1) { | ||||||
|         // not initialized |         // not initialized | ||||||
|         va_start(args, format); |         va_start(args, format); | ||||||
|         vsnprintf(global_name, sizeof(global_name), format, args); |         vsnprintf(global_name, sizeof(global_name), format, args); | ||||||
|  |         ptr = global_name; | ||||||
|     } else { |     } else { | ||||||
|         int ret; |         int ret; | ||||||
|         void *ptr = pthread_getspecific(key_name); |         if ((ptr = pthread_getspecific(key_name)) == NULL) { | ||||||
|         if (!ptr) { |  | ||||||
|             ptr = malloc(LOG_NAME_LEN); |             ptr = malloc(LOG_NAME_LEN); | ||||||
|             if ((ret = pthread_setspecific(key_name, ptr)) != 0) { |             if ((ret = pthread_setspecific(key_name, ptr)) != 0) { | ||||||
|                 errno = ret; |                 errno = ret; | ||||||
| @@ -185,6 +199,10 @@ void logger_set_name(const char *restrict format, ...) { | |||||||
|         vsnprintf(ptr, LOG_NAME_LEN, format, args); |         vsnprintf(ptr, LOG_NAME_LEN, format, args); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // set thread name | ||||||
|  |     // warning: max length is 16 (incl. terminating null byte) | ||||||
|  |     pthread_setname_np(pthread_self(), ptr); | ||||||
|  |  | ||||||
|     // cleanup |     // cleanup | ||||||
|     va_end(args); |     va_end(args); | ||||||
| } | } | ||||||
| @@ -216,6 +234,8 @@ void logger_set_prefix(const char *restrict format, ...) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static void *logger_thread(void *arg) { | static void *logger_thread(void *arg) { | ||||||
|  |     char time_buf[LOG_TIME_BUF_SIZE]; | ||||||
|  |  | ||||||
|     logger_set_name("logger"); |     logger_set_name("logger"); | ||||||
|     alive = 1; |     alive = 1; | ||||||
|  |  | ||||||
| @@ -237,6 +257,7 @@ static void *logger_thread(void *arg) { | |||||||
|  |  | ||||||
|         printf("%s" LOG_PREFIX "%s%s %s\n", |         printf("%s" LOG_PREFIX "%s%s %s\n", | ||||||
|                (msg->lvl <= LOG_ERROR) ? ERR_STR : ((msg->lvl <= LOG_WARNING) ? WRN_STR : ""), |                (msg->lvl <= LOG_ERROR) ? ERR_STR : ((msg->lvl <= LOG_WARNING) ? WRN_STR : ""), | ||||||
|  |                (timestr(msg->time, time_buf)), | ||||||
|                (msg->name[0] != 0) ? (char *) msg->name : "", level_keywords[msg->lvl], CLR_STR, |                (msg->name[0] != 0) ? (char *) msg->name : "", level_keywords[msg->lvl], CLR_STR, | ||||||
|                (msg->prefix[0] != 0) ? (char *) msg->prefix : "",  msg->txt); |                (msg->prefix[0] != 0) ? (char *) msg->prefix : "",  msg->txt); | ||||||
|  |  | ||||||
| @@ -278,4 +299,9 @@ int logger_init(void) { | |||||||
|  |  | ||||||
| void logger_stop(void) { | void logger_stop(void) { | ||||||
|     alive = 0; |     alive = 0; | ||||||
|  |     pthread_kill(thread, SIGUSR1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int logger_join(void) { | ||||||
|  |     return pthread_join(thread, NULL); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,4 +37,6 @@ int logger_init(void); | |||||||
|  |  | ||||||
| void logger_stop(void); | void logger_stop(void); | ||||||
|  |  | ||||||
|  | int logger_join(void); | ||||||
|  |  | ||||||
| #endif //SESIMOS_LOGGER_H | #endif //SESIMOS_LOGGER_H | ||||||
|   | |||||||
							
								
								
									
										183
									
								
								src/server.c
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								src/server.c
									
									
									
									
									
								
							| @@ -32,21 +32,43 @@ | |||||||
| #include <openssl/pem.h> | #include <openssl/pem.h> | ||||||
| #include <openssl/ssl.h> | #include <openssl/ssl.h> | ||||||
| #include <openssl/conf.h> | #include <openssl/conf.h> | ||||||
|  | #include <semaphore.h> | ||||||
|  |  | ||||||
| const char *config_file; | const char *config_file; | ||||||
|  |  | ||||||
| static int sockets[NUM_SOCKETS]; | static int sockets[NUM_SOCKETS]; | ||||||
| static SSL_CTX *contexts[CONFIG_MAX_CERT_CONFIG]; | static SSL_CTX *contexts[CONFIG_MAX_CERT_CONFIG]; | ||||||
|  |  | ||||||
| static client_ctx_t **clients; | static client_ctx_t **clients; | ||||||
|  | static sem_t sem_clients_lock; | ||||||
|  |  | ||||||
| static const char *color_table[] = {"\x1B[31m", "\x1B[32m", "\x1B[33m", "\x1B[34m", "\x1B[35m", "\x1B[36m"}; | static void clean(void) { | ||||||
|  |     notice("Cleaning sesimos cache and metadata files..."); | ||||||
|  |  | ||||||
| static int clean() { |     // remove legacy files | ||||||
|     remove("/var/sesimos/server/cache"); |     //     /.../server/, /.../server/cache | ||||||
|     rmdir("/var/sesimos/server/"); |     if (rm_rf("/var/sesimos/server") != 0) { | ||||||
|     return 0; |         error("Unable to remove /var/sesimos/server/"); | ||||||
|  |     } else if (!errno) { | ||||||
|  |         notice("Successfully removed /var/sesimos/server/"); | ||||||
|  |     } | ||||||
|  |     errno = 0; | ||||||
|  |  | ||||||
|  |     // remove cache and metadata files | ||||||
|  |     char buf[512]; | ||||||
|  |     for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { | ||||||
|  |         host_config_t *hc = &config.hosts[i]; | ||||||
|  |         if (hc->type == CONFIG_TYPE_UNSET) break; | ||||||
|  |         if (hc->type != CONFIG_TYPE_LOCAL) continue; | ||||||
|  |  | ||||||
|  |         snprintf(buf, sizeof(buf), "%s/.sesimos", hc->local.webroot); | ||||||
|  |         if (rm_rf(buf) != 0) { | ||||||
|  |             error("Unable to remove %s/", buf); | ||||||
|  |         } else if (!errno) { | ||||||
|  |             notice("Successfully removed %s/", buf); | ||||||
|  |         } | ||||||
|  |         errno = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     notice("Cleaned all sesimos cache and metadata files!"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { | static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { | ||||||
| @@ -59,15 +81,39 @@ static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void server_free_client(client_ctx_t *ctx) { | void server_free_client(client_ctx_t *ctx) { | ||||||
|     for (int i = 0; i < list_size(clients); i++) { |     // try to lock clients list | ||||||
|         if (clients[i] == ctx) { |     retry: | ||||||
|             clients = list_remove(clients, i); |     if (sem_wait(&sem_clients_lock) != 0) { | ||||||
|             break; |         if (errno == EINTR) { | ||||||
|  |             goto retry; | ||||||
|  |         } else { | ||||||
|  |             critical("Unable to lock clients list"); | ||||||
|  |             return; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // delete from list | ||||||
|  |     clients = list_delete(clients, &ctx); | ||||||
|  |     if (clients == NULL) { | ||||||
|  |         critical("Unable to delete context from list"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // unlock clients list | ||||||
|  |     sem_post(&sem_clients_lock); | ||||||
|  |  | ||||||
|  |     // free data | ||||||
|     free(ctx); |     free(ctx); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void ssl_free() { | ||||||
|  |     for (int i = 0; i < CONFIG_MAX_CERT_CONFIG; i++) { | ||||||
|  |         const cert_config_t *conf = &config.certs[i]; | ||||||
|  |         if (conf->name[0] == 0) break; | ||||||
|  |         SSL_CTX_free(contexts[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static void accept_cb(void *arg) { | static void accept_cb(void *arg) { | ||||||
|     int i = (int) (((int *) arg) - sockets); |     int i = (int) (((int *) arg) - sockets); | ||||||
|     int fd = sockets[i]; |     int fd = sockets[i]; | ||||||
| @@ -78,15 +124,6 @@ static void accept_cb(void *arg) { | |||||||
|         errno = 0; |         errno = 0; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     clients = list_append(clients, &client_ctx); |  | ||||||
|     if (clients == NULL) { |  | ||||||
|         critical("Unable to add client context to list"); |  | ||||||
|         errno = 0; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     client_ctx->in_use = 1; |  | ||||||
|     sock *client = &client_ctx->socket; |     sock *client = &client_ctx->socket; | ||||||
|  |  | ||||||
|     client->ctx = contexts[0]; |     client->ctx = contexts[0]; | ||||||
| @@ -94,6 +131,7 @@ static void accept_cb(void *arg) { | |||||||
|     int client_fd = accept(fd, &client->_addr.sock, &addr_len); |     int client_fd = accept(fd, &client->_addr.sock, &addr_len); | ||||||
|     if (client_fd < 0) { |     if (client_fd < 0) { | ||||||
|         critical("Unable to accept connection"); |         critical("Unable to accept connection"); | ||||||
|  |         free(client_ctx); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -104,6 +142,29 @@ static void accept_cb(void *arg) { | |||||||
|     client_ctx->cnx_s = client->ts_start; |     client_ctx->cnx_s = client->ts_start; | ||||||
|     client_ctx->cnx_e = -1, client_ctx->req_s = -1, client_ctx->req_e = -1, client_ctx->res_ts = -1; |     client_ctx->cnx_e = -1, client_ctx->req_s = -1, client_ctx->req_e = -1, client_ctx->res_ts = -1; | ||||||
|  |  | ||||||
|  |     // try to lock clients list | ||||||
|  |     retry: | ||||||
|  |     if (sem_wait(&sem_clients_lock) != 0) { | ||||||
|  |         if (errno == EINTR) { | ||||||
|  |             goto retry; | ||||||
|  |         } else { | ||||||
|  |             critical("Unable to lock clients list"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // append to list | ||||||
|  |     clients = list_append(clients, &client_ctx); | ||||||
|  |     if (clients == NULL) { | ||||||
|  |         critical("Unable to add client context to list"); | ||||||
|  |         free(client_ctx); | ||||||
|  |         errno = 0; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // unlock clients list | ||||||
|  |     sem_post(&sem_clients_lock); | ||||||
|  |  | ||||||
|     tcp_accept(client_ctx); |     tcp_accept(client_ctx); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -140,11 +201,11 @@ static void terminate_gracefully(int sig) { | |||||||
|     workers_stop(); |     workers_stop(); | ||||||
|     workers_destroy(); |     workers_destroy(); | ||||||
|  |  | ||||||
|     for (int i = 0; i < list_size(clients); i++) { |     while (list_size(clients) > 0) | ||||||
|         tcp_close(clients[i]); |         tcp_close(clients[0]); | ||||||
|     } |  | ||||||
|     proxy_close_all(); |  | ||||||
|     logger_set_prefix(""); |     logger_set_prefix(""); | ||||||
|  |     proxy_close_all(); | ||||||
|  |  | ||||||
|     async_stop(); |     async_stop(); | ||||||
| } | } | ||||||
| @@ -154,21 +215,15 @@ static void nothing(int sig) {} | |||||||
| int main(int argc, char *const argv[]) { | int main(int argc, char *const argv[]) { | ||||||
|     const int YES = 1; |     const int YES = 1; | ||||||
|     int ret; |     int ret; | ||||||
|  |     int mode = 0; | ||||||
|  |  | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|     clients = list_create(sizeof(void *), 64); |  | ||||||
|     if (clients == NULL) { |  | ||||||
|         critical("Unable to initialize client list"); |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const struct sockaddr_in6 addresses[2] = { |     const struct sockaddr_in6 addresses[2] = { | ||||||
|             {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(80)}, |             {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(80)}, | ||||||
|             {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(443)} |             {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(443)} | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     logger_init(); |  | ||||||
|  |  | ||||||
|     logger_set_name("server"); |     logger_set_name("server"); | ||||||
|  |  | ||||||
|     if (setvbuf(stdout, NULL, _IOLBF, 0) != 0 || setvbuf(stderr, NULL, _IOLBF, 0) != 0) { |     if (setvbuf(stdout, NULL, _IOLBF, 0) != 0 || setvbuf(stderr, NULL, _IOLBF, 0) != 0) { | ||||||
| @@ -179,13 +234,13 @@ int main(int argc, char *const argv[]) { | |||||||
|  |  | ||||||
|     static const struct option long_opts[] = { |     static const struct option long_opts[] = { | ||||||
|             {"help",    no_argument,        0, 'h'}, |             {"help",    no_argument,        0, 'h'}, | ||||||
|  |             {"clean",   no_argument,        0, 'C'}, | ||||||
|             {"config",  required_argument,  0, 'c'}, |             {"config",  required_argument,  0, 'c'}, | ||||||
|             { 0,        0,                  0,  0 } |             { 0,        0,                  0,  0 } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     config_file = NULL; |     config_file = NULL; | ||||||
|     int c, opt_idx; |     for (int c, opt_idx; (c = getopt_long(argc, argv, "hCc:", long_opts, &opt_idx)) != -1;) { | ||||||
|     while ((c = getopt_long(argc, argv, "hc:", long_opts, &opt_idx)) != -1) { |  | ||||||
|         switch (c) { |         switch (c) { | ||||||
|             case 'h': |             case 'h': | ||||||
|                 fprintf(stderr, |                 fprintf(stderr, | ||||||
| @@ -193,11 +248,15 @@ int main(int argc, char *const argv[]) { | |||||||
|                         "\n" |                         "\n" | ||||||
|                         "Options:\n" |                         "Options:\n" | ||||||
|                         "  -c, --config <CONFIG-FILE>  path to the config file. If not provided, default will be used\n" |                         "  -c, --config <CONFIG-FILE>  path to the config file. If not provided, default will be used\n" | ||||||
|  |                         "  -C, --clean                 clear cached files and other metadata\n" | ||||||
|                         "  -h, --help                  print this dialogue\n"); |                         "  -h, --help                  print this dialogue\n"); | ||||||
|                 return 0; |                 return 0; | ||||||
|             case 'c': |             case 'c': | ||||||
|                 config_file = optarg; |                 config_file = optarg; | ||||||
|                 break; |                 break; | ||||||
|  |             case 'C': | ||||||
|  |                 mode = 1; | ||||||
|  |                 break; | ||||||
|             case '?': |             case '?': | ||||||
|             default: |             default: | ||||||
|                 critical("Unable to parse arguments"); |                 critical("Unable to parse arguments"); | ||||||
| @@ -213,6 +272,11 @@ int main(int argc, char *const argv[]) { | |||||||
|     if (config_load(config_file == NULL ? DEFAULT_CONFIG_FILE : config_file) != 0) |     if (config_load(config_file == NULL ? DEFAULT_CONFIG_FILE : config_file) != 0) | ||||||
|         return 1; |         return 1; | ||||||
|  |  | ||||||
|  |     if (mode == 1) { | ||||||
|  |         clean(); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ((sockets[0] = socket(AF_INET6, SOCK_STREAM, 0)) == -1 || |     if ((sockets[0] = socket(AF_INET6, SOCK_STREAM, 0)) == -1 || | ||||||
|         (sockets[1] = socket(AF_INET6, SOCK_STREAM, 0)) == -1) |         (sockets[1] = socket(AF_INET6, SOCK_STREAM, 0)) == -1) | ||||||
|     { |     { | ||||||
| @@ -249,12 +313,6 @@ int main(int argc, char *const argv[]) { | |||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((ret = cache_init()) != 0) { |  | ||||||
|         geoip_free(); |  | ||||||
|         if (ret == -1) critical("Unable to initialize cache"); |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < CONFIG_MAX_CERT_CONFIG; i++) { |     for (int i = 0; i < CONFIG_MAX_CERT_CONFIG; i++) { | ||||||
|         const cert_config_t *conf = &config.certs[i]; |         const cert_config_t *conf = &config.certs[i]; | ||||||
|         if (conf->name[0] == 0) break; |         if (conf->name[0] == 0) break; | ||||||
| @@ -281,15 +339,35 @@ int main(int argc, char *const argv[]) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     clients = list_create(sizeof(client_ctx_t *), 64); | ||||||
|  |     if (clients == NULL) { | ||||||
|  |         critical("Unable to initialize client list"); | ||||||
|  |         ssl_free(); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sem_init(&sem_clients_lock, 0, 1) != 0) { | ||||||
|  |         critical("Unable to create clients lock semaphore"); | ||||||
|  |         ssl_free(); | ||||||
|  |         list_free(clients); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (async_init() != 0) { |     if (async_init() != 0) { | ||||||
|         critical("Unable to initialize async thread"); |         critical("Unable to initialize async thread"); | ||||||
|  |         ssl_free(); | ||||||
|         geoip_free(); |         geoip_free(); | ||||||
|  |         list_free(clients); | ||||||
|  |         sem_destroy(&sem_clients_lock); | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (proxy_preload() != 0) { |     if (proxy_preload() != 0) { | ||||||
|         critical("Unable to initialize proxy"); |         critical("Unable to initialize proxy"); | ||||||
|  |         ssl_free(); | ||||||
|         geoip_free(); |         geoip_free(); | ||||||
|  |         list_free(clients); | ||||||
|  |         sem_destroy(&sem_clients_lock); | ||||||
|         async_free(); |         async_free(); | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
| @@ -297,12 +375,33 @@ int main(int argc, char *const argv[]) { | |||||||
|     for (int i = 0; i < NUM_SOCKETS; i++) { |     for (int i = 0; i < NUM_SOCKETS; i++) { | ||||||
|         if (listen(sockets[i], LISTEN_BACKLOG) < 0) { |         if (listen(sockets[i], LISTEN_BACKLOG) < 0) { | ||||||
|             critical("Unable to listen on socket %i", i); |             critical("Unable to listen on socket %i", i); | ||||||
|  |             ssl_free(); | ||||||
|             geoip_free(); |             geoip_free(); | ||||||
|  |             list_free(clients); | ||||||
|  |             sem_destroy(&sem_clients_lock); | ||||||
|  |             async_free(); | ||||||
|             proxy_unload(); |             proxy_unload(); | ||||||
|             return 1; |             return 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     logger_init(); | ||||||
|  |  | ||||||
|  |     if ((ret = cache_init()) != 0) { | ||||||
|  |         if (ret == -1) critical("Unable to initialize cache"); | ||||||
|  |         ssl_free(); | ||||||
|  |         geoip_free(); | ||||||
|  |         list_free(clients); | ||||||
|  |         sem_destroy(&sem_clients_lock); | ||||||
|  |         async_free(); | ||||||
|  |         proxy_unload(); | ||||||
|  |         logger_stop(); | ||||||
|  |         logger_join(); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     logger_set_name("main"); | ||||||
|  |  | ||||||
|     workers_init(); |     workers_init(); | ||||||
|  |  | ||||||
|     for (int i = 0; i < NUM_SOCKETS; i++) { |     for (int i = 0; i < NUM_SOCKETS; i++) { | ||||||
| @@ -316,10 +415,14 @@ int main(int argc, char *const argv[]) { | |||||||
|     notice("Goodbye!"); |     notice("Goodbye!"); | ||||||
|  |  | ||||||
|     // cleanup |     // cleanup | ||||||
|  |     ssl_free(); | ||||||
|     list_free(clients); |     list_free(clients); | ||||||
|  |     sem_destroy(&sem_clients_lock); | ||||||
|     geoip_free(); |     geoip_free(); | ||||||
|     proxy_unload(); |     proxy_unload(); | ||||||
|     cache_join(); |     cache_join(); | ||||||
|     async_free(); |     async_free(); | ||||||
|  |     logger_stop(); | ||||||
|  |     logger_join(); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx); | |||||||
| static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx); | static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx); | ||||||
|  |  | ||||||
| void fastcgi_handler_func(client_ctx_t *ctx) { | void fastcgi_handler_func(client_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); |     logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); | ||||||
|  |  | ||||||
|     fastcgi_cnx_t fcgi_cnx; |     fastcgi_cnx_t fcgi_cnx; | ||||||
|     int ret = fastcgi_handler_1(ctx, &fcgi_cnx); |     int ret = fastcgi_handler_1(ctx, &fcgi_cnx); | ||||||
| @@ -38,7 +38,8 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
|     sock *client = &ctx->socket; |     sock *client = &ctx->socket; | ||||||
|     char *err_msg = ctx->err_msg; |     char *err_msg = ctx->err_msg; | ||||||
|  |  | ||||||
|     fcgi_cnx->socket = 0; |     fcgi_cnx->socket.socket = 0; | ||||||
|  |     fcgi_cnx->socket.enc = 0; | ||||||
|     fcgi_cnx->req_id = 0; |     fcgi_cnx->req_id = 0; | ||||||
|     fcgi_cnx->r_addr = ctx->socket.addr; |     fcgi_cnx->r_addr = ctx->socket.addr; | ||||||
|     fcgi_cnx->r_host = (ctx->host[0] != 0) ? ctx->host : NULL; |     fcgi_cnx->r_host = (ctx->host[0] != 0) ? ctx->host : NULL; | ||||||
| @@ -46,10 +47,8 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
|     char buf[1024]; |     char buf[1024]; | ||||||
|  |  | ||||||
|     int mode, ret; |     int mode, ret; | ||||||
|     if (strcmp(uri->filename + strlen(uri->filename) - 4, ".ncr") == 0) { |     if (strends(uri->filename, ".php")) { | ||||||
|         mode = FASTCGI_SESIMOS; |         mode = FASTCGI_BACKEND_PHP; | ||||||
|     } else if (strcmp(uri->filename + strlen(uri->filename) - 4, ".php") == 0) { |  | ||||||
|         mode = FASTCGI_PHP; |  | ||||||
|     } else { |     } else { | ||||||
|         res->status = http_get_status(500); |         res->status = http_get_status(500); | ||||||
|         error("Invalid FastCGI extension: %s", uri->filename); |         error("Invalid FastCGI extension: %s", uri->filename); | ||||||
| @@ -73,7 +72,7 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
|     if (client_content_length != NULL) { |     if (client_content_length != NULL) { | ||||||
|         unsigned long client_content_len = strtoul(client_content_length, NULL, 10); |         unsigned long client_content_len = strtoul(client_content_length, NULL, 10); | ||||||
|         ret = fastcgi_receive(fcgi_cnx, client, client_content_len); |         ret = fastcgi_receive(fcgi_cnx, client, client_content_len); | ||||||
|     } else if (client_transfer_encoding != NULL && strstr(client_transfer_encoding, "chunked") != NULL) { |     } else if (strcontains(client_transfer_encoding, "chunked")) { | ||||||
|         ret = fastcgi_receive_chunked(fcgi_cnx, client); |         ret = fastcgi_receive_chunked(fcgi_cnx, client); | ||||||
|     } else { |     } else { | ||||||
|         ret = 0; |         ret = 0; | ||||||
| @@ -118,7 +117,7 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
|     const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); |     const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); | ||||||
|     if (content_encoding == NULL && |     if (content_encoding == NULL && | ||||||
|         content_type != NULL && |         content_type != NULL && | ||||||
|         strncmp(content_type, "text/html", 9) == 0 && |         strstarts(content_type, "text/html") && | ||||||
|         ctx->content_length != -1 && |         ctx->content_length != -1 && | ||||||
|         ctx->content_length <= sizeof(ctx->msg_content) - 1) |         ctx->content_length <= sizeof(ctx->msg_content) - 1) | ||||||
|     { |     { | ||||||
| @@ -127,27 +126,8 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ctx->use_fastcgi = 1; |     ctx->use_fastcgi = 1; | ||||||
|  |  | ||||||
|     if (ctx->content_length != -1 && ctx->content_length < 1024000) { |  | ||||||
|         ctx->use_fastcgi |= FASTCGI_COMPRESS_HOLD; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ctx->content_length = -1; |     ctx->content_length = -1; | ||||||
|  |  | ||||||
|     int http_comp = http_get_compression(req, res); |  | ||||||
|     if (http_comp & COMPRESS) { |  | ||||||
|         if (http_comp & COMPRESS_BR) { |  | ||||||
|             ctx->use_fastcgi |= FASTCGI_COMPRESS_BR; |  | ||||||
|             sprintf(buf, "br"); |  | ||||||
|         } else if (http_comp & COMPRESS_GZ) { |  | ||||||
|             ctx->use_fastcgi |= FASTCGI_COMPRESS_GZ; |  | ||||||
|             sprintf(buf, "gzip"); |  | ||||||
|         } |  | ||||||
|         http_add_header_field(&res->hdr, "Vary", "Accept-Encoding"); |  | ||||||
|         http_add_header_field(&res->hdr, "Content-Encoding", buf); |  | ||||||
|         http_remove_header_field(&res->hdr, "Content-Length", HTTP_REMOVE_ALL); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (http_get_header_field(&res->hdr, "Content-Length") == NULL) { |     if (http_get_header_field(&res->hdr, "Content-Length") == NULL) { | ||||||
|         http_add_header_field(&res->hdr, "Transfer-Encoding", "chunked"); |         http_add_header_field(&res->hdr, "Transfer-Encoding", "chunked"); | ||||||
|     } |     } | ||||||
| @@ -156,19 +136,17 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { | ||||||
|     const char *transfer_encoding = http_get_header_field(&ctx->res.hdr, "Transfer-Encoding"); |     int chunked = strcontains(http_get_header_field(&ctx->res.hdr, "Transfer-Encoding"), "chunked"); | ||||||
|     int chunked = (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL); |  | ||||||
|  |  | ||||||
|     int flags = (chunked ? FASTCGI_CHUNKED : 0) | (ctx->use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD)); |     int flags = (chunked ? FASTCGI_CHUNKED : 0); | ||||||
|     int ret = fastcgi_send(fcgi_cnx, &ctx->socket, flags); |     int ret = fastcgi_send(fcgi_cnx, &ctx->socket, flags); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         ctx->c_keep_alive = 0; |         ctx->c_keep_alive = 0; | ||||||
|         errno = 0; |         errno = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fcgi_cnx->socket != 0) { |     if (fcgi_cnx->socket.socket != 0) { | ||||||
|         close(fcgi_cnx->socket); |         sock_close(&fcgi_cnx->socket); | ||||||
|         fcgi_cnx->socket = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #ifndef SESIMOS_FUNC_H | #ifndef SESIMOS_FUNC_H | ||||||
| #define SESIMOS_FUNC_H | #define SESIMOS_FUNC_H | ||||||
|  |  | ||||||
|  | #include "../defs.h" | ||||||
| #include "../lib/sock.h" | #include "../lib/sock.h" | ||||||
| #include "../lib/http.h" | #include "../lib/http.h" | ||||||
| #include "../lib/uri.h" | #include "../lib/uri.h" | ||||||
| @@ -18,7 +19,7 @@ | |||||||
| typedef struct { | typedef struct { | ||||||
|     sock socket; |     sock socket; | ||||||
|     int req_num; |     int req_num; | ||||||
|     unsigned char in_use: 1, s_keep_alive:1, c_keep_alive:1, use_fastcgi:4, use_proxy:2, ws_close:2; |     unsigned char s_keep_alive:1, c_keep_alive:1, use_fastcgi:4, use_proxy:2, ws_close:2; | ||||||
|     char cc[3], host[256]; |     char cc[3], host[256]; | ||||||
|     char req_host[256], err_msg[256]; |     char req_host[256], err_msg[256]; | ||||||
|     char log_prefix[128]; |     char log_prefix[128]; | ||||||
| @@ -62,8 +63,6 @@ void timeout_request(client_ctx_t *ctx); | |||||||
|  |  | ||||||
| void tcp_close(client_ctx_t *ctx); | void tcp_close(client_ctx_t *ctx); | ||||||
|  |  | ||||||
| void proxy_close(proxy_ctx_t *ctx); |  | ||||||
|  |  | ||||||
| int ws_handle_connection(client_ctx_t *ctx); | int ws_handle_connection(client_ctx_t *ctx); | ||||||
|  |  | ||||||
| void ws_close(ws_ctx_t *ctx); | void ws_close(ws_ctx_t *ctx); | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ | |||||||
| static int local_handler(client_ctx_t *ctx); | static int local_handler(client_ctx_t *ctx); | ||||||
|  |  | ||||||
| void local_handler_func(client_ctx_t *ctx) { | void local_handler_func(client_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); |     logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); | ||||||
|  |  | ||||||
|     switch (local_handler(ctx)) { |     switch (local_handler(ctx)) { | ||||||
|         case 0: |         case 0: | ||||||
| @@ -36,6 +36,76 @@ void local_handler_func(client_ctx_t *ctx) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int range_handler(client_ctx_t *ctx) { | ||||||
|  |     char buf[64]; | ||||||
|  |     long num0, num1, num2; | ||||||
|  |     char *ptr; | ||||||
|  |     int mode; | ||||||
|  |     const char *range = http_get_header_field(&ctx->req.hdr, "Range"); | ||||||
|  |  | ||||||
|  |     if (strcontains(range, ",")) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     ctx->file = fopen(ctx->uri.filename, "rb"); | ||||||
|  |  | ||||||
|  |     if (strstarts(range, "bytes=")) { | ||||||
|  |         mode = 0; | ||||||
|  |         range += 6; | ||||||
|  |         num0 = fsize(ctx->file), num1 = 0, num2 = num0 - 1; | ||||||
|  |         sprintf(buf, "bytes */%li", num0); | ||||||
|  |     } else if (strstarts(range, "lines=") && mime_is_text(ctx->uri.meta->type)) { | ||||||
|  |         mode = 1; | ||||||
|  |         range += 6; | ||||||
|  |         num0 = flines(ctx->file), num1 = 1, num2 = num0; | ||||||
|  |         sprintf(buf, "lines */%li", num0); | ||||||
|  |     } else { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     http_add_header_field(&ctx->res.hdr, "Content-Range", buf); | ||||||
|  |  | ||||||
|  |     if ((ptr = strchr(range, '-')) == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (num0 == 0) { | ||||||
|  |         ctx->content_length = 0; | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ptr[1] != 0) num2 = (long) strtoul(ptr + 1, NULL, 10); | ||||||
|  |     if (ptr != range) { | ||||||
|  |         num1 = (long) strtoul(range, NULL, 10); | ||||||
|  |     } else { | ||||||
|  |         if (mode == 0) { | ||||||
|  |             num1 = num0 - num2 - 1; | ||||||
|  |             num2 = num0 - 1; | ||||||
|  |         } else if (mode == 1) { | ||||||
|  |             num1 = num0 - num2 + 1; | ||||||
|  |             num2 = num0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((mode == 0 && (num1 >= num0 || num2 >= num0 || num1 > num2 || num1 < 0 || num2 < 0)) || | ||||||
|  |         (mode == 1 && (num1 > num0 || num2 > num0 || num1 > num2 || num1 <= 0 || num2 <= 0))) | ||||||
|  |     { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sprintf(buf, "%s %li-%li/%li", (mode == 0) ? "bytes" : "lines", num1, num2, num0); | ||||||
|  |     http_remove_header_field(&ctx->res.hdr, "Content-Range", HTTP_REMOVE_ALL); | ||||||
|  |     http_add_header_field(&ctx->res.hdr, "Content-Range", buf); | ||||||
|  |  | ||||||
|  |     if (mode == 0) { | ||||||
|  |         fseek(ctx->file, num1, SEEK_SET); | ||||||
|  |         ctx->content_length = num2 - num1 + 1; | ||||||
|  |     } else if (mode == 1) { | ||||||
|  |         fseekl(ctx->file, num1); | ||||||
|  |         ctx->content_length = file_get_line_pos(ctx->file, num2 + 1) - file_get_line_pos(ctx->file, num1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int local_handler(client_ctx_t *ctx) { | static int local_handler(client_ctx_t *ctx) { | ||||||
|     http_res *res = &ctx->res; |     http_res *res = &ctx->res; | ||||||
|     http_req *req = &ctx->req; |     http_req *req = &ctx->req; | ||||||
| @@ -45,14 +115,14 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|     char buf1[1024], buf2[1024]; |     char buf1[1024], buf2[1024]; | ||||||
|     int accept_if_modified_since = 0; |     int accept_if_modified_since = 0; | ||||||
|  |  | ||||||
|     if (strcmp(req->method, "TRACE") == 0) { |     if (streq(req->method, "TRACE")) { | ||||||
|         res->status = http_get_status(200); |         res->status = http_get_status(200); | ||||||
|         http_add_header_field(&res->hdr, "Content-Type", "message/http"); |         http_add_header_field(&res->hdr, "Content-Type", "message/http"); | ||||||
|  |  | ||||||
|         ctx->msg_buf_ptr = malloc(4096); |         ctx->msg_buf_ptr = malloc(4096); | ||||||
|         ctx->msg_buf = ctx->msg_buf_ptr; |         ctx->msg_buf = ctx->msg_buf_ptr; | ||||||
|         ctx->content_length = snprintf(ctx->msg_buf, 4096 - ctx->content_length, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version); |         ctx->content_length = snprintf(ctx->msg_buf, 4096 - ctx->content_length, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version); | ||||||
|         for (int i = 0; i < list_size(&req->hdr); i++) { |         for (int i = 0; i < list_size(req->hdr.fields); i++) { | ||||||
|             const http_field *f = &req->hdr.fields[i]; |             const http_field *f = &req->hdr.fields[i]; | ||||||
|             ctx->content_length += snprintf(ctx->msg_buf + ctx->content_length, 4096 - ctx->content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); |             ctx->content_length += snprintf(ctx->msg_buf + ctx->content_length, 4096 - ctx->content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); | ||||||
|         } |         } | ||||||
| @@ -60,11 +130,11 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (strncmp(uri->req_path, "/.well-known/", 13) == 0) { |     if (strstarts(uri->req_path, "/.well-known/")) { | ||||||
|         http_add_header_field(&res->hdr, "Access-Control-Allow-Origin", "*"); |         http_add_header_field(&res->hdr, "Access-Control-Allow-Origin", "*"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (strncmp(uri->req_path, "/.well-known/", 13) != 0 && strstr(uri->path, "/.") != NULL) { |     if (!strstarts(uri->req_path, "/.well-known/") && strcontains(uri->path, "/.")) { | ||||||
|         res->status = http_get_status(403); |         res->status = http_get_status(403); | ||||||
|         sprintf(err_msg, "Parts of this URI are hidden."); |         sprintf(err_msg, "Parts of this URI are hidden."); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -87,8 +157,11 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|  |  | ||||||
|     if (uri->is_static) { |     if (uri->is_static) { | ||||||
|         res->status = http_get_status(200); |         res->status = http_get_status(200); | ||||||
|         http_add_header_field(&res->hdr, "Accept-Ranges", "bytes"); |         cache_init_uri(ctx->conf->cache, uri); | ||||||
|         if (strcmp(req->method, "GET") != 0 && strcmp(req->method, "HEAD") != 0) { |  | ||||||
|  |         http_add_header_field(&res->hdr, "Accept-Ranges", mime_is_text(uri->meta->type) ? "bytes, lines" : "bytes"); | ||||||
|  |  | ||||||
|  |         if (!streq(req->method, "GET") && !streq(req->method, "HEAD")) { | ||||||
|             res->status = http_get_status(405); |             res->status = http_get_status(405); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
| @@ -99,18 +172,15 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         cache_init_uri(ctx->conf->cache, uri); |         const char *last_modified = http_format_date(uri->meta->mtime, buf1, sizeof(buf1)); | ||||||
|  |  | ||||||
|         const char *last_modified = http_format_date(uri->meta->stat.st_mtime, buf1, sizeof(buf1)); |  | ||||||
|         http_add_header_field(&res->hdr, "Last-Modified", last_modified); |         http_add_header_field(&res->hdr, "Last-Modified", last_modified); | ||||||
|         sprintf(buf2, "%s; charset=%s", uri->meta->type, uri->meta->charset); |         sprintf(buf2, "%s; charset=%s", uri->meta->type, uri->meta->charset); | ||||||
|         http_add_header_field(&res->hdr, "Content-Type", buf2); |         http_add_header_field(&res->hdr, "Content-Type", buf2); | ||||||
|  |  | ||||||
|  |  | ||||||
|         const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); |         const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); | ||||||
|         int enc = 0; |         int enc = 0; | ||||||
|         if (accept_encoding != NULL) { |         if (accept_encoding != NULL) { | ||||||
|             if (uri->meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) { |             if (uri->meta->filename_comp_br[0] != 0 && strcontains(accept_encoding, "br")) { | ||||||
|                 ctx->file = fopen(uri->meta->filename_comp_br, "rb"); |                 ctx->file = fopen(uri->meta->filename_comp_br, "rb"); | ||||||
|                 if (ctx->file == NULL) { |                 if (ctx->file == NULL) { | ||||||
|                     cache_mark_dirty(ctx->conf->cache, uri->filename); |                     cache_mark_dirty(ctx->conf->cache, uri->filename); | ||||||
| @@ -119,7 +189,7 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|                     http_add_header_field(&res->hdr, "Content-Encoding", "br"); |                     http_add_header_field(&res->hdr, "Content-Encoding", "br"); | ||||||
|                     enc = COMPRESS_BR; |                     enc = COMPRESS_BR; | ||||||
|                 } |                 } | ||||||
|             } else if (uri->meta->filename_comp_gz[0] != 0 && strstr(accept_encoding, "gzip") != NULL) { |             } else if (uri->meta->filename_comp_gz[0] != 0 && strcontains(accept_encoding, "gzip")) { | ||||||
|                 ctx->file = fopen(uri->meta->filename_comp_gz, "rb"); |                 ctx->file = fopen(uri->meta->filename_comp_gz, "rb"); | ||||||
|                 if (ctx->file == NULL) { |                 if (ctx->file == NULL) { | ||||||
|                     cache_mark_dirty(ctx->conf->cache, uri->filename); |                     cache_mark_dirty(ctx->conf->cache, uri->filename); | ||||||
| @@ -135,80 +205,44 @@ static int local_handler(client_ctx_t *ctx) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (uri->meta->etag[0] != 0) { |         if (uri->meta->etag[0] != 0) { | ||||||
|  |             strcpy(buf1, uri->meta->etag); | ||||||
|             if (enc) { |             if (enc) { | ||||||
|                 sprintf(buf1, "%s-%s", uri->meta->etag, (enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : ""); |                 strcat(buf1, "-"); | ||||||
|                 http_add_header_field(&res->hdr, "ETag", buf1); |                 strcat(buf1, (enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : ""); | ||||||
|             } else { |  | ||||||
|                 http_add_header_field(&res->hdr, "ETag", uri->meta->etag); |  | ||||||
|             } |             } | ||||||
|  |             http_add_header_field(&res->hdr, "ETag", buf1); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (strncmp(uri->meta->type, "text/", 5) == 0) { |         http_add_header_field(&res->hdr, "Cache-Control", mime_is_text(uri->meta->type) ? "public, max-age=3600" : "public, max-age=86400"); | ||||||
|             http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=3600"); |  | ||||||
|         } else { |  | ||||||
|             http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const char *if_modified_since = http_get_header_field(&req->hdr, "If-Modified-Since"); |         const char *if_modified_since = http_get_header_field(&req->hdr, "If-Modified-Since"); | ||||||
|         const char *if_none_match = http_get_header_field(&req->hdr, "If-None-Match"); |         const char *if_none_match = http_get_header_field(&req->hdr, "If-None-Match"); | ||||||
|         if ((if_none_match != NULL && strstr(if_none_match, uri->meta->etag) == NULL) || |         if ((if_none_match != NULL && !strcontains(if_none_match, uri->meta->etag)) || | ||||||
|             (accept_if_modified_since && if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0)) |             (accept_if_modified_since && streq(if_modified_since, last_modified))) | ||||||
|         { |         { | ||||||
|             res->status = http_get_status(304); |             res->status = http_get_status(304); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const char *range = http_get_header_field(&req->hdr, "Range"); |         if (http_get_header_field(&req->hdr, "Range") != NULL) { | ||||||
|         if (range != NULL) { |             if (range_handler(ctx) == 0) { | ||||||
|             if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) { |                 res->status = http_get_status(206); | ||||||
|                 res->status = http_get_status(416); |             } else { | ||||||
|  |                 if (ctx->file) { | ||||||
|  |                     fclose(ctx->file); | ||||||
|  |                     ctx->file = NULL; | ||||||
|  |                 } | ||||||
|                 http_remove_header_field(&res->hdr, "Content-Type", HTTP_REMOVE_ALL); |                 http_remove_header_field(&res->hdr, "Content-Type", HTTP_REMOVE_ALL); | ||||||
|                 http_remove_header_field(&res->hdr, "Last-Modified", HTTP_REMOVE_ALL); |                 http_remove_header_field(&res->hdr, "Last-Modified", HTTP_REMOVE_ALL); | ||||||
|                 http_remove_header_field(&res->hdr, "ETag", HTTP_REMOVE_ALL); |                 http_remove_header_field(&res->hdr, "ETag", HTTP_REMOVE_ALL); | ||||||
|                 http_remove_header_field(&res->hdr, "Cache-Control", HTTP_REMOVE_ALL); |                 http_remove_header_field(&res->hdr, "Cache-Control", HTTP_REMOVE_ALL); | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|             range += 6; |  | ||||||
|             char *ptr = strchr(range, '-'); |  | ||||||
|             if (ptr == NULL) { |  | ||||||
|                 res->status = http_get_status(416); |                 res->status = http_get_status(416); | ||||||
|                 return 0; |  | ||||||
|             } |             } | ||||||
|             ctx->file = fopen(uri->filename, "rb"); |  | ||||||
|             fseek(ctx->file, 0, SEEK_END); |  | ||||||
|             unsigned long file_len = ftell(ctx->file); |  | ||||||
|             fseek(ctx->file, 0, SEEK_SET); |  | ||||||
|             if (file_len == 0) { |  | ||||||
|                 ctx->content_length = 0; |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|             long num1 = 0; |  | ||||||
|             long num2 = (long) file_len - 1; |  | ||||||
|  |  | ||||||
|             if (ptr != range) num1 = (long) strtoul(range, NULL, 10); |  | ||||||
|             if (ptr[1] != 0) num2 = (long) strtoul(ptr + 1, NULL, 10); |  | ||||||
|  |  | ||||||
|             if (num1 >= file_len || num2 >= file_len || num1 > num2) { |  | ||||||
|                 res->status = http_get_status(416); |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|             sprintf(buf1, "bytes %li-%li/%li", num1, num2, file_len); |  | ||||||
|             http_add_header_field(&res->hdr, "Content-Range", buf1); |  | ||||||
|  |  | ||||||
|             res->status = http_get_status(206); |  | ||||||
|             fseek(ctx->file, num1, SEEK_SET); |  | ||||||
|             ctx->content_length = num2 - num1 + 1; |  | ||||||
|  |  | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (ctx->file == NULL) { |         if (ctx->file == NULL) ctx->file = fopen(uri->filename, "rb"); | ||||||
|             ctx->file = fopen(uri->filename, "rb"); |         ctx->content_length = fsize(ctx->file); | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fseek(ctx->file, 0, SEEK_END); |  | ||||||
|         ctx->content_length = ftell(ctx->file); |  | ||||||
|         fseek(ctx->file, 0, SEEK_SET); |  | ||||||
|     } else { |     } else { | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -14,13 +14,12 @@ | |||||||
| #include "../workers.h" | #include "../workers.h" | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
| static int proxy_handler_1(client_ctx_t *ctx); | static int proxy_handler_1(client_ctx_t *ctx); | ||||||
| static int proxy_handler_2(client_ctx_t *ctx); | static int proxy_handler_2(client_ctx_t *ctx); | ||||||
|  |  | ||||||
| void proxy_handler_func(client_ctx_t *ctx) { | void proxy_handler_func(client_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); |     logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); | ||||||
|  |  | ||||||
|     // TODO handle 1xx responses |     // TODO handle 1xx responses | ||||||
|  |  | ||||||
| @@ -29,12 +28,14 @@ void proxy_handler_func(client_ctx_t *ctx) { | |||||||
|  |  | ||||||
|     if (ret == 1) { |     if (ret == 1) { | ||||||
|         proxy_unlock_ctx(ctx->proxy); |         proxy_unlock_ctx(ctx->proxy); | ||||||
|  |         ctx->proxy->client = NULL; | ||||||
|         ctx->proxy = NULL; |         ctx->proxy = NULL; | ||||||
|     } else if (ctx->use_proxy == 0) { |     } else if (ctx->use_proxy == 0) { | ||||||
|         proxy_close(ctx->proxy); |         proxy_close(ctx->proxy); | ||||||
|     } else if (ctx->use_proxy == 1) { |     } else if (ctx->use_proxy == 1) { | ||||||
|         proxy_handler_2(ctx); |         proxy_handler_2(ctx); | ||||||
|         proxy_unlock_ctx(ctx->proxy); |         proxy_unlock_ctx(ctx->proxy); | ||||||
|  |         ctx->proxy->client = NULL; | ||||||
|         ctx->proxy = NULL; |         ctx->proxy = NULL; | ||||||
|     } else if (ctx->use_proxy == 2) { |     } else if (ctx->use_proxy == 2) { | ||||||
|         // WebSocket |         // WebSocket | ||||||
| @@ -63,12 +64,12 @@ static int proxy_handler_1(client_ctx_t *ctx) { | |||||||
|         const char *connection = http_get_header_field(&res->hdr, "Connection"); |         const char *connection = http_get_header_field(&res->hdr, "Connection"); | ||||||
|         const char *upgrade = http_get_header_field(&res->hdr, "Upgrade"); |         const char *upgrade = http_get_header_field(&res->hdr, "Upgrade"); | ||||||
|         if (connection != NULL && upgrade != NULL && |         if (connection != NULL && upgrade != NULL && | ||||||
|             (strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL) && |             (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) && | ||||||
|              strcmp(upgrade, "websocket") == 0) |             streq(upgrade, "websocket")) | ||||||
|         { |         { | ||||||
|             const char *ws_accept = http_get_header_field(&res->hdr, "Sec-WebSocket-Accept"); |             const char *ws_accept = http_get_header_field(&res->hdr, "Sec-WebSocket-Accept"); | ||||||
|             if (ws_calc_accept_key(status->ws_key, buf) == 0) { |             if (ws_calc_accept_key(status->ws_key, buf) == 0) { | ||||||
|                 ctx->use_proxy = (strcmp(buf, ws_accept) == 0) ? 2 : 1; |                 ctx->use_proxy = streq(buf, ws_accept) ? 2 : 1; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             status->status = 101; |             status->status = 101; | ||||||
| @@ -84,11 +85,11 @@ static int proxy_handler_1(client_ctx_t *ctx) { | |||||||
|         const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); |         const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); | ||||||
|         if (content_encoding == NULL && ( |         if (content_encoding == NULL && ( | ||||||
|                 content_length_f == NULL || |                 content_length_f == NULL || | ||||||
|                 (content_length_f != NULL && strcmp(content_length_f, "0") == 0) || |                 streq(content_length_f, "0") || | ||||||
|                 (content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0))) |                 (content_length_f != NULL && strstarts(content_type, "text/html")))) | ||||||
|         { |         { | ||||||
|             long content_len = (strcmp(ctx->req.method, "HEAD") != 0 && content_length_f != NULL) ? strtol(content_length_f, NULL, 10) : 0; |             long content_len = (!streq(ctx->req.method, "HEAD") && content_length_f != NULL) ? strtol(content_length_f, NULL, 10) : 0; | ||||||
|             if (content_len <= sizeof(ctx->msg_content) - 1) { |             if (content_len < sizeof(ctx->msg_content)) { | ||||||
|                 if (status->status != 101) { |                 if (status->status != 101) { | ||||||
|                     status->status = res->status->code; |                     status->status = res->status->code; | ||||||
|                     status->origin = res->status->code >= 400 ? SERVER : NONE; |                     status->origin = res->status->code >= 400 ? SERVER : NONE; | ||||||
| @@ -103,48 +104,17 @@ static int proxy_handler_1(client_ctx_t *ctx) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (strcmp(ctx->req.method, "HEAD") == 0) { |     return streq(ctx->req.method, "HEAD") ? 1 : 0; | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|     char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); |  | ||||||
|     if (use_proxy && content_encoding == NULL) { |  | ||||||
|         int http_comp = http_get_compression(&req, &res); |  | ||||||
|         if (http_comp & COMPRESS_BR) { |  | ||||||
|             use_proxy |= PROXY_COMPRESS_BR; |  | ||||||
|         } else if (http_comp & COMPRESS_GZ) { |  | ||||||
|             use_proxy |= PROXY_COMPRESS_GZ; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char *transfer_encoding = http_get_header_field(&res->hdr, "Transfer-Encoding"); |  | ||||||
|     int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0; |  | ||||||
|     http_remove_header_field(&res->hdr, "Transfer-Encoding", HTTP_REMOVE_ALL); |  | ||||||
|     ret = sprintf(buf, "%s%s%s", |  | ||||||
|                   (use_proxy & PROXY_COMPRESS_BR) ? "br" : |  | ||||||
|                   ((use_proxy & PROXY_COMPRESS_GZ) ? "gzip" : ""), |  | ||||||
|                   ((use_proxy & PROXY_COMPRESS) && chunked) ? ", " : "", |  | ||||||
|                   chunked ? "chunked" : ""); |  | ||||||
|     if (ret > 0) { |  | ||||||
|         http_add_header_field(&res->hdr, "Transfer-Encoding", buf); |  | ||||||
|     } |  | ||||||
|     */ |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int proxy_handler_2(client_ctx_t *ctx) { | static int proxy_handler_2(client_ctx_t *ctx) { | ||||||
|     const char *transfer_encoding = http_get_header_field(&ctx->res.hdr, "Transfer-Encoding"); |     const char *transfer_encoding = http_get_header_field(&ctx->res.hdr, "Transfer-Encoding"); | ||||||
|     int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL; |     int chunked = strcontains(transfer_encoding, "chunked"); | ||||||
|  |  | ||||||
|     const char *content_len = http_get_header_field(&ctx->res.hdr, "Content-Length"); |     const char *content_len = http_get_header_field(&ctx->res.hdr, "Content-Length"); | ||||||
|     unsigned long len_to_send = 0; |     unsigned long len_to_send = (content_len != NULL) ? strtol(content_len, NULL, 10) : 0; | ||||||
|     if (content_len != NULL) { |  | ||||||
|         len_to_send = strtol(content_len, NULL, 10); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int flags = (chunked ? PROXY_CHUNKED : 0) | (ctx->use_proxy & PROXY_COMPRESS); |     int flags = (chunked ? PROXY_CHUNKED : 0); | ||||||
|     int ret = proxy_send(ctx->proxy, &ctx->socket, len_to_send, flags); |     int ret = proxy_send(ctx->proxy, &ctx->socket, len_to_send, flags); | ||||||
|  |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
| @@ -153,18 +123,3 @@ static int proxy_handler_2(client_ctx_t *ctx) { | |||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void proxy_close(proxy_ctx_t *ctx) { |  | ||||||
|     client_ctx_t *cctx = ctx->client; |  | ||||||
|     if (cctx) { |  | ||||||
|         logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, cctx->req_host, CLR_STR, cctx->log_prefix); |  | ||||||
|     } else { |  | ||||||
|         logger_set_prefix(""); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     info(BLUE_STR "Closing proxy connection"); |  | ||||||
|     sock_close(&ctx->proxy); |  | ||||||
|  |  | ||||||
|     memset(ctx, 0, sizeof(*ctx)); |  | ||||||
|     errno = 0; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ | |||||||
| static int request_handler(client_ctx_t *ctx); | static int request_handler(client_ctx_t *ctx); | ||||||
|  |  | ||||||
| void request_handler_func(client_ctx_t *ctx) { | void request_handler_func(client_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); |     logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); | ||||||
|  |  | ||||||
|     switch (request_handler(ctx)) { |     switch (request_handler(ctx)) { | ||||||
|         case 0: |         case 0: | ||||||
| @@ -62,10 +62,8 @@ static void init_ctx(client_ctx_t *ctx) { | |||||||
|     memset(&ctx->req, 0, sizeof(ctx->req)); |     memset(&ctx->req, 0, sizeof(ctx->req)); | ||||||
|     memset(&ctx->res, 0, sizeof(ctx->res)); |     memset(&ctx->res, 0, sizeof(ctx->res)); | ||||||
|  |  | ||||||
|  |  | ||||||
|     ctx->res.status = http_get_status(501); |     ctx->res.status = http_get_status(501); | ||||||
|     http_init_hdr(&ctx->res.hdr); |     http_init_hdr(&ctx->res.hdr); | ||||||
|     ctx->res.hdr.last_field_num = -1; |  | ||||||
|     sprintf(ctx->res.version, "1.1"); |     sprintf(ctx->res.version, "1.1"); | ||||||
|  |  | ||||||
|     ctx->status.status = 0; |     ctx->status.status = 0; | ||||||
| @@ -108,7 +106,7 @@ static int request_handler(client_ctx_t *ctx) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *hdr_connection = http_get_header_field(&req->hdr, "Connection"); |     const char *hdr_connection = http_get_header_field(&req->hdr, "Connection"); | ||||||
|     ctx->c_keep_alive = (hdr_connection != NULL && (strstr(hdr_connection, "keep-alive") != NULL || strstr(hdr_connection, "Keep-Alive") != NULL)); |     ctx->c_keep_alive = (strcontains(hdr_connection, "keep-alive") || strcontains(hdr_connection, "Keep-Alive")); | ||||||
|     const char *host_ptr = http_get_header_field(&req->hdr, "Host"); |     const char *host_ptr = http_get_header_field(&req->hdr, "Host"); | ||||||
|     if (host_ptr != NULL && strlen(host_ptr) > 255) { |     if (host_ptr != NULL && strlen(host_ptr) > 255) { | ||||||
|         ctx->req_host[0] = 0; |         ctx->req_host[0] = 0; | ||||||
| @@ -128,11 +126,11 @@ static int request_handler(client_ctx_t *ctx) { | |||||||
|         strcpy(ctx->req_host, host_ptr); |         strcpy(ctx->req_host, host_ptr); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); |     logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); | ||||||
|     info(BLD_STR "%s %s", req->method, req->uri); |     info(BLD_STR "%s %s", req->method, req->uri); | ||||||
|  |  | ||||||
|     if (strncmp(req->uri, "/.sesimos/res/", 14) == 0) { |     if (strstarts(req->uri, "/.sesimos/res/")) { | ||||||
|         if (strcmp(req->method, "GET") != 0 && strcmp(req->method, "HEAD") != 0) { |         if (!streq(req->method, "GET") && !streq(req->method, "HEAD")) { | ||||||
|             res->status = http_get_status(405); |             res->status = http_get_status(405); | ||||||
|             http_add_header_field(&res->hdr, "Allow", "GET, HEAD"); |             http_add_header_field(&res->hdr, "Allow", "GET, HEAD"); | ||||||
|             return 0; |             return 0; | ||||||
| @@ -150,7 +148,7 @@ static int request_handler(client_ctx_t *ctx) { | |||||||
|         res->status = http_get_status(404); |         res->status = http_get_status(404); | ||||||
|         for (int i = 0; i < sizeof(resources) / sizeof(res_t); i++) { |         for (int i = 0; i < sizeof(resources) / sizeof(res_t); i++) { | ||||||
|             const res_t *r = &resources[i]; |             const res_t *r = &resources[i]; | ||||||
|             if (strcmp(req->uri + 14, r->name) == 0) { |             if (streq(req->uri + 14, r->name)) { | ||||||
|                 res->status = http_get_status(203); |                 res->status = http_get_status(203); | ||||||
|                 http_add_header_field(&res->hdr, "Content-Type", r->type); |                 http_add_header_field(&res->hdr, "Content-Type", r->type); | ||||||
|                 http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400"); |                 http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400"); | ||||||
| @@ -189,13 +187,13 @@ static int request_handler(client_ctx_t *ctx) { | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ctx->conf->type == CONFIG_TYPE_LOCAL && strcmp(req->method, "TRACE") == 0) { |     if (ctx->conf->type == CONFIG_TYPE_LOCAL && streq(req->method, "TRACE")) { | ||||||
|         return 1; |         return 1; | ||||||
|     } else if (dir_mode != URI_DIR_MODE_NO_VALIDATION) { |     } else if (dir_mode != URI_DIR_MODE_NO_VALIDATION) { | ||||||
|         ssize_t size = sizeof(buf0); |         ssize_t size = sizeof(buf0); | ||||||
|         url_decode(req->uri, buf0, &size); |         url_decode(req->uri, buf0, &size); | ||||||
|         int change_proto = (!client->enc && strncmp(uri->uri, "/.well-known/", 13) != 0); |         int change_proto = (!client->enc && !strstarts(uri->uri, "/.well-known/")); | ||||||
|         if (strcmp(uri->uri, buf0) != 0 || change_proto) { |         if (!streq(uri->uri, buf0) || change_proto) { | ||||||
|             res->status = http_get_status(308); |             res->status = http_get_status(308); | ||||||
|             size = url_encode(uri->uri, strlen(uri->uri), buf0, sizeof(buf0)); |             size = url_encode(uri->uri, strlen(uri->uri), buf0, sizeof(buf0)); | ||||||
|             if (change_proto) { |             if (change_proto) { | ||||||
| @@ -261,8 +259,8 @@ int respond(client_ctx_t *ctx) { | |||||||
|             http_add_header_field(&res->hdr, "Content-Type", "text/html; charset=UTF-8"); |             http_add_header_field(&res->hdr, "Content-Type", "text/html; charset=UTF-8"); | ||||||
|  |  | ||||||
|             // TODO list Locations on 3xx Redirects |             // TODO list Locations on 3xx Redirects | ||||||
|             const http_doc_info *http_info = http_get_status_info(res->status); |             const http_doc_info *http_info = http_get_status_info(res->status->code); | ||||||
|             const http_status_msg *http_msg = http_get_error_msg(res->status); |             const http_status_msg *http_msg = http_get_error_msg(res->status->code); | ||||||
|  |  | ||||||
|             if (ctx->msg_content[0] == 0) { |             if (ctx->msg_content[0] == 0) { | ||||||
|                 if (res->status->code >= 300 && res->status->code < 400) { |                 if (res->status->code >= 300 && res->status->code < 400) { | ||||||
| @@ -271,7 +269,7 @@ int respond(client_ctx_t *ctx) { | |||||||
|                         snprintf(ctx->msg_content, sizeof(ctx->msg_content), "      <ul>\n        <li><a href=\"%s\">%s</a></li>\n      </ul>\n", location, location); |                         snprintf(ctx->msg_content, sizeof(ctx->msg_content), "      <ul>\n        <li><a href=\"%s\">%s</a></li>\n      </ul>\n", location, location); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } else if (strncmp(ctx->msg_content, "<!DOCTYPE html>", 15) == 0 || strncmp(ctx->msg_content, "<html", 5) == 0) { |             } else if (strstarts(ctx->msg_content, "<!DOCTYPE html>") || strstarts(ctx->msg_content, "<html")) { | ||||||
|                 ctx->msg_content[0] = 0; |                 ctx->msg_content[0] = 0; | ||||||
|                 // TODO let relevant information pass? |                 // TODO let relevant information pass? | ||||||
|             } |             } | ||||||
| @@ -302,10 +300,10 @@ int respond(client_ctx_t *ctx) { | |||||||
|             ctx->msg_buf_ptr = malloc(4096); |             ctx->msg_buf_ptr = malloc(4096); | ||||||
|             ctx->msg_buf = ctx->msg_buf_ptr; |             ctx->msg_buf = ctx->msg_buf_ptr; | ||||||
|             snprintf(msg_pre_buf_1, sizeof(msg_pre_buf_1), http_info->doc, |             snprintf(msg_pre_buf_1, sizeof(msg_pre_buf_1), http_info->doc, | ||||||
|                      res->status->code, res->status->msg, http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : ""); |                      res->status->code, res->status->msg, http_msg != NULL ? http_msg->msg : "", err_msg); | ||||||
|             ctx->content_length = snprintf(ctx->msg_buf, 4096, http_default_doc, res->status->code, |             ctx->content_length = snprintf(ctx->msg_buf, 4096, http_default_doc, res->status->code, res->status->msg, | ||||||
|                                            res->status->msg, msg_pre_buf_1, http_info->mode, http_info->icon, http_info->color, ctx->req_host, |                                            msg_pre_buf_1, http_info->mode, http_info->icon, http_info->color, | ||||||
|                                            proxy_doc, ctx->msg_content[0] != 0 ? ctx->msg_content : "", SERVER_STR_HTML); |                                            ctx->req_host, proxy_doc, ctx->msg_content, SERVER_STR_HTML); | ||||||
|         } |         } | ||||||
|         if (ctx->content_length >= 0) { |         if (ctx->content_length >= 0) { | ||||||
|             sprintf(buf0, "%li", ctx->content_length); |             sprintf(buf0, "%li", ctx->content_length); | ||||||
| @@ -331,7 +329,7 @@ int respond(client_ctx_t *ctx) { | |||||||
|     http_send_response(client, res); |     http_send_response(client, res); | ||||||
|     ctx->res_ts = clock_micros(); |     ctx->res_ts = clock_micros(); | ||||||
|     const char *location = http_get_header_field(&res->hdr, "Location"); |     const char *location = http_get_header_field(&res->hdr, "Location"); | ||||||
|     info("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res->status), ctx->use_proxy ? "-> " : "", res->status->code, |     info("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res->status->code), ctx->use_proxy ? "-> " : "", res->status->code, | ||||||
|          res->status->msg, location != NULL ? " -> " : "", location != NULL ? location : "", |          res->status->msg, location != NULL ? " -> " : "", location != NULL ? location : "", | ||||||
|          format_duration(ctx->res_ts - ctx->req_s, buf0), CLR_STR); |          format_duration(ctx->res_ts - ctx->req_s, buf0), CLR_STR); | ||||||
|  |  | ||||||
| @@ -340,10 +338,10 @@ int respond(client_ctx_t *ctx) { | |||||||
|     if (ctx->use_proxy) { |     if (ctx->use_proxy) { | ||||||
|         // reverse proxy |         // reverse proxy | ||||||
|         return 3; |         return 3; | ||||||
|     } else if (strcmp(req->method, "HEAD") != 0) { |     } else if (!streq(req->method, "HEAD")) { | ||||||
|         // default response |         // default response | ||||||
|         if (ctx->msg_buf != NULL) { |         if (ctx->msg_buf != NULL) { | ||||||
|             ret = sock_send(client, ctx->msg_buf, ctx->content_length, 0); |             ret = sock_send_x(client, ctx->msg_buf, ctx->content_length, 0); | ||||||
|             if (ret <= 0) { |             if (ret <= 0) { | ||||||
|                 error("Unable to send"); |                 error("Unable to send"); | ||||||
|             } |             } | ||||||
| @@ -354,7 +352,7 @@ int respond(client_ctx_t *ctx) { | |||||||
|                 if (snd_len + len > ctx->content_length) { |                 if (snd_len + len > ctx->content_length) { | ||||||
|                     len = ctx->content_length - snd_len; |                     len = ctx->content_length - snd_len; | ||||||
|                 } |                 } | ||||||
|                 ret = sock_send(client, buffer, len, feof(ctx->file) ? 0 : MSG_MORE); |                 ret = sock_send_x(client, buffer, len, feof(ctx->file) ? 0 : MSG_MORE); | ||||||
|                 if (ret <= 0) { |                 if (ret <= 0) { | ||||||
|                     error("Unable to send"); |                     error("Unable to send"); | ||||||
|                     break; |                     break; | ||||||
| @@ -386,7 +384,7 @@ void request_complete(client_ctx_t *ctx) { | |||||||
|  |  | ||||||
| void timeout_request(client_ctx_t *ctx) { | void timeout_request(client_ctx_t *ctx) { | ||||||
|     init_ctx(ctx); |     init_ctx(ctx); | ||||||
|     logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); |     logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); | ||||||
|  |  | ||||||
|     ctx->s_keep_alive = 0; |     ctx->s_keep_alive = 0; | ||||||
|     ctx->res.status = http_get_status(408); |     ctx->res.status = http_get_status(408); | ||||||
|   | |||||||
| @@ -32,8 +32,10 @@ void tcp_acceptor_func(client_ctx_t *ctx) { | |||||||
| static int tcp_acceptor(client_ctx_t *ctx) { | static int tcp_acceptor(client_ctx_t *ctx) { | ||||||
|     struct sockaddr_in6 server_addr; |     struct sockaddr_in6 server_addr; | ||||||
|  |  | ||||||
|  |     memset(ctx->_c_addr, 0, sizeof(ctx->_c_addr)); | ||||||
|  |     memset(ctx->_s_addr, 0, sizeof(ctx->_s_addr)); | ||||||
|     inet_ntop(ctx->socket._addr.ipv6.sin6_family, &ctx->socket._addr.ipv6.sin6_addr, ctx->_c_addr, sizeof(ctx->_c_addr)); |     inet_ntop(ctx->socket._addr.ipv6.sin6_family, &ctx->socket._addr.ipv6.sin6_addr, ctx->_c_addr, sizeof(ctx->_c_addr)); | ||||||
|     if (strncmp(ctx->_c_addr, "::ffff:", 7) == 0) { |     if (strstarts(ctx->_c_addr, "::ffff:")) { | ||||||
|         ctx->socket.addr = ctx->_c_addr + 7; |         ctx->socket.addr = ctx->_c_addr + 7; | ||||||
|     } else { |     } else { | ||||||
|         ctx->socket.addr = ctx->_c_addr; |         ctx->socket.addr = ctx->_c_addr; | ||||||
| @@ -42,17 +44,17 @@ static int tcp_acceptor(client_ctx_t *ctx) { | |||||||
|     socklen_t len = sizeof(server_addr); |     socklen_t len = sizeof(server_addr); | ||||||
|     getsockname(ctx->socket.socket, (struct sockaddr *) &server_addr, &len); |     getsockname(ctx->socket.socket, (struct sockaddr *) &server_addr, &len); | ||||||
|     inet_ntop(server_addr.sin6_family, (void *) &server_addr.sin6_addr, ctx->_s_addr, sizeof(ctx->_s_addr)); |     inet_ntop(server_addr.sin6_family, (void *) &server_addr.sin6_addr, ctx->_s_addr, sizeof(ctx->_s_addr)); | ||||||
|     if (strncmp(ctx->_s_addr, "::ffff:", 7) == 0) { |     if (strstarts(ctx->_s_addr, "::ffff:")) { | ||||||
|         ctx->socket.s_addr = ctx->_s_addr + 7; |         ctx->socket.s_addr = ctx->_s_addr + 7; | ||||||
|     } else { |     } else { | ||||||
|         ctx->socket.s_addr = ctx->_s_addr; |         ctx->socket.s_addr = ctx->_s_addr; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sprintf(ctx->log_prefix, "[%s%4i%s]%s[%*s][%5i]%s", (int) ctx->socket.enc ? HTTPS_STR : HTTP_STR, |     sprintf(ctx->log_prefix, "[%s%4i%s]%s[%*s][%5i]%s", (int) ctx->socket.enc ? HTTPS_STR : HTTP_STR, | ||||||
|             ntohs(server_addr.sin6_port), CLR_STR, /*color_table[0]*/ "", INET6_ADDRSTRLEN, ctx->socket.addr, |             ntohs(server_addr.sin6_port), CLR_STR, /*color_table[0]*/ "", ADDRSTRLEN, ctx->socket.addr, | ||||||
|             ntohs(ctx->socket._addr.ipv6.sin6_port), CLR_STR); |             ntohs(ctx->socket._addr.ipv6.sin6_port), CLR_STR); | ||||||
|  |  | ||||||
|     logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); |     logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); | ||||||
|  |  | ||||||
|     int ret; |     int ret; | ||||||
|     char buf[1024]; |     char buf[1024]; | ||||||
| @@ -103,7 +105,7 @@ static int tcp_acceptor(client_ctx_t *ctx) { | |||||||
|  |  | ||||||
|         ret = SSL_accept(client->ssl); |         ret = SSL_accept(client->ssl); | ||||||
|         if (ret != 1) { |         if (ret != 1) { | ||||||
|             error_ssl(SSL_get_error(client->ssl, ret)); |             sock_error(client, ret); | ||||||
|             info("Unable to perform handshake"); |             info("Unable to perform handshake"); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -118,7 +120,8 @@ static int tcp_acceptor(client_ctx_t *ctx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void tcp_close(client_ctx_t *ctx) { | void tcp_close(client_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); |     errno = 0; | ||||||
|  |     logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix); | ||||||
|  |  | ||||||
|     sock_close(&ctx->socket); |     sock_close(&ctx->socket); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ | |||||||
| static int ws_frame_handler(ws_ctx_t *ctx); | static int ws_frame_handler(ws_ctx_t *ctx); | ||||||
|  |  | ||||||
| void ws_frame_handler_func(ws_ctx_t *ctx) { | void ws_frame_handler_func(ws_ctx_t *ctx) { | ||||||
|     logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); |     logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); | ||||||
|  |  | ||||||
|     if (ws_frame_handler(ctx) == 0) { |     if (ws_frame_handler(ctx) == 0) { | ||||||
|         if (ctx->client->ws_close == 3) { |         if (ctx->client->ws_close == 3) { | ||||||
| @@ -84,7 +84,7 @@ void ws_close(ws_ctx_t *ctx) { | |||||||
|     ws_ctx_t *other = ctx->other; |     ws_ctx_t *other = ctx->other; | ||||||
|     if (other) { |     if (other) { | ||||||
|         other->other = NULL; |         other->other = NULL; | ||||||
|         logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); |         logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); | ||||||
|         info("Closing WebSocket connection"); |         info("Closing WebSocket connection"); | ||||||
|         proxy_close(ctx->client->proxy); |         proxy_close(ctx->client->proxy); | ||||||
|         tcp_close(ctx->client); |         tcp_close(ctx->client); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user