Advertisement
Guest User

Untitled

a guest
May 23rd, 2016
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 29.81 KB | None | 0 0
  1. //
  2. // server.c
  3. //
  4. // Computer Science 50
  5. // Problem Set 6
  6. //
  7.  
  8. // feature test macro requirements
  9. #define _GNU_SOURCE
  10. #define _XOPEN_SOURCE 700
  11. #define _XOPEN_SOURCE_EXTENDED
  12.  
  13. // limits on an HTTP request's size, based on Apache's
  14. // http://httpd.apache.org/docs/2.2/mod/core.html
  15. #define LimitRequestFields 50
  16. #define LimitRequestFieldSize 4094
  17. #define LimitRequestLine 8190
  18.  
  19. // arbitray limit to http version string length
  20. #define LimitVersionLength 20
  21.  
  22. // number of bytes for buffers
  23. #define BYTES 512
  24.  
  25. // header files
  26. #include <arpa/inet.h>
  27. #include <dirent.h>
  28. #include <errno.h>
  29. #include <limits.h>
  30. #include <math.h>
  31. #include <signal.h>
  32. #include <stdbool.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <strings.h>
  37. #include <sys/socket.h>
  38. #include <sys/stat.h>
  39. #include <sys/types.h>
  40. #include <unistd.h>
  41.  
  42. // types
  43. typedef char BYTE;
  44.  
  45. // prototypes
  46. bool connected(void);
  47. void error(unsigned short code);
  48. void freedir(struct dirent** namelist, int n);
  49. void handler(int signal);
  50. char* htmlspecialchars(const char* s);
  51. char* indexes(const char* path);
  52. void interpret(const char* path, const char* query);
  53. void list(const char* path);
  54. bool load(FILE* file, BYTE** content, size_t* length);
  55. const char* lookup(const char* path);
  56. bool parse(const char* line, char* path, char* query);
  57. const char* reason(unsigned short code);
  58. void redirect(const char* uri);
  59. bool request(char** message, size_t* length);
  60. void respond(int code, const char* headers, const char* body, size_t length);
  61. void start(short port, const char* path);
  62. void stop(void);
  63. void transfer(const char* path, const char* type);
  64. char* urldecode(const char* s);
  65.  
  66.  
  67. // server's root
  68. char* root = NULL;
  69.  
  70. // file descriptor for sockets
  71. int cfd = -1, sfd = -1;
  72.  
  73. // flag indicating whether control-c has been heard
  74. bool signaled = false;
  75.  
  76. const char* valid_methods = "OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT";
  77. const char* supported_methods[1] = {"GET"};
  78. const char* supported_versions[1] = {"HTTP/1.1"};
  79.  
  80. int main(int argc, char* argv[])
  81. {
  82.     // a global variable defined in errno.h that's "set by system
  83.     // calls and some library functions [to a nonzero value]
  84.     // in the event of an error to indicate what went wrong"
  85.     errno = 0;
  86.  
  87.     // default to port 8080
  88.     int port = 8080;
  89.  
  90.     // usage
  91.     const char* usage = "Usage: server [-p port] /path/to/root";
  92.  
  93.     // parse command-line arguments
  94.     int opt;
  95.     while ((opt = getopt(argc, argv, "hp:")) != -1)
  96.     {
  97.         switch (opt)
  98.         {
  99.             // -h
  100.             case 'h':
  101.                 printf("%s\n", usage);
  102.                 return 0;
  103.  
  104.             // -p port
  105.             case 'p':
  106.                 port = atoi(optarg);
  107.                 break;
  108.         }
  109.     }
  110.  
  111.     // ensure port is a non-negative short and path to server's root is specified
  112.     if (port < 0 || port > SHRT_MAX || argv[optind] == NULL || strlen(argv[optind]) == 0)
  113.     {
  114.         // announce usage
  115.         printf("%s\n", usage);
  116.  
  117.         // return 2 just like bash's builtins
  118.         return 2;
  119.     }
  120.  
  121.     // start server
  122.     start(port, argv[optind]);
  123.  
  124.     // listen for SIGINT (aka control-c)
  125.     struct sigaction act;
  126.     act.sa_handler = handler;
  127.     act.sa_flags = 0;
  128.     sigemptyset(&act.sa_mask);
  129.     sigaction(SIGINT, &act, NULL);
  130.  
  131.     // a message and its length
  132.     char* message = NULL;
  133.     size_t length = 0;
  134.  
  135.     // path requested
  136.     char* path = NULL;
  137.  
  138.     // accept connections one at a time
  139.     while (true)
  140.     {
  141.         // free last path, if any
  142.         if (path != NULL)
  143.         {
  144.             free(path);
  145.             path = NULL;
  146.         }
  147.  
  148.         // free last message, if any
  149.         if (message != NULL)
  150.         {
  151.             free(message);
  152.             message = NULL;
  153.         }
  154.         length = 0;
  155.  
  156.         // close last client's socket, if any
  157.         if (cfd != -1)
  158.         {
  159.             close(cfd);
  160.             cfd = -1;
  161.         }
  162.  
  163.         // check for control-c
  164.         if (signaled)
  165.         {
  166.             stop();
  167.         }
  168.  
  169.         // check whether client has connected
  170.         if (connected())
  171.         {
  172.             // check for request
  173.             if (request(&message, &length))
  174.             {
  175.                 // extract message's request-line
  176.                 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
  177.                 const char* haystack = message;
  178.                 const char* needle = strstr(haystack, "\r\n");
  179.                 if (needle == NULL)
  180.                 {
  181.                     error(500);
  182.                     continue;
  183.                 }
  184.                 char line[needle - haystack + 2 + 1];
  185.                 strncpy(line, haystack, needle - haystack + 2);
  186.                 line[needle - haystack + 2] = '\0';
  187.  
  188.                 // parse request-line
  189.                 char abs_path[LimitRequestLine + 1];
  190.                 char query[LimitRequestLine + 1];
  191.                 if (parse(line, abs_path, query))
  192.                 {
  193.                     // URL-decode absolute-path
  194.                     char* p = urldecode(abs_path);
  195.                     if (p == NULL)
  196.                     {
  197.                         error(500);
  198.                         continue;
  199.                     }
  200.  
  201.                     // resolve absolute-path to local path
  202.                     path = malloc(strlen(root) + strlen(p) + 1);
  203.                     if (path == NULL)
  204.                     {
  205.                         error(500);
  206.                         continue;
  207.                     }
  208.                     strcpy(path, root);
  209.                     strcat(path, p);
  210.                     free(p);
  211.                    
  212.                     // ensure path exists
  213.                     if (access(path, F_OK) == -1)
  214.                     {
  215.                         error(404);
  216.                         continue;
  217.                     }
  218.  
  219.                     // if path to directory
  220.                     struct stat sb;
  221.                     if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
  222.                     {
  223.                         // redirect from absolute-path to absolute-path/
  224.                         if (abs_path[strlen(abs_path) - 1] != '/')
  225.                         {
  226.                             char uri[strlen(abs_path) + 1 + 1];
  227.                             strcpy(uri, abs_path);
  228.                             strcat(uri, "/");
  229.                             redirect(uri);
  230.                             continue;
  231.                         }
  232.  
  233.                         // use path/index.php or path/index.html, if present, instead of directory's path
  234.                         char* index = indexes(path);
  235.                        
  236.                         if (index != NULL)
  237.                         {
  238.                             free(path);
  239.                             path = index;
  240.                            
  241.                         }
  242.  
  243.                         // list contents of directory
  244.                         else
  245.                         {
  246.                             list(path);
  247.                             continue;
  248.                         }
  249.                     }
  250.  
  251.                     // look up MIME type for file at path
  252.                     const char* type = lookup(path);
  253.                     if (type == NULL)
  254.                     {
  255.                         error(501);
  256.                         continue;
  257.                     }
  258.  
  259.                     // interpret PHP script at path
  260.                     if (strcasecmp("text/x-php", type) == 0)
  261.                     {
  262.                         interpret(path, query);
  263.                     }
  264.  
  265.                     // transfer file at path
  266.                     else
  267.                     {
  268.                         transfer(path, type);
  269.                     }
  270.                 }
  271.             }
  272.         }
  273.     }
  274. }
  275.  
  276. /**
  277.  * Checks (without blocking) whether a client has connected to server.
  278.  * Returns true iff so.
  279.  */
  280. bool connected(void)
  281. {
  282.     struct sockaddr_in cli_addr;
  283.     memset(&cli_addr, 0, sizeof(cli_addr));
  284.     socklen_t cli_len = sizeof(cli_addr);
  285.     cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_len);
  286.     if (cfd == -1)
  287.     {
  288.         return false;
  289.     }
  290.     return true;
  291. }
  292.  
  293. /**
  294.  * Responds to client with specified status code.
  295.  */
  296. void error(unsigned short code)
  297. {
  298.     // determine code's reason-phrase
  299.     const char* phrase = reason(code);
  300.     if (phrase == NULL)
  301.     {
  302.         return;
  303.     }
  304.  
  305.     // template for response's content
  306.     char* template = "<html><head><title>%i %s</title></head><body><h1>%i %s</h1></body></html>";
  307.  
  308.     // render template
  309.     char body[(strlen(template) - 2 - ((int) log10(code) + 1) - 2 + strlen(phrase)) * 2 + 1];
  310.     int length = sprintf(body, template, code, phrase, code, phrase);
  311.     if (length < 0)
  312.     {
  313.         body[0] = '\0';
  314.         length = 0;
  315.     }
  316.  
  317.     // respond with error
  318.     char* headers = "Content-Type: text/html\r\n";
  319.     respond(code, headers, body, length);
  320. }
  321.  
  322. /**
  323.  * Frees memory allocated by scandir.
  324.  */
  325. void freedir(struct dirent** namelist, int n)
  326. {
  327.     if (namelist != NULL)
  328.     {
  329.         for (int i = 0; i < n; i++)
  330.         {
  331.             free(namelist[i]);
  332.         }
  333.         free(namelist);
  334.     }
  335. }
  336.  
  337. /**
  338.  * Handles signals.
  339.  */
  340. void handler(int signal)
  341. {
  342.     // control-c
  343.     if (signal == SIGINT)
  344.     {
  345.         signaled = true;
  346.     }
  347. }
  348.  
  349. /**
  350.  * Escapes string for HTML. Returns dynamically allocated memory for escaped
  351.  * string that must be deallocated by caller.
  352.  */
  353. char* htmlspecialchars(const char* s)
  354. {
  355.     // ensure s is not NULL
  356.     if (s == NULL)
  357.     {
  358.         return NULL;
  359.     }
  360.  
  361.     // allocate enough space for an unescaped copy of s
  362.     char* t = malloc(strlen(s) + 1);
  363.     if (t == NULL)
  364.     {
  365.         return NULL;
  366.     }
  367.     t[0] = '\0';
  368.  
  369.     // iterate over characters in s, escaping as needed
  370.     for (int i = 0, old = strlen(s), new = old; i < old; i++)
  371.     {
  372.         // escape &
  373.         if (s[i] == '&')
  374.         {
  375.             const char* entity = "&amp;";
  376.             new += strlen(entity);
  377.             t = realloc(t, new);
  378.             if (t == NULL)
  379.             {
  380.                 return NULL;
  381.             }
  382.             strcat(t, entity);
  383.         }
  384.  
  385.         // escape "
  386.         else if (s[i] == '"')
  387.         {
  388.             const char* entity = "&quot;";
  389.             new += strlen(entity);
  390.             t = realloc(t, new);
  391.             if (t == NULL)
  392.             {
  393.                 return NULL;
  394.             }
  395.             strcat(t, entity);
  396.         }
  397.  
  398.         // escape '
  399.         else if (s[i] == '\'')
  400.         {
  401.             const char* entity = "&#039;";
  402.             new += strlen(entity);
  403.             t = realloc(t, new);
  404.             if (t == NULL)
  405.             {
  406.                 return NULL;
  407.             }
  408.             strcat(t, entity);
  409.         }
  410.  
  411.         // escape <
  412.         else if (s[i] == '<')
  413.         {
  414.             const char* entity = "&lt;";
  415.             new += strlen(entity);
  416.             t = realloc(t, new);
  417.             if (t == NULL)
  418.             {
  419.                 return NULL;
  420.             }
  421.             strcat(t, entity);
  422.         }
  423.  
  424.         // escape >
  425.         else if (s[i] == '>')
  426.         {
  427.             const char* entity = "&gt;";
  428.             new += strlen(entity);
  429.             t = realloc(t, new);
  430.             if (t == NULL)
  431.             {
  432.                 return NULL;
  433.             }
  434.             strcat(t, entity);
  435.         }
  436.  
  437.         // don't escape
  438.         else
  439.         {
  440.             strncat(t, s + i, 1);
  441.         }
  442.     }
  443.  
  444.     // escaped string
  445.     return t;
  446. }
  447.  
  448. /**
  449.  * Checks, in order, whether index.php or index.html exists inside of path.
  450.  * Returns path to first match if so, else NULL.
  451.  */
  452. char* indexes(const char* path)
  453. {
  454.     // ensure path is readable and executable
  455.     if (access(path, R_OK | X_OK) == -1)
  456.     {
  457.         error(403);
  458.         return NULL;
  459.     }
  460.    
  461.     // open the directory
  462.     DIR* dir = opendir(path);
  463.     if (!dir)
  464.         return NULL;
  465.    
  466.     // search for index.php and index.html in the directory
  467.     struct dirent* names = NULL;
  468.        
  469.     for (names = readdir(dir); names != NULL; names = readdir(dir))
  470.     {
  471.        
  472.         if (strcmp(names->d_name, "index.html") == 0)
  473.         {
  474.             char* index = malloc(sizeof(char) * (strlen(path) + strlen(names->d_name) + 1));
  475.             if (!index)
  476.                 return NULL;
  477.                
  478.             index = strcpy(index, path);
  479.             index = strcat(index, names->d_name);
  480.             return index;
  481.         }
  482.        
  483.         else if (strcmp(names->d_name, "index.php") == 0)
  484.         {
  485.             char* index = malloc(sizeof(char) * (strlen(path) + strlen(names->d_name) + 1));
  486.             if (!index)
  487.                 return NULL;
  488.                
  489.             index = strcpy(index, path);
  490.             index = strcat(index, names->d_name);
  491.             return index;
  492.         }
  493.     }
  494.    
  495.     return NULL;
  496. }
  497.  
  498. /**
  499.  * Interprets PHP file at path using query string.
  500.  */
  501. void interpret(const char* path, const char* query)
  502. {
  503.     // ensure path is readable
  504.     if (access(path, R_OK) == -1)
  505.     {
  506.         error(403);
  507.         return;
  508.     }
  509.  
  510.     // open pipe to PHP interpreter
  511.     char* format = "QUERY_STRING=\"%s\" REDIRECT_STATUS=200 SCRIPT_FILENAME=\"%s\" php-cgi";
  512.     char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];
  513.     if (sprintf(command, format, query, path) < 0)
  514.     {
  515.         error(500);
  516.         return;
  517.     }
  518.     FILE* file = popen(command, "r");
  519.     if (file == NULL)
  520.     {
  521.         error(500);
  522.         return;
  523.     }
  524.  
  525.     // load interpreter's content
  526.     char* content;
  527.     size_t length;
  528.     if (load(file, &content, &length) == false)
  529.     {
  530.         error(500);
  531.         return;
  532.     }
  533.  
  534.     // close pipe
  535.     pclose(file);
  536.  
  537.     // subtract php-cgi's headers from content's length to get body's length
  538.     char* haystack = content;
  539.     char* needle = strstr(haystack, "\r\n\r\n");
  540.     if (needle == NULL)
  541.     {
  542.         free(content);
  543.         error(500);
  544.         return;
  545.     }
  546.  
  547.     // extract headers
  548.     char headers[needle + 2 - haystack + 1];
  549.     strncpy(headers, content, needle + 2 - haystack);
  550.     headers[needle + 2 - haystack] = '\0';
  551.  
  552.     // respond with interpreter's content
  553.     respond(200, headers, needle + 4, length - (needle - haystack + 4));
  554.  
  555.     // free interpreter's content
  556.     free(content);
  557. }
  558.  
  559. /**
  560.  * Responds to client with directory listing of path.
  561.  */
  562. void list(const char* path)
  563. {
  564.     // ensure path is readable and executable
  565.     if (access(path, R_OK | X_OK) == -1)
  566.     {
  567.         error(403);
  568.         return;
  569.     }
  570.  
  571.     // open directory
  572.     DIR* dir = opendir(path);
  573.     if (dir == NULL)
  574.     {
  575.         return;
  576.     }
  577.  
  578.     // buffer for list items
  579.     char* list = malloc(1);
  580.     list[0] = '\0';
  581.  
  582.     // iterate over directory entries
  583.     struct dirent** namelist = NULL;
  584.     int n = scandir(path, &namelist, NULL, alphasort);
  585.     for (int i = 0; i < n; i++)
  586.     {
  587.         // omit . from list
  588.         if (strcmp(namelist[i]->d_name, ".") == 0)
  589.         {
  590.             continue;
  591.         }
  592.  
  593.         // escape entry's name
  594.         char* name = htmlspecialchars(namelist[i]->d_name);
  595.         if (name == NULL)
  596.         {
  597.             free(list);
  598.             freedir(namelist, n);
  599.             error(500);
  600.             return;
  601.         }
  602.  
  603.         // append list item to buffer
  604.         char* template = "<li><a href=\"%s\">%s</a></li>";
  605.         list = realloc(list, strlen(list) + strlen(template) - 2 + strlen(name) - 2 + strlen(name) + 1);
  606.         if (list == NULL)
  607.         {
  608.             free(name);
  609.             freedir(namelist, n);
  610.             error(500);
  611.             return;
  612.         }
  613.         if (sprintf(list + strlen(list), template, name, name) < 0)
  614.         {
  615.             free(name);
  616.             freedir(namelist, n);
  617.             free(list);
  618.             error(500);
  619.             return;
  620.         }
  621.  
  622.         // free escaped name
  623.         free(name);
  624.     }
  625.  
  626.     // free memory allocated by scandir
  627.     freedir(namelist, n);
  628.  
  629.     // prepare response
  630.     const char* relative = path + strlen(root);
  631.     char* template = "<html><head><title>%s</title></head><body><h1>%s</h1><ul>%s</ul></body></html>";
  632.     char body[strlen(template) - 2 + strlen(relative) - 2 + strlen(relative) - 2 + strlen(list) + 1];
  633.     int length = sprintf(body, template, relative, relative, list);
  634.     if (length < 0)
  635.     {
  636.         free(list);
  637.         closedir(dir);
  638.         error(500);
  639.         return;
  640.     }
  641.  
  642.     // free buffer
  643.     free(list);
  644.  
  645.     // close directory
  646.     closedir(dir);
  647.  
  648.     // respond with list
  649.     char* headers = "Content-Type: text/html\r\n";
  650.     respond(200, headers, body, length);
  651. }
  652.  
  653. /**
  654.  * Loads a file into memory dynamically allocated on heap.
  655.  * Stores address thereof in *content and length thereof in *length.
  656.  */
  657. bool load(FILE* file, BYTE** content, size_t* length)
  658. {
  659.     //get the length of the file
  660.     //fseek(file, 0 , SEEK_END);
  661.     //*length = ftell(file);
  662.     //rewind(file);
  663.    
  664.     //if there is no current file position indicator, give the mem a buffersize
  665.     if ((ftell(file)) == -1)
  666.     {
  667.         *length = BYTES;
  668.     }
  669.    
  670.     //find actual size
  671.     else
  672.     {
  673.         fseek(file, 0 , SEEK_END);
  674.         *length = ftell(file);
  675.         rewind(file);
  676.     }
  677.    
  678.     //allocate memory for the buffer to put chars in
  679.     char* buffer = calloc(sizeof(char) * (*length), sizeof(char));
  680.     if (!buffer)
  681.         return false;
  682.    
  683.     //read the file in to the buffer
  684.     fread(buffer, *length, 1, file);
  685.    
  686.     //read that data into our content variable
  687.     *content = buffer;
  688.    
  689.     //make sure there is content
  690.     if(content != NULL)
  691.         return true;
  692.     else
  693.         return false;
  694. }
  695. /**
  696.  * Returns MIME type for supported extensions, else NULL.
  697.  */
  698. const char* lookup(const char* path)
  699. {
  700.     // check that path is not null
  701.     if (path == NULL)
  702.         return NULL;
  703.        
  704.     //returns pointer of last occurance of '.'
  705.     char* extension = strrchr(path, '.');
  706.    
  707.     //compares the ext. to the actual ext. given and returns correct format
  708.     if (strcasecmp(extension, ".css") == 0)
  709.          return "text/css";
  710.     else if (strcasecmp(extension, ".html") == 0)
  711.         return "text/html";
  712.     else if (strcasecmp(extension, ".gif") == 0)
  713.         return "image/gif";
  714.     else if (strcasecmp(extension, ".ico") == 0)
  715.         return "image/x-icon";
  716.     else if (strcasecmp(extension, ".jpg") == 0)
  717.         return "image/jpeg";
  718.     else if (strcasecmp(extension, ".js") == 0)
  719.         return "text/javascript";
  720.     else if (strcasecmp(extension, ".php") == 0)
  721.         return "text/x-php";
  722.     else if (strcasecmp(extension, ".png") == 0)
  723.         return "image/png";
  724.     else
  725.         return NULL;
  726.        
  727.     free(extension);
  728. }
  729.  
  730. /**
  731.  * Parses a request-line, storing its absolute-path at abs_path
  732.  * and its query string at query, both of which are assumed
  733.  * to be at least of length LimitRequestLine + 1.
  734.  */
  735. bool parse(const char* line, char* abs_path, char* query)
  736. {
  737.     int length = strlen(line) +1;
  738.    
  739.     //copy the line into new so we can use strsep and null over chars
  740.     char* copyline = malloc(sizeof(char) * length);
  741.     strncpy(copyline, line, length);
  742.    
  743.     //find token deliminated by space and repeat with NULL to use same string
  744.     char* method = strtok(copyline, " ");
  745.     char* request = strtok(NULL, " ");
  746.     char* version = strtok(NULL, " ");
  747.    
  748.     //null terminate and remove CRLF
  749.     strstr(version, "\r\n")[0] = '\0';
  750.     strcat(request, "\0");
  751.     strcat(method, "\0");
  752.    
  753.     //return error 405 if method is not GET, string compare method to find out
  754.     if (strcmp(method, "GET") != 0)
  755.     {
  756.         error(405);
  757.         return false;
  758.     }
  759.    
  760.     //return error 502 if request-target does not start with /
  761.     if (request[0] != '/')
  762.     {
  763.         error(501);
  764.         return false;
  765.     }
  766.    
  767.     //if there is a " in the reuest, return an error
  768.     if (strchr("\"", *request) != NULL)
  769.     {
  770.         error(400);
  771.         return false;
  772.     }
  773.    
  774.     //HTTP version has to be 1.1
  775.     if (strcmp(version, "HTTP/1.1") != 0)
  776.     {
  777.         error(505);
  778.         return false;
  779.     }
  780.    
  781.     strcpy(abs_path, request);
  782.    
  783.     if (strchr(abs_path, '?') != NULL)
  784.     {
  785.         strcpy(abs_path, strtok(request, "?"));
  786.         strcpy(query, strtok(NULL, "?"));
  787.         strcat(query, "\0");
  788.         strcat(abs_path, "\0");
  789.     }
  790.    
  791.     else
  792.     {
  793.         strcat(abs_path, "\0");
  794.     }
  795.    
  796.     return true;
  797. }
  798.  
  799. /**
  800.  * Returns status code's reason phrase.
  801.  *
  802.  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
  803.  * https://tools.ietf.org/html/rfc2324
  804.  */
  805. const char* reason(unsigned short code)
  806. {
  807.     switch (code)
  808.     {
  809.         case 200: return "OK";
  810.         case 301: return "Moved Permanently";
  811.         case 400: return "Bad Request";
  812.         case 403: return "Forbidden";
  813.         case 404: return "Not Found";
  814.         case 405: return "Method Not Allowed";
  815.         case 414: return "Request-URI Too Long";
  816.         case 418: return "I'm a teapot";
  817.         case 500: return "Internal Server Error";
  818.         case 501: return "Not Implemented";
  819.         case 505: return "HTTP Version Not Supported";
  820.         default: return NULL;
  821.     }
  822. }
  823.  
  824. /**
  825.  * Redirects client to uri.
  826.  */
  827. void redirect(const char* uri)
  828. {
  829.     char* template = "Location: %s\r\n";
  830.     char headers[strlen(template) - 2 + strlen(uri) + 1];
  831.     if (sprintf(headers, template, uri) < 0)
  832.     {
  833.         error(500);
  834.         return;
  835.     }
  836.     respond(301, headers, NULL, 0);
  837. }
  838.  
  839. /**
  840.  * Reads (without blocking) an HTTP request's headers into memory dynamically allocated on heap.
  841.  * Stores address thereof in *message and length thereof in *length.
  842.  */
  843. bool request(char** message, size_t* length)
  844. {
  845.     // ensure socket is open
  846.     if (cfd == -1)
  847.     {
  848.         return false;
  849.     }
  850.  
  851.     // initialize message and its length
  852.     *message = NULL;
  853.     *length = 0;
  854.  
  855.     // read message
  856.     while (*length < LimitRequestLine + LimitRequestFields * LimitRequestFieldSize + 4)
  857.     {
  858.         // read from socket
  859.         BYTE buffer[BYTES];
  860.         ssize_t bytes = read(cfd, buffer, BYTES);
  861.         if (bytes < 0)
  862.         {
  863.             if (*message != NULL)
  864.             {
  865.                 free(*message);
  866.                 *message = NULL;
  867.             }
  868.             *length = 0;
  869.             break;
  870.         }
  871.  
  872.         // append bytes to message
  873.         *message = realloc(*message, *length + bytes + 1);
  874.         if (*message == NULL)
  875.         {
  876.             *length = 0;
  877.             break;
  878.         }
  879.         memcpy(*message + *length, buffer, bytes);
  880.         *length += bytes;
  881.  
  882.         // null-terminate message thus far
  883.         *(*message + *length) = '\0';
  884.  
  885.         // search for CRLF CRLF
  886.         int offset = (*length - bytes < 3) ? *length - bytes : 3;
  887.         char* haystack = *message + *length - bytes - offset;
  888.         char* needle = strstr(haystack, "\r\n\r\n");
  889.         if (needle != NULL)
  890.         {
  891.             // trim to one CRLF and null-terminate
  892.             *length = needle - *message + 2;
  893.             *message = realloc(*message, *length + 1);
  894.             if (*message == NULL)
  895.             {
  896.                 break;
  897.             }
  898.             *(*message + *length) = '\0';
  899.  
  900.             // ensure request-line is no longer than LimitRequestLine
  901.             haystack = *message;
  902.             needle = strstr(haystack, "\r\n");
  903.             if (needle == NULL || (needle - haystack + 2) > LimitRequestLine)
  904.             {
  905.                 break;
  906.             }
  907.  
  908.             // count fields in message
  909.             int fields = 0;
  910.             haystack = needle + 2;
  911.             while (*haystack != '\0')
  912.             {
  913.                 // look for CRLF
  914.                 needle = strstr(haystack, "\r\n");
  915.                 if (needle == NULL)
  916.                 {
  917.                     break;
  918.                 }
  919.  
  920.                 // ensure field is no longer than LimitRequestFieldSize
  921.                 if (needle - haystack + 2 > LimitRequestFieldSize)
  922.                 {
  923.                     break;
  924.                 }
  925.  
  926.                 // look beyond CRLF
  927.                 haystack = needle + 2;
  928.             }
  929.  
  930.             // if we didn't get to end of message, we must have erred
  931.             if (*haystack != '\0')
  932.             {
  933.                 break;
  934.             }
  935.  
  936.             // ensure message has no more than LimitRequestFields
  937.             if (fields > LimitRequestFields)
  938.             {
  939.                 break;
  940.             }
  941.  
  942.             // valid
  943.             return true;
  944.         }
  945.     }
  946.  
  947.     // invalid
  948.     if (*message != NULL)
  949.     {
  950.         free(*message);
  951.     }
  952.     *message = NULL;
  953.     *length = 0;
  954.     return false;
  955. }
  956.  
  957. /**
  958.  * Responds to a client with status code, headers, and body of specified length.
  959.  */
  960. void respond(int code, const char* headers, const char* body, size_t length)
  961. {
  962.     // determine Status-Line's phrase
  963.     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
  964.     const char* phrase = reason(code);
  965.     if (phrase == NULL)
  966.     {
  967.         return;
  968.     }
  969.  
  970.     // respond with Status-Line
  971.     if (dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)
  972.     {
  973.         return;
  974.     }
  975.  
  976.     // respond with headers
  977.     if (dprintf(cfd, "%s", headers) < 0)
  978.     {
  979.         return;
  980.     }
  981.  
  982.     // respond with CRLF
  983.     if (dprintf(cfd, "\r\n") < 0)
  984.     {
  985.         return;
  986.     }
  987.  
  988.     // respond with body
  989.     if (write(cfd, body, length) == -1)
  990.     {
  991.         return;
  992.     }
  993.  
  994.     // log response line
  995.     if (code == 200)
  996.     {
  997.         // green
  998.         printf("\033[32m");
  999.     }
  1000.     else
  1001.     {
  1002.         // red
  1003.         printf("\033[33m");
  1004.     }
  1005.     printf("HTTP/1.1 %i %s", code, phrase);
  1006.     printf("\033[39m\n");
  1007. }
  1008.  
  1009. /**
  1010.  * Starts server on specified port rooted at path.
  1011.  */
  1012. void start(short port, const char* path)
  1013. {
  1014.     // path to server's root
  1015.     root = realpath(path, NULL);
  1016.     if (root == NULL)
  1017.     {
  1018.         stop();
  1019.     }
  1020.  
  1021.     // ensure root is executable
  1022.     if (access(root, X_OK) == -1)
  1023.     {
  1024.         stop();
  1025.     }
  1026.  
  1027.     // announce root
  1028.     printf("\033[33m");
  1029.     printf("Using %s for server's root", root);
  1030.     printf("\033[39m\n");
  1031.  
  1032.     // create a socket
  1033.     sfd = socket(AF_INET, SOCK_STREAM, 0);
  1034.     if (sfd == -1)
  1035.     {
  1036.         stop();
  1037.     }
  1038.  
  1039.     // allow reuse of address (to avoid "Address already in use")
  1040.     int optval = 1;
  1041.     setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  1042.  
  1043.     // assign name to socket
  1044.     struct sockaddr_in serv_addr;
  1045.     memset(&serv_addr, 0, sizeof(serv_addr));
  1046.     serv_addr.sin_family = AF_INET;
  1047.     serv_addr.sin_port = htons(port);
  1048.     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  1049.     if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
  1050.     {
  1051.         printf("\033[33m");
  1052.         printf("Port %i already in use", port);
  1053.         printf("\033[39m\n");
  1054.         stop();
  1055.     }
  1056.  
  1057.     // listen for connections
  1058.     if (listen(sfd, SOMAXCONN) == -1)
  1059.     {
  1060.         stop();
  1061.     }
  1062.  
  1063.     // announce port in use
  1064.     struct sockaddr_in addr;
  1065.     socklen_t addrlen = sizeof(addr);
  1066.     if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)
  1067.     {
  1068.         stop();
  1069.     }
  1070.     printf("\033[33m");
  1071.     printf("Listening on port %i", ntohs(addr.sin_port));
  1072.     printf("\033[39m\n");
  1073. }
  1074.  
  1075. /**
  1076.  * Stop server, deallocating any resources.
  1077.  */
  1078. void stop(void)
  1079. {
  1080.     // preserve errno across this function's library calls
  1081.     int errsv = errno;
  1082.  
  1083.     // announce stop
  1084.     printf("\033[33m");
  1085.     printf("Stopping server\n");
  1086.     printf("\033[39m");
  1087.  
  1088.     // free root, which was allocated by realpath
  1089.     if (root != NULL)
  1090.     {
  1091.         free(root);
  1092.     }
  1093.  
  1094.     // close server socket
  1095.     if (sfd != -1)
  1096.     {
  1097.         close(sfd);
  1098.     }
  1099.  
  1100.     // stop server
  1101.     exit(errsv);
  1102. }
  1103.  
  1104. /**
  1105.  * Transfers file at path with specified type to client.
  1106.  */
  1107. void transfer(const char* path, const char* type)
  1108. {
  1109.     // ensure path is readable
  1110.     if (access(path, R_OK) == -1)
  1111.     {
  1112.         error(403);
  1113.         return;
  1114.     }
  1115.  
  1116.     // open file
  1117.     FILE* file = fopen(path, "r");
  1118.     if (file == NULL)
  1119.     {
  1120.         error(500);
  1121.         return;
  1122.     }
  1123.  
  1124.     // load file's content
  1125.     BYTE* content;
  1126.     size_t length;
  1127.     if (load(file, &content, &length) == false)
  1128.     {
  1129.         error(500);
  1130.         return;
  1131.     }
  1132.  
  1133.     // close file
  1134.     fclose(file);
  1135.  
  1136.     // prepare response
  1137.     char* template = "Content-Type: %s\r\n";
  1138.     char headers[strlen(template) - 2 + strlen(type) + 1];
  1139.     if (sprintf(headers, template, type) < 0)
  1140.     {
  1141.         error(500);
  1142.         return;
  1143.     }
  1144.  
  1145.     // respond with file's content
  1146.     respond(200, headers, content, length);
  1147.  
  1148.     // free file's content
  1149.     free(content);
  1150. }
  1151.  
  1152. /**
  1153.  * URL-decodes string, returning dynamically allocated memory for decoded string
  1154.  * that must be deallocated by caller
  1155.  */
  1156. char* urldecode(const char* s)
  1157. {
  1158.     // check whether s is NULL
  1159.     if (s == NULL)
  1160.     {
  1161.         return NULL;
  1162.     }
  1163.  
  1164.     // allocate enough (zeroed) memory for an undecoded copy of s
  1165.     char* t = calloc(strlen(s) + 1, 1);
  1166.     if (t == NULL)
  1167.     {
  1168.         return NULL;
  1169.     }
  1170.    
  1171.     // iterate over characters in s, decoding percent-encoded octets, per
  1172.     // https://www.ietf.org/rfc/rfc3986.txt
  1173.     for (int i = 0, j = 0, n = strlen(s); i < n; i++, j++)
  1174.     {
  1175.         if (s[i] == '%' && i < n - 2)
  1176.         {
  1177.             char octet[3];
  1178.             octet[0] = s[i + 1];
  1179.             octet[1] = s[i + 2];
  1180.             octet[2] = '\0';
  1181.             t[j] = (char) strtol(octet, NULL, 16);
  1182.             i += 2;
  1183.         }
  1184.         else if (s[i] == '+')
  1185.         {
  1186.             t[j] = ' ';
  1187.         }
  1188.         else
  1189.         {
  1190.             t[j] = s[i];
  1191.         }
  1192.     }
  1193.  
  1194.     // escaped string
  1195.     return t;
  1196. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement