Added basic file transfer
This commit is contained in:
		
							
								
								
									
										107
									
								
								src/cache.c
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								src/cache.c
									
									
									
									
									
								
							| @@ -10,22 +10,123 @@ | |||||||
|  |  | ||||||
| int magic_init() { | int magic_init() { | ||||||
|     magic = magic_open(MAGIC_MIME); |     magic = magic_open(MAGIC_MIME); | ||||||
|     magic_load(magic, "/usr/share/misc/magic.mgc"); |     if (magic == NULL) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to open magic cookie: %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     if (magic_load(magic, MAGIC_FILE) != 0) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to load magic cookie: %s" CLR_STR "\n", magic_error(magic)); | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cache_init() { | int cache_init() { | ||||||
|     magic_init(); |     if (magic_init() != 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int shm_id = shmget(SHM_KEY, FILE_CACHE_SIZE * sizeof(cache_entry), IPC_CREAT | IPC_EXCL); | ||||||
|  |     if (shm_id < 0) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void *shm = shmat(shm_id, NULL, SHM_RDONLY); | ||||||
|  |     if (shm == (void *) -1) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |         return -3; | ||||||
|  |     } | ||||||
|  |     cache = shm; | ||||||
|  |  | ||||||
|  |     void *shm_rw = shmat(shm_id, NULL, 0); | ||||||
|  |     if (shm_rw == (void *) -1) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |         return -4; | ||||||
|  |     } | ||||||
|  |     cache = shm_rw; | ||||||
|  |     memset(cache, 0, FILE_CACHE_SIZE * sizeof(cache_entry)); | ||||||
|  |     // TODO load cache from file | ||||||
|  |     shmdt(shm_rw); | ||||||
|  |     cache = shm; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int cache_unload() { | ||||||
|  |     int shm_id = shmget(SHM_KEY, 0, 0); | ||||||
|  |     if (shm_id < 0) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |     } else if (shmctl(shm_id, IPC_RMID, NULL) < 0) { | ||||||
|  |         fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno)); | ||||||
|  |     } | ||||||
|  |     shmdt(cache); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int cache_update_entry(int entry_num, const char *filename) { | ||||||
|  |     void *cache_ro = cache; | ||||||
|  |     int shm_id = shmget(SHM_KEY, 0, 0); | ||||||
|  |     void *shm_rw = shmat(shm_id, NULL, 0); | ||||||
|  |     if (shm_rw == (void *) -1) { | ||||||
|  |         print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno)); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     cache = shm_rw; | ||||||
|  |  | ||||||
|  |     struct stat statbuf; | ||||||
|  |     stat(filename, &statbuf); | ||||||
|  |     memcpy(&cache[entry_num].meta.stat, &statbuf, sizeof(statbuf)); | ||||||
|  |  | ||||||
|  |     strcpy(cache[entry_num].filename, filename); | ||||||
|  |     magic_setflags(magic, MAGIC_MIME_TYPE); | ||||||
|  |     strcpy(cache[entry_num].meta.type, magic_file(magic, filename)); | ||||||
|  |     magic_setflags(magic, MAGIC_MIME_ENCODING); | ||||||
|  |     strcpy(cache[entry_num].meta.charset, magic_file(magic, filename)); | ||||||
|  |     cache[entry_num].is_valid_etag = 0; | ||||||
|  |     cache[entry_num].is_updating = 0; | ||||||
|  |  | ||||||
|  |     shmdt(shm_rw); | ||||||
|  |     cache = cache_ro; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int uri_cache_init(http_uri *uri) { | int uri_cache_init(http_uri *uri) { | ||||||
|     if (uri->filename == NULL) { |     if (uri->filename == NULL) { | ||||||
|         return -1; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |     for (i = 0; i < FILE_CACHE_SIZE; i++) { | ||||||
|  |         if (cache[i].filename[0] != 0 && strncmp(cache[i].filename, uri->filename, cache[i].filename_len) == 0) { | ||||||
|  |             uri->meta = &cache[i].meta; | ||||||
|  |             if (cache[i].is_updating) { | ||||||
|  |                 return 0; | ||||||
|  |             } else { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (uri->meta == NULL) { | ||||||
|  |         for (i = 0; i < FILE_CACHE_SIZE; i++) { | ||||||
|  |             if (cache[i].filename[0] == 0) { | ||||||
|  |                 if (cache_update_entry(i, uri->filename) != 0) { | ||||||
|  |                     return -1; | ||||||
|  |                 } | ||||||
|  |                 uri->meta = &cache[i].meta; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         struct stat statbuf; | ||||||
|  |         stat(uri->filename, &statbuf); | ||||||
|  |         if (uri->meta->stat.st_mtimensec != statbuf.st_mtimensec) { | ||||||
|  |             if (cache_update_entry(i, uri->filename) != 0) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								src/cache.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/cache.h
									
									
									
									
									
								
							| @@ -9,24 +9,27 @@ | |||||||
| #define NECRONDA_SERVER_CACHE_H | #define NECRONDA_SERVER_CACHE_H | ||||||
|  |  | ||||||
| #include <magic.h> | #include <magic.h> | ||||||
|  | #include <sys/ipc.h> | ||||||
|  | #include <sys/shm.h> | ||||||
|  |  | ||||||
| magic_t magic; | magic_t magic; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     char *etag; |     char etag[64]; | ||||||
|     char *type; |     char type[24]; | ||||||
|     char *subtype; |     char charset[16]; | ||||||
|     char *filename_comp; |     char filename_comp[256]; | ||||||
|     struct stat stat; |     struct stat stat; | ||||||
| } meta_data; | } meta_data; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     char *filename; |     char filename[256]; | ||||||
|     unsigned short filename_len; |     unsigned short filename_len; | ||||||
|  |     unsigned char is_valid_etag:1; | ||||||
|  |     unsigned char is_updating:1; | ||||||
|     meta_data meta; |     meta_data meta; | ||||||
| } cache_entry; | } cache_entry; | ||||||
|  |  | ||||||
| cache_entry cache[FILE_CACHE_SIZE]; | cache_entry *cache; | ||||||
| int cache_entries = 0; |  | ||||||
|  |  | ||||||
| #endif //NECRONDA_SERVER_CACHE_H | #endif //NECRONDA_SERVER_CACHE_H | ||||||
|   | |||||||
							
								
								
									
										85
									
								
								src/client.c
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								src/client.c
									
									
									
									
									
								
							| @@ -39,11 +39,12 @@ int client_request_handler(sock *client, int req_num) { | |||||||
|     struct timespec begin, end; |     struct timespec begin, end; | ||||||
|     int ret, client_keep_alive, dir_mode; |     int ret, client_keep_alive, dir_mode; | ||||||
|     char buf0[1024], buf1[1024]; |     char buf0[1024], buf1[1024]; | ||||||
|     char msg_buf[4096], msg_pre_buf[4096]; |     char msg_buf[4096], msg_pre_buf[4096], err_msg[256]; | ||||||
|     char err_msg[256]; |     char buffer[CHUNK_SIZE]; | ||||||
|     err_msg[0] = 0; |     err_msg[0] = 0; | ||||||
|     char *host, *hdr_connection, *webroot; |     char *host, *hdr_connection, *webroot; | ||||||
|     unsigned long content_length = 0; |     unsigned long content_length = 0; | ||||||
|  |     FILE *file = NULL; | ||||||
|  |  | ||||||
|     http_res res; |     http_res res; | ||||||
|     sprintf(res.version, "1.1"); |     sprintf(res.version, "1.1"); | ||||||
| @@ -165,13 +166,27 @@ int client_request_handler(sock *client, int req_num) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (uri.is_static) { |     if (uri.is_static) { | ||||||
|         uri_init_cache(&uri); |         res.status = http_get_status(200); | ||||||
|         http_add_header_field(&res.hdr, "Allow", "GET, HEAD"); |         http_add_header_field(&res.hdr, "Allow", "GET, HEAD"); | ||||||
|         http_add_header_field(&res.hdr, "Accept-Ranges", "bytes"); |         http_add_header_field(&res.hdr, "Accept-Ranges", "bytes"); | ||||||
|         if (strncmp(req.method, "GET", 3) != 0 && strncmp(req.method, "HEAD", 4) != 0) { |         if (strncmp(req.method, "GET", 3) != 0 && strncmp(req.method, "HEAD", 4) != 0) { | ||||||
|             res.status = http_get_status(405); |             res.status = http_get_status(405); | ||||||
|             goto respond; |             goto respond; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         ret = uri_cache_init(&uri); | ||||||
|  |         if (ret != 0) { | ||||||
|  |             res.status = http_get_status(500); | ||||||
|  |             sprintf(err_msg, "Unable to communicate with internal file cache."); | ||||||
|  |             goto respond; | ||||||
|  |         } | ||||||
|  |         sprintf(buf0, "%s, charset=%s", uri.meta->type, uri.meta->charset); | ||||||
|  |         http_add_header_field(&res.hdr, "Content-Type", buf0); | ||||||
|  |  | ||||||
|  |         file = fopen(uri.filename, "rb"); | ||||||
|  |         fseek(file, 0, 2); | ||||||
|  |         content_length = ftell(file); | ||||||
|  |         fseek(file, 0, 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     respond: |     respond: | ||||||
| @@ -185,31 +200,63 @@ int client_request_handler(sock *client, int req_num) { | |||||||
|     if (http_get_header_field(&res.hdr, "Accept-Ranges", HTTP_PRESERVE_UPPER) == NULL) { |     if (http_get_header_field(&res.hdr, "Accept-Ranges", HTTP_PRESERVE_UPPER) == NULL) { | ||||||
|         http_add_header_field(&res.hdr, "Accept-Ranges", "none"); |         http_add_header_field(&res.hdr, "Accept-Ranges", "none"); | ||||||
|     } |     } | ||||||
|     unsigned long len = 0; |  | ||||||
|     if (res.status->code >= 400 && res.status->code < 600) { |     if (res.status->code >= 400 && res.status->code < 600) { | ||||||
|         http_error_msg *http_msg = http_get_error_msg(res.status->code); |         http_error_msg *http_msg = http_get_error_msg(res.status->code); | ||||||
|         sprintf(msg_pre_buf, http_error_document, res.status->code, res.status->msg, |         sprintf(msg_pre_buf, http_error_document, res.status->code, res.status->msg, | ||||||
|                 http_msg != NULL ? http_msg->err_msg : "", err_msg[0] != 0 ? err_msg : ""); |                 http_msg != NULL ? http_msg->err_msg : "", err_msg[0] != 0 ? err_msg : ""); | ||||||
|         len = sprintf(msg_buf, http_default_document, res.status->code, res.status->msg, |         content_length = sprintf(msg_buf, http_default_document, res.status->code, res.status->msg, | ||||||
|                       msg_pre_buf, res.status->code >= 300 && res.status->code < 400 ? "info" : "error", |                                  msg_pre_buf, res.status->code >= 300 && res.status->code < 400 ? "info" : "error", | ||||||
|                       http_error_icon, "#C00000"); |                                  http_error_icon, "#C00000"); | ||||||
|         sprintf(buf0, "%li", len); |  | ||||||
|         http_add_header_field(&res.hdr, "Content-Length", buf0); |  | ||||||
|         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"); | ||||||
|     } else { |  | ||||||
|         sprintf(buf0, "%li", content_length); |  | ||||||
|         http_add_header_field(&res.hdr, "Content-Length", buf0); |  | ||||||
|     } |     } | ||||||
|  |     sprintf(buf0, "%li", content_length); | ||||||
|  |     http_add_header_field(&res.hdr, "Content-Length", buf0); | ||||||
|  |  | ||||||
|     http_send_response(client, &res); |     http_send_response(client, &res); | ||||||
|  |     clock_gettime(CLOCK_MONOTONIC, &end); | ||||||
|  |     char *location = http_get_header_field(&res.hdr, "Location", HTTP_PRESERVE_UPPER); | ||||||
|  |     unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; | ||||||
|  |     print("%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg, | ||||||
|  |           location != NULL ? " -> " : "", location != NULL ? location : "", format_duration(micros, buf0), CLR_STR); | ||||||
|  |  | ||||||
|     if (strncmp(req.method, "HEAD", 4) != 0) { |     if (strncmp(req.method, "HEAD", 4) != 0) { | ||||||
|  |         unsigned long snd_len = 0; | ||||||
|  |         unsigned long len = 0; | ||||||
|         if (res.status->code >= 400 && res.status->code < 600) { |         if (res.status->code >= 400 && res.status->code < 600) { | ||||||
|             int snd_len = 0; |             while (snd_len < content_length) { | ||||||
|             while (snd_len < len) { |  | ||||||
|                 if (client->enc) { |                 if (client->enc) { | ||||||
|                     ret = SSL_write(client->ssl, msg_buf, (int) (len - snd_len)); |                     ret = SSL_write(client->ssl, msg_buf, (int) (content_length - snd_len)); | ||||||
|  |                     if (ret <= 0) { | ||||||
|  |                         print(ERR_STR "Unable to send: %s" CLR_STR, ssl_get_error(client->ssl, ret)); | ||||||
|  |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     ret = send(client->socket, msg_buf, len - snd_len, 0); |                     ret = send(client->socket, msg_buf, content_length - snd_len, 0); | ||||||
|  |                     if (ret < 0) { | ||||||
|  |                         print(ERR_STR "Unable to send: %s" CLR_STR, strerror(errno)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (ret < 0) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 snd_len += ret; | ||||||
|  |             } | ||||||
|  |         } else if (file != NULL) { | ||||||
|  |             while (snd_len < content_length) { | ||||||
|  |                 len = fread(&buffer, 1, CHUNK_SIZE, file); | ||||||
|  |                 if (client->enc) { | ||||||
|  |                     ret = SSL_write(client->ssl, buffer, (int) len); | ||||||
|  |                     if (ret <= 0) { | ||||||
|  |                         print(ERR_STR "Unable to send: %s" CLR_STR, ssl_get_error(client->ssl, ret)); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     ret = send(client->socket, buffer, len, 0); | ||||||
|  |                     if (ret < 0) { | ||||||
|  |                         print(ERR_STR "Unable to send: %s" CLR_STR, strerror(errno)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (ret <= 0) { | ||||||
|  |                     break; | ||||||
|                 } |                 } | ||||||
|                 snd_len += ret; |                 snd_len += ret; | ||||||
|             } |             } | ||||||
| @@ -217,10 +264,8 @@ int client_request_handler(sock *client, int req_num) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     clock_gettime(CLOCK_MONOTONIC, &end); |     clock_gettime(CLOCK_MONOTONIC, &end); | ||||||
|     char *location = http_get_header_field(&res.hdr, "Location", HTTP_PRESERVE_UPPER); |     micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; | ||||||
|     unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; |     print("Transfer complete: %s", format_duration(micros, buf0)); | ||||||
|     print("%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg, |  | ||||||
|           location != NULL ? " -> " : "", location != NULL ? location : "", format_duration(micros, buf0), CLR_STR); |  | ||||||
|  |  | ||||||
|     uri_free(&uri); |     uri_free(&uri); | ||||||
|     abort: |     abort: | ||||||
|   | |||||||
| @@ -80,6 +80,7 @@ void destroy() { | |||||||
|     if (kills > 0) { |     if (kills > 0) { | ||||||
|         fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills); |         fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills); | ||||||
|     } |     } | ||||||
|  |     cache_unload(); | ||||||
|     exit(2); |     exit(2); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -137,6 +138,7 @@ void terminate() { | |||||||
|     } else { |     } else { | ||||||
|         fprintf(stderr, "Goodbye\n"); |         fprintf(stderr, "Goodbye\n"); | ||||||
|     } |     } | ||||||
|  |     cache_unload(); | ||||||
|     exit(0); |     exit(0); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -239,7 +241,9 @@ int main(int argc, const char *argv[]) { | |||||||
|     signal(SIGINT, terminate); |     signal(SIGINT, terminate); | ||||||
|     signal(SIGTERM, terminate); |     signal(SIGTERM, terminate); | ||||||
|  |  | ||||||
|     cache_init(); |     if (cache_init() != 0) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|     openssl_init(); |     openssl_init(); | ||||||
|  |  | ||||||
|     client.ctx = SSL_CTX_new(TLS_server_method()); |     client.ctx = SSL_CTX_new(TLS_server_method()); | ||||||
|   | |||||||
| @@ -33,8 +33,10 @@ | |||||||
| #define REQ_PER_CONNECTION 100 | #define REQ_PER_CONNECTION 100 | ||||||
| #define CLIENT_TIMEOUT 3600 | #define CLIENT_TIMEOUT 3600 | ||||||
|  |  | ||||||
|  | #define CHUNK_SIZE 4096 | ||||||
| #define CLIENT_MAX_HEADER_SIZE 8192 | #define CLIENT_MAX_HEADER_SIZE 8192 | ||||||
| #define FILE_CACHE_SIZE 1024 | #define FILE_CACHE_SIZE 1024 | ||||||
|  | #define SHM_KEY 255641 | ||||||
|  |  | ||||||
| #define ERR_STR "\x1B[1;31m" | #define ERR_STR "\x1B[1;31m" | ||||||
| #define CLR_STR "\x1B[0m" | #define CLR_STR "\x1B[0m" | ||||||
| @@ -52,6 +54,8 @@ | |||||||
| #define SERVER_STR "Necronda/" NECRONDA_VERSION | #define SERVER_STR "Necronda/" NECRONDA_VERSION | ||||||
| #define NECRONDA_DEFAULT "www.necronda.net" | #define NECRONDA_DEFAULT "www.necronda.net" | ||||||
|  |  | ||||||
|  | #define MAGIC_FILE "/usr/share/file/misc/magic.mgc" | ||||||
|  |  | ||||||
| int SOCKETS[NUM_SOCKETS]; | int SOCKETS[NUM_SOCKETS]; | ||||||
| pid_t CHILDREN[MAX_CHILDREN]; | pid_t CHILDREN[MAX_CHILDREN]; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,6 +36,7 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|     uri->query = NULL; |     uri->query = NULL; | ||||||
|     uri->filename = NULL; |     uri->filename = NULL; | ||||||
|     uri->uri = NULL; |     uri->uri = NULL; | ||||||
|  |     uri->meta = NULL; | ||||||
|     uri->is_static = 1; |     uri->is_static = 1; | ||||||
|     uri->is_dir = 0; |     uri->is_dir = 0; | ||||||
|     if (uri_str[0] != '/') { |     if (uri_str[0] != '/') { | ||||||
| @@ -155,10 +156,6 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int uri_init_cache(http_uri *uri) { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void uri_free(http_uri *uri) { | void uri_free(http_uri *uri) { | ||||||
|     if (uri->webroot != NULL) free(uri->webroot); |     if (uri->webroot != NULL) free(uri->webroot); | ||||||
|     if (uri->req_path != NULL) free(uri->req_path); |     if (uri->req_path != NULL) free(uri->req_path); | ||||||
|   | |||||||
| @@ -23,9 +23,9 @@ typedef struct { | |||||||
|     char *query;          // "username=test" |     char *query;          // "username=test" | ||||||
|     char *filename;       // "/account/index.php" |     char *filename;       // "/account/index.php" | ||||||
|     char *uri;            // "/account/login?username=test" |     char *uri;            // "/account/login?username=test" | ||||||
|     meta_data meta; |     meta_data *meta; | ||||||
|     unsigned int is_static:1; |     unsigned char is_static:1; | ||||||
|     unsigned int is_dir:1; |     unsigned char is_dir:1; | ||||||
| } http_uri; | } http_uri; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user