From e7ff0ead3b63bc1de71c4dbc41cd0b45dd284057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sun, 1 Aug 2021 18:09:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E5=8F=98=E5=91=BD=E4=BB=A4=E8=A1=8C?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BB=A5uid=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=92=8C414=E7=8A=B6=E6=80=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++------- server.c | 73 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 3599627..2176884 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,31 @@ Created November 1999 by J. David Blackstone. Modified June 2021 by Fumiama(源文雨) # Protocol +A necessary subset of `HTTP 1.0` with following options of request header being supported. -A necessary subset of `HTTP 1.0` with following options of request header being supported ### From client - Content-Length + ### From server - Content-Length - Content-Type (only support text/plain image/x-icon text/css text/html) - Server + ### Code - 200 OK - 400 BAD REQUEST - 404 NOT FOUND +- 414 Request-URI Too Long - 500 Internal Server Error - 501 Method Not Implemented # Features - 1. Serve files 2. CGI 3. Listen on `ipv6` 4. Multi-thread # Compile - ```bash git clone https://github.com/fumiama/simple-http-server.git cd simple-http-server @@ -41,17 +42,16 @@ make install ``` # Command line usage - ```bash -simple-http-server -d port chdir +simple-http-server [-d] [-p ] [-r ] [-u ] ``` -- **-d** - run as daemon -- **port** - bind server on this port (0 for a random one) -- **chdir** - change root dir to here +- **-d**: run as daemon. +- **-p**: if not set, choose a random one. +- **-r**: http root dir. +- **-u**: run as this uid. # CGI usage - When you put an executable file into the web path, the server will call `execl` to run it while passing 3 parameters as below ```c diff --git a/server.c b/server.c index 3f512c3..c8b15e9 100644 --- a/server.c +++ b/server.c @@ -58,6 +58,7 @@ static void headers(int, const char *); static void not_found(int); static void serve_file(int, const char *); static int startup(u_int16_t *); +static void toolong(int); static void unimplemented(int); /**********************************************************************/ @@ -65,6 +66,11 @@ static void unimplemented(int); * return. Process the request appropriately. * Parameters: the socket connected to the client */ /**********************************************************************/ + +/* read & discard headers */ +#define discard(client) \ + while((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)) + static void accept_request(void *cli) { pthread_detach(pthread_self()); int client =(int)cli; @@ -95,6 +101,7 @@ static void accept_request(void *cli) { if(strcasecmp(method, "POST") == 0) { if(method_type == GET) { + discard(client); unimplemented(client); return; } else { @@ -104,11 +111,14 @@ static void accept_request(void *cli) { } i = 0; - while(ISspace(buf[j]) &&(j < sizeof(buf))) j++; - while(!ISspace(buf[j]) &&(i < sizeof(url) - 1) &&(j < sizeof(buf))) { + while(ISspace(buf[j]) && (j < sizeof(buf))) j++; + for(; !ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)); i++, j++) { url[i] = buf[j]; - i++; - j++; + } + if(!ISspace(buf[j])) { + discard(client); + toolong(client); + return; } url[i] = '\0'; @@ -127,16 +137,24 @@ static void accept_request(void *cli) { while((*url_start == '.' || *url_start == '/' || *url_start == '#') && *url_start != 0) url_start++; path[0] = '.'; path[1] = '/'; path[2] = 0; if(*url_start) strncat(path, url_start, 499); - printf("%s: %s with query: %s\n", method, path, query_string); + printf("%s %s with query '%s': ", method, path, query_string); + //花括号不可省略 if(stat(path, &st) == -1) { - /* read & discard headers */ - while((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); + discard(client); not_found(client); } else { - if((st.st_mode & S_IFMT) == S_IFDIR) strcat(path, "/index.html"); - else if((st.st_mode & S_IXUSR) ||(st.st_mode & S_IXGRP) ||(st.st_mode & S_IXOTH)) cgi = 1; - /* read & discard headers */ + if((st.st_mode & S_IFMT) == S_IFDIR) { + strcat(path, "/index.html"); + //花括号不可省略 + if(stat(path, &st) == -1) { + discard(client); + not_found(client); + close(client); + return; + } + } int content_length = 0; + if((st.st_mode & S_IXUSR) ||(st.st_mode & S_IXGRP) ||(st.st_mode & S_IXOTH)) cgi = 1; while((numchars > 0) && strcmp("\n", buf)) { numchars = get_line(client, buf, sizeof(buf)); if(!content_length && strcasestr(buf, "Content-Length: ")) { @@ -391,6 +409,7 @@ static void headers(int client, const char *filepath) { ADD_HERDER_PARAM(CONTENT_TYPE, EXTNM_IS_NOT("html")?(EXTNM_IS_NOT(".css")?(EXTNM_IS_NOT(".ico")?"text/plain":"image/x-icon"):"text/css"):"text/html"); ADD_HERDER_PARAM(CONTENT_LEN "\r\n", get_file_size(filepath, client)); send(client, buf, offset, 0); + puts("200 OK."); } /**********************************************************************/ @@ -399,6 +418,7 @@ static void headers(int client, const char *filepath) { #define HTTP404 "HTTP/1.0 404 NOT FOUND\r\n" SERVER_STRING "Content-Type: text/html\r\n\r\nNot Found\r\n

The server could not fulfill\r\nyour request because the resource specified\r\nis unavailable or nonexistent.\r\n\r\n" static void not_found(int client) { send(client, HTTP404, sizeof(HTTP404)-1, 0); + puts("404 Not Found."); } /**********************************************************************/ @@ -466,6 +486,16 @@ static int startup(uint16_t *port) { return(httpd); } +/**********************************************************************/ +/* Inform the client that the requested web method has not been + * implemented. + * Parameter: the client socket */ +/**********************************************************************/ +#define HTTP414 "HTTP/1.0 414 Request-URI Too Long\r\n" SERVER_STRING "Content-Type: text/html\r\n\r\nRequest-URI Too Long\r\n\r\n

HTTP request url length is over 254 chars.\r\n\r\n" +static void toolong(int client) { + send(client, HTTP414, sizeof(HTTP414)-1, 0); +} + /**********************************************************************/ /* Inform the client that the requested web method has not been * implemented. @@ -492,23 +522,36 @@ static void unimplemented(int client) { error_die("accept");\ } +#define argequ(arg) (*(uint16_t*)argv[i] == *(uint16_t*)(arg)) int main(int argc, char **argv) { - if(argc != 3 && argc != 4) puts("Usage: simple-http-server -d port chdir"); + if(argc > 8) puts("Usage:\tsimple-http-server [-d] [-p ] [-r ] [-u ]\n -d:\trun as daemon.\n -p:\tif not set, choose a random one.\n -r:\thttp root dir.\n -u:\trun as this uid."); else { - int as_daemon = *(uint16_t*)argv[1] == *(uint16_t*)"-d"; + int as_daemon = 0; + uint16_t port = 0; + char *cdir = "./"; + uid_t uid = -1; int server_sock = -1; - uint16_t port =(uint16_t)atoi(argv[as_daemon?2:1]); int client_sock = -1; int pid = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread; - char *cdir = argv[as_daemon?3:2]; + for(int i = 1; i < argc; i++) { + if(!as_daemon && argequ("-d")) as_daemon = 1; + else if(argequ("-p")) port = (uint16_t)atoi(argv[i + 1]); + else if(argequ("-r")) cdir = argv[i + 1]; + else if(argequ("-u")) uid = atoi(argv[i + 1]); + } + if(chdir(cdir)) error_die("chdir"); server_sock = startup(&port); - printf("httpd running on port %d\n", port); + printf("httpd running on 0.0.0.0:%d at %s\n", port, cdir); if(as_daemon) { + if(uid > 0) { + setuid(uid); + setgid(uid); + } pid = fork(); if(pid == 0) pid = fork(); else return 0;