Advertisement
Guest User

Untitled

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