Guest User

Untitled

a guest
Aug 26th, 2016
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 28.53 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.     if (path == NULL)
  451.     {
  452.         return NULL;
  453.     }
  454.     filetest = fopen("index.php","r");
  455.     filetestTheSequal = fopen("index.html","r");
  456.     if(filetest == NULL && filetestTheSequal ==NULL)
  457.     {
  458.         return NULL;
  459.     }
  460.     else if(filetest != NULL && filetestTheSequal == NULL)
  461.     {
  462.         fclose(filetest);
  463.         fclose(filetestTheSequal);
  464.         return "/path/to/a/directory/index.php";
  465.     }
  466.     else if(filetestTheSequal != NULL && filetest == NULL)
  467.     {
  468.         fclose(filetest);
  469.         fclose(filetestTheSequal);
  470.         return"/path/to/a/directory/index.html";
  471.     }
  472.     fclose(filetest);
  473.     fclose(filetestTheSequal);
  474.     return NULL;
  475. }
  476.  
  477. /**
  478.  * Interprets PHP file at path using query string.
  479.  */
  480. void interpret(const char* path, const char* query)
  481. {
  482.     // ensure path is readable
  483.     if (access(path, R_OK) == -1)
  484.     {
  485.         error(403);
  486.         return;
  487.     }
  488.  
  489.     // open pipe to PHP interpreter
  490.     char* format = "QUERY_STRING=\"%s\" REDIRECT_STATUS=200 SCRIPT_FILENAME=\"%s\" php-cgi";
  491.     char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];
  492.     if (sprintf(command, format, query, path) < 0)
  493.     {
  494.         error(500);
  495.         return;
  496.     }
  497.     FILE* file = popen(command, "r");
  498.     if (file == NULL)
  499.     {
  500.         error(500);
  501.         return;
  502.     }
  503.  
  504.     // load interpreter's content
  505.     char* content;
  506.     size_t length;
  507.     if (load(file, &content, &length) == false)
  508.     {
  509.         error(500);
  510.         return;
  511.     }
  512.  
  513.     // close pipe
  514.     pclose(file);
  515.  
  516.     // subtract php-cgi's headers from content's length to get body's length
  517.     char* haystack = content;
  518.     char* needle = strstr(haystack, "\r\n\r\n");
  519.     if (needle == NULL)
  520.     {
  521.         free(content);
  522.         error(500);
  523.         return;
  524.     }
  525.  
  526.     // extract headers
  527.     char headers[needle + 2 - haystack + 1];
  528.     strncpy(headers, content, needle + 2 - haystack);
  529.     headers[needle + 2 - haystack] = '\0';
  530.  
  531.     // respond with interpreter's content
  532.     respond(200, headers, needle + 4, length - (needle - haystack + 4));
  533.  
  534.     // free interpreter's content
  535.     free(content);
  536. }
  537.  
  538. /**
  539.  * Responds to client with directory listing of path.
  540.  */
  541. void list(const char* path)
  542. {
  543.     // ensure path is readable and executable
  544.     if (access(path, R_OK | X_OK) == -1)
  545.     {
  546.         error(403);
  547.         return;
  548.     }
  549.  
  550.     // open directory
  551.     DIR* dir = opendir(path);
  552.     if (dir == NULL)
  553.     {
  554.         return;
  555.     }
  556.  
  557.     // buffer for list items
  558.     char* list = malloc(1);
  559.     list[0] = '\0';
  560.  
  561.     // iterate over directory entries
  562.     struct dirent** namelist = NULL;
  563.     int n = scandir(path, &namelist, NULL, alphasort);
  564.     for (int i = 0; i < n; i++)
  565.     {
  566.         // omit . from list
  567.         if (strcmp(namelist[i]->d_name, ".") == 0)
  568.         {
  569.             continue;
  570.         }
  571.  
  572.         // escape entry's name
  573.         char* name = htmlspecialchars(namelist[i]->d_name);
  574.         if (name == NULL)
  575.         {
  576.             free(list);
  577.             freedir(namelist, n);
  578.             error(500);
  579.             return;
  580.         }
  581.  
  582.         // append list item to buffer
  583.         char* template = "<li><a href=\"%s\">%s</a></li>";
  584.         list = realloc(list, strlen(list) + strlen(template) - 2 + strlen(name) - 2 + strlen(name) + 1);
  585.         if (list == NULL)
  586.         {
  587.             free(name);
  588.             freedir(namelist, n);
  589.             error(500);
  590.             return;
  591.         }
  592.         if (sprintf(list + strlen(list), template, name, name) < 0)
  593.         {
  594.             free(name);
  595.             freedir(namelist, n);
  596.             free(list);
  597.             error(500);
  598.             return;
  599.         }
  600.  
  601.         // free escaped name
  602.         free(name);
  603.     }
  604.  
  605.     // free memory allocated by scandir
  606.     freedir(namelist, n);
  607.  
  608.     // prepare response
  609.     const char* relative = path + strlen(root);
  610.     char* template = "<html><head><title>%s</title></head><body><h1>%s</h1><ul>%s</ul></body></html>";
  611.     char body[strlen(template) - 2 + strlen(relative) - 2 + strlen(relative) - 2 + strlen(list) + 1];
  612.     int length = sprintf(body, template, relative, relative, list);
  613.     if (length < 0)
  614.     {
  615.         free(list);
  616.         closedir(dir);
  617.         error(500);
  618.         return;
  619.     }
  620.  
  621.     // free buffer
  622.     free(list);
  623.  
  624.     // close directory
  625.     closedir(dir);
  626.  
  627.     // respond with list
  628.     char* headers = "Content-Type: text/html\r\n";
  629.     respond(200, headers, body, length);
  630. }
  631.  
  632. /**
  633.  * Loads a file into memory dynamically allocated on heap.
  634.  * Stores address thereof in *content and length thereof in *length.
  635.  */
  636. bool load(FILE* file, BYTE** content, size_t* length)
  637. {
  638.     if(file == NULL)
  639.     {
  640.         error(404);
  641.         return false;
  642.     }
  643.     BYTE storage[BYTES];
  644.     fread(storage,sizeof(storage),BYTES,file);
  645.     *length = BYTES;
  646.     *content = &storage[0];
  647.     return true;
  648. }
  649.  
  650. /**
  651.  * Returns MIME type for supported extensions, else NULL.
  652.  */
  653. const char* lookup(const char* path)
  654. {
  655.     if(path == NULL)
  656.     {
  657.         return NULL;
  658.     }
  659.     else if(strcasecmp(path,".css") == 0)
  660.     {
  661.         return "text/css";
  662.     }
  663.     else if(strcasecmp(path,".html") == 0)
  664.     {
  665.         return "text/html";
  666.     }
  667.     else if(strcasecmp(path,".gif") == 0)
  668.     {
  669.         return "text/css";
  670.     }
  671.     else if(strcasecmp(path,".css") == 0)
  672.     {
  673.         return "x-icon/ico";
  674.     }
  675.     else if(strcasecmp(path,".jpg") == 0)
  676.     {
  677.         return "image/jpeg";
  678.     }
  679.     else if(strcasecmp(path,".js") == 0)
  680.     {
  681.         return "text/javascript";
  682.     }
  683.     else if(strcasecmp(path,".php") == 0)
  684.     {
  685.         return "text/x-php";
  686.     }
  687.     else if(strcasecmp(path,".png") == 0)
  688.     {
  689.         return "image/png";
  690.     }
  691.     return NULL;
  692. }
  693.  
  694. /**
  695.  * Parses a request-line, storing its absolute-path at abs_path
  696.  * and its query string at query, both of which are assumed
  697.  * to be at least of length LimitRequestLine + 1.
  698.  */
  699. bool parse(const char* line, char* abs_path, char* query)
  700. {
  701.     char* lnCopy = strdup(line); // creates a copy of a non constant version of the line
  702.     char* method = strtok(lnCopy," "); // gets the first part of the line
  703.     char* request_target =  strtok(NULL, " "); // second part
  704.     char* version = strtok(NULL,"\r"); // third part
  705.     if(strcasecmp(version,"HTTP/1.1") != 0) // checks to make sure its the right HTTP version
  706.         {
  707.             error(505);
  708.             return false;
  709.         }
  710.         if(strcmp(method,"GET") != 0)
  711.         {
  712.             error(405);
  713.             return false;
  714.         }
  715.         for(int i = 0; i< strlen(request_target); i++)
  716.         {
  717.             if(request_target[i] == 34)
  718.             {
  719.                 error(400);
  720.                 return false;
  721.             }
  722.         }
  723.         if(strcasecmp (&request_target [0],"/") != 0)
  724.         {
  725.             printf("%i\n",strcasecmp (&request_target [0],"/"));
  726.             error(501);
  727.             return false;
  728.         }
  729.         printf("%s\n",request_target);
  730.        
  731.         bool qCheck = false;
  732.     for(int i =0; i< strlen(request_target);i++)
  733.     {
  734.         if(qCheck == false)
  735.         {
  736.             abs_path[i]=request_target[i];
  737.             if(strcmp(&request_target[i],"?") == 0)
  738.             {
  739.                 abs_path[i+1] = '\0';
  740.                 qCheck = true;
  741.             }
  742.         }
  743.         else if(qCheck == true)
  744.         {
  745.             if(strcmp(&request_target[i],"?") != 0)
  746.             {
  747.                 query[i] = request_target[i];
  748.             }
  749.         }
  750.     }
  751.     if(qCheck == false)
  752.     {
  753.         abs_path[strlen(abs_path)] = '\0';
  754.         query[0] = '\0';
  755.     }
  756.     else if(qCheck == true)
  757.     {
  758.         query[strlen(query)] = '\0';
  759.     }
  760.     printf("%s\n",abs_path);
  761.     //error(501);
  762.     return true;
  763. }
  764.  
  765. /**
  766.  * Returns status code's reason phrase.
  767.  *
  768.  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
  769.  * https://tools.ietf.org/html/rfc2324
  770.  */
  771. const char* reason(unsigned short code)
  772. {
  773.     switch (code)
  774.     {
  775.         case 200: return "OK";
  776.         case 301: return "Moved Permanently";
  777.         case 400: return "Bad Request";
  778.         case 403: return "Forbidden";
  779.         case 404: return "Not Found";
  780.         case 405: return "Method Not Allowed";
  781.         case 414: return "Request-URI Too Long";
  782.         case 418: return "I'm a teapot";
  783.         case 500: return "Internal Server Error";
  784.         case 501: return "Not Implemented";
  785.         case 505: return "HTTP Version Not Supported";
  786.         default: return NULL;
  787.     }
  788. }
  789.  
  790. /**
  791.  * Redirects client to uri.
  792.  */
  793. void redirect(const char* uri)
  794. {
  795.     char* template = "Location: %s\r\n";
  796.     char headers[strlen(template) - 2 + strlen(uri) + 1];
  797.     if (sprintf(headers, template, uri) < 0)
  798.     {
  799.         error(500);
  800.         return;
  801.     }
  802.     respond(301, headers, NULL, 0);
  803. }
  804.  
  805. /**
  806.  * Reads (without blocking) an HTTP request's headers into memory dynamically allocated on heap.
  807.  * Stores address thereof in *message and length thereof in *length.
  808.  */
  809. bool request(char** message, size_t* length)
  810. {
  811.     // ensure socket is open
  812.     if (cfd == -1)
  813.     {
  814.         return false;
  815.     }
  816.  
  817.     // initialize message and its length
  818.     *message = NULL;
  819.     *length = 0;
  820.  
  821.     // read message
  822.     while (*length < LimitRequestLine + LimitRequestFields * LimitRequestFieldSize + 4)
  823.     {
  824.         // read from socket
  825.         BYTE buffer[BYTES];
  826.         ssize_t bytes = read(cfd, buffer, BYTES);
  827.         if (bytes < 0)
  828.         {
  829.             if (*message != NULL)
  830.             {
  831.                 free(*message);
  832.                 *message = NULL;
  833.             }
  834.             *length = 0;
  835.             break;
  836.         }
  837.  
  838.         // append bytes to message
  839.         *message = realloc(*message, *length + bytes + 1);
  840.         if (*message == NULL)
  841.         {
  842.             *length = 0;
  843.             break;
  844.         }
  845.         memcpy(*message + *length, buffer, bytes);
  846.         *length += bytes;
  847.  
  848.         // null-terminate message thus far
  849.         *(*message + *length) = '\0';
  850.  
  851.         // search for CRLF CRLF
  852.         int offset = (*length - bytes < 3) ? *length - bytes : 3;
  853.         char* haystack = *message + *length - bytes - offset;
  854.         char* needle = strstr(haystack, "\r\n\r\n");
  855.         if (needle != NULL)
  856.         {
  857.             // trim to one CRLF and null-terminate
  858.             *length = needle - *message + 2;
  859.             *message = realloc(*message, *length + 1);
  860.             if (*message == NULL)
  861.             {
  862.                 break;
  863.             }
  864.             *(*message + *length) = '\0';
  865.  
  866.             // ensure request-line is no longer than LimitRequestLine
  867.             haystack = *message;
  868.             needle = strstr(haystack, "\r\n");
  869.             if (needle == NULL || (needle - haystack + 2) > LimitRequestLine)
  870.             {
  871.                 break;
  872.             }
  873.  
  874.             // count fields in message
  875.             int fields = 0;
  876.             haystack = needle + 2;
  877.             while (*haystack != '\0')
  878.             {
  879.                 // look for CRLF
  880.                 needle = strstr(haystack, "\r\n");
  881.                 if (needle == NULL)
  882.                 {
  883.                     break;
  884.                 }
  885.  
  886.                 // ensure field is no longer than LimitRequestFieldSize
  887.                 if (needle - haystack + 2 > LimitRequestFieldSize)
  888.                 {
  889.                     break;
  890.                 }
  891.  
  892.                 // look beyond CRLF
  893.                 haystack = needle + 2;
  894.             }
  895.  
  896.             // if we didn't get to end of message, we must have erred
  897.             if (*haystack != '\0')
  898.             {
  899.                 break;
  900.             }
  901.  
  902.             // ensure message has no more than LimitRequestFields
  903.             if (fields > LimitRequestFields)
  904.             {
  905.                 break;
  906.             }
  907.  
  908.             // valid
  909.             return true;
  910.         }
  911.     }
  912.  
  913.     // invalid
  914.     if (*message != NULL)
  915.     {
  916.         free(*message);
  917.     }
  918.     *message = NULL;
  919.     *length = 0;
  920.     return false;
  921. }
  922.  
  923. /**
  924.  * Responds to a client with status code, headers, and body of specified length.
  925.  */
  926. void respond(int code, const char* headers, const char* body, size_t length)
  927. {
  928.     // determine Status-Line's phrase
  929.     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
  930.     const char* phrase = reason(code);
  931.     if (phrase == NULL)
  932.     {
  933.         return;
  934.     }
  935.  
  936.     // respond with Status-Line
  937.     if (dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)
  938.     {
  939.         return;
  940.     }
  941.  
  942.     // respond with headers
  943.     if (dprintf(cfd, "%s", headers) < 0)
  944.     {
  945.         return;
  946.     }
  947.  
  948.     // respond with CRLF
  949.     if (dprintf(cfd, "\r\n") < 0)
  950.     {
  951.         return;
  952.     }
  953.  
  954.     // respond with body
  955.     if (write(cfd, body, length) == -1)
  956.     {
  957.         return;
  958.     }
  959.  
  960.     // log response line
  961.     if (code == 200)
  962.     {
  963.         // green
  964.         printf("\033[32m");
  965.     }
  966.     else
  967.     {
  968.         // red
  969.         printf("\033[33m");
  970.     }
  971.     printf("HTTP/1.1 %i %s", code, phrase);
  972.     printf("\033[39m\n");
  973. }
  974.  
  975. /**
  976.  * Starts server on specified port rooted at path.
  977.  */
  978. void start(short port, const char* path)
  979. {
  980.     // path to server's root
  981.     root = realpath(path, NULL);
  982.     if (root == NULL)
  983.     {
  984.         stop();
  985.     }
  986.  
  987.     // ensure root is executable
  988.     if (access(root, X_OK) == -1)
  989.     {
  990.         stop();
  991.     }
  992.  
  993.     // announce root
  994.     printf("\033[33m");
  995.     printf("Using %s for server's root", root);
  996.     printf("\033[39m\n");
  997.  
  998.     // create a socket
  999.     sfd = socket(AF_INET, SOCK_STREAM, 0);
  1000.     if (sfd == -1)
  1001.     {
  1002.         stop();
  1003.     }
  1004.  
  1005.     // allow reuse of address (to avoid "Address already in use")
  1006.     int optval = 1;
  1007.     setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  1008.  
  1009.     // assign name to socket
  1010.     struct sockaddr_in serv_addr;
  1011.     memset(&serv_addr, 0, sizeof(serv_addr));
  1012.     serv_addr.sin_family = AF_INET;
  1013.     serv_addr.sin_port = htons(port);
  1014.     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  1015.     if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
  1016.     {
  1017.         printf("\033[33m");
  1018.         printf("Port %i already in use", port);
  1019.         printf("\033[39m\n");
  1020.         stop();
  1021.     }
  1022.  
  1023.     // listen for connections
  1024.     if (listen(sfd, SOMAXCONN) == -1)
  1025.     {
  1026.         stop();
  1027.     }
  1028.  
  1029.     // announce port in use
  1030.     struct sockaddr_in addr;
  1031.     socklen_t addrlen = sizeof(addr);
  1032.     if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)
  1033.     {
  1034.         stop();
  1035.     }
  1036.     printf("\033[33m");
  1037.     printf("Listening on port %i", ntohs(addr.sin_port));
  1038.     printf("\033[39m\n");
  1039. }
  1040.  
  1041. /**
  1042.  * Stop server, deallocating any resources.
  1043.  */
  1044. void stop(void)
  1045. {
  1046.     // preserve errno across this function's library calls
  1047.     int errsv = errno;
  1048.  
  1049.     // announce stop
  1050.     printf("\033[33m");
  1051.     printf("Stopping server\n");
  1052.     printf("\033[39m");
  1053.  
  1054.     // free root, which was allocated by realpath
  1055.     if (root != NULL)
  1056.     {
  1057.         free(root);
  1058.     }
  1059.  
  1060.     // close server socket
  1061.     if (sfd != -1)
  1062.     {
  1063.         close(sfd);
  1064.     }
  1065.  
  1066.     // stop server
  1067.     exit(errsv);
  1068. }
  1069.  
  1070. /**
  1071.  * Transfers file at path with specified type to client.
  1072.  */
  1073. void transfer(const char* path, const char* type)
  1074. {
  1075.     // ensure path is readable
  1076.     if (access(path, R_OK) == -1)
  1077.     {
  1078.         error(403);
  1079.         return;
  1080.     }
  1081.  
  1082.     // open file
  1083.     FILE* file = fopen(path, "r");
  1084.     if (file == NULL)
  1085.     {
  1086.         error(500);
  1087.         return;
  1088.     }
  1089.  
  1090.     // load file's content
  1091.     BYTE* content;
  1092.     size_t length;
  1093.     if (load(file, &content, &length) == false)
  1094.     {
  1095.         error(500);
  1096.         return;
  1097.     }
  1098.  
  1099.     // close file
  1100.     fclose(file);
  1101.  
  1102.     // prepare response
  1103.     char* template = "Content-Type: %s\r\n";
  1104.     char headers[strlen(template) - 2 + strlen(type) + 1];
  1105.     if (sprintf(headers, template, type) < 0)
  1106.     {
  1107.         error(500);
  1108.         return;
  1109.     }
  1110.  
  1111.     // respond with file's content
  1112.     respond(200, headers, content, length);
  1113.  
  1114.     // free file's content
  1115.     free(content);
  1116. }
  1117.  
  1118. /**
  1119.  * URL-decodes string, returning dynamically allocated memory for decoded string
  1120.  * that must be deallocated by caller.
  1121.  */
  1122. char* urldecode(const char* s)
  1123. {
  1124.     // check whether s is NULL
  1125.     if (s == NULL)
  1126.     {
  1127.         return NULL;
  1128.     }
  1129.  
  1130.     // allocate enough (zeroed) memory for an undecoded copy of s
  1131.     char* t = calloc(strlen(s) + 1, 1);
  1132.     if (t == NULL)
  1133.     {
  1134.         return NULL;
  1135.     }
  1136.    
  1137.     // iterate over characters in s, decoding percent-encoded octets, per
  1138.     // https://www.ietf.org/rfc/rfc3986.txt
  1139.     for (int i = 0, j = 0, n = strlen(s); i < n; i++, j++)
  1140.     {
  1141.         if (s[i] == '%' && i < n - 2)
  1142.         {
  1143.             char octet[3];
  1144.             octet[0] = s[i + 1];
  1145.             octet[1] = s[i + 2];
  1146.             octet[2] = '\0';
  1147.             t[j] = (char) strtol(octet, NULL, 16);
  1148.             i += 2;
  1149.         }
  1150.         else if (s[i] == '+')
  1151.         {
  1152.             t[j] = ' ';
  1153.         }
  1154.         else
  1155.         {
  1156.             t[j] = s[i];
  1157.         }
  1158.     }
  1159.  
  1160.     // escaped string
  1161.     return t;
  1162. }
Advertisement
Add Comment
Please, Sign In to add comment