Advertisement
Guest User

Untitled

a guest
Aug 29th, 2016
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 29.31 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.         printf("My 404\n");
  643.         error(404);
  644.         return false;
  645.     }
  646.     BYTE storage[BYTES];
  647.     fread(storage,sizeof(storage),BYTES,file);
  648.     *length = BYTES;
  649.     *content = &storage[0];
  650.     return true;
  651. }
  652.  
  653. /**
  654.  * Returns MIME type for supported extensions, else NULL.
  655.  */
  656. const char* lookup(const char* path)
  657. {
  658.     printf("%s lookup path\n",path);
  659.     bool tFound = false;
  660.     char* fType = malloc(sizeof(char));
  661.     int fIncriment = 0;
  662.     if(path == NULL)
  663.     {
  664.         return NULL;
  665.     }
  666.     for(int i = 0; i<strlen(path);i++)
  667.     {
  668.         if (path[i] == '.')
  669.         {
  670.             tFound = true;
  671.         }
  672.         if(tFound == true)
  673.         {
  674.             fType[fIncriment] = path[i];
  675.             fIncriment++;
  676.         }
  677.     }
  678.     printf("%s fType\n",fType);
  679.     if(strcasecmp(fType,".css") == 0)
  680.     {
  681.         free(fType);
  682.         return "text/css";
  683.     }
  684.     else if(strcasecmp(fType,".html") == 0)
  685.     {
  686.         free(fType);
  687.         return "text/html";
  688.     }
  689.     else if(strcasecmp(fType,".gif") == 0)
  690.     {
  691.         free(fType);
  692.         return "text/css";
  693.     }
  694.     else if(strcasecmp(fType,".css") == 0)
  695.     {
  696.         free(fType);
  697.         return "x-icon/ico";
  698.     }
  699.     else if(strcasecmp(fType,".jpg") == 0)
  700.     {
  701.         free(fType);
  702.         return "image/jpeg";
  703.     }
  704.     else if(strcasecmp(fType,".js") == 0)
  705.     {
  706.         free(fType);
  707.         return "text/javascript";
  708.     }
  709.     else if(strcasecmp(fType,".php") == 0)
  710.     {
  711.         free(fType);
  712.         return "text/x-php";
  713.     }
  714.     else if(strcasecmp(fType,".png") == 0)
  715.     {
  716.         free(fType);
  717.         return "image/png";
  718.     }
  719.     free(fType);
  720.     printf("end of index\n");
  721.     return NULL;
  722. }
  723.  
  724. /**
  725.  * Parses a request-line, storing its absolute-path at abs_path
  726.  * and its query string at query, both of which are assumed
  727.  * to be at least of length LimitRequestLine + 1.
  728.  */
  729. bool parse(const char* line, char* abs_path, char* query)
  730. {
  731.     char* lnCopy = strdup(line); // creates a copy of a non constant version of the line
  732.     char* method = strtok(lnCopy," "); // gets the first part of the line
  733.     char* request_target =  strtok(NULL, " "); // second part
  734.     char* version = strtok(NULL,"\r"); // third part
  735.     if(strcasecmp(version,"HTTP/1.1") != 0) // checks to make sure its the right HTTP version
  736.         {
  737.             error(505);
  738.             return false;
  739.         }
  740.         if(strcmp(method,"GET") != 0)
  741.         {
  742.             error(405);
  743.             return false;
  744.         }
  745.         for(int i = 0; i< strlen(request_target); i++)
  746.         {
  747.             if(request_target[i] == 34)
  748.             {
  749.                 error(400);
  750.                 return false;
  751.             }
  752.         }
  753.         if(request_target[0] != '/')
  754.         {
  755.             printf("%i\n",strcasecmp (&request_target [0],"/"));
  756.             error(501);
  757.             return false;
  758.         }
  759.         printf("%s\n",request_target);
  760.        
  761.         bool qCheck = false;
  762.     for(int i =0; i< strlen(request_target);i++)
  763.     {
  764.         if(qCheck == false)
  765.         {
  766.             abs_path[i]=request_target[i];
  767.             if(strcmp(&request_target[i],"?") == 0)
  768.             {
  769.                 abs_path[i] = '\0';
  770.                 qCheck = true;
  771.             }
  772.         }
  773.         else if(qCheck == true)
  774.         {
  775.             if(strcmp(&request_target[i],"?") != 0)
  776.             {
  777.                 query[i] = request_target[i];
  778.             }
  779.         }
  780.     }
  781.     if(qCheck == false)
  782.     {
  783.         abs_path[strlen(request_target)] = '\0';
  784.         query[0] = '\0';
  785.     }
  786.     else if(qCheck == true)
  787.     {
  788.         abs_path[strlen(request_target)-strlen(query)] = '\0';
  789.         query[strlen(query)] = '\0';
  790.     }
  791.     printf("%s end of parse\n",abs_path);
  792.     return true;
  793. }
  794.  
  795. /**
  796.  * Returns status code's reason phrase.
  797.  *
  798.  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
  799.  * https://tools.ietf.org/html/rfc2324
  800.  */
  801. const char* reason(unsigned short code)
  802. {
  803.     switch (code)
  804.     {
  805.         case 200: return "OK";
  806.         case 301: return "Moved Permanently";
  807.         case 400: return "Bad Request";
  808.         case 403: return "Forbidden";
  809.         case 404: return "Not Found";
  810.         case 405: return "Method Not Allowed";
  811.         case 414: return "Request-URI Too Long";
  812.         case 418: return "I'm a teapot";
  813.         case 500: return "Internal Server Error";
  814.         case 501: return "Not Implemented";
  815.         case 505: return "HTTP Version Not Supported";
  816.         default: return NULL;
  817.     }
  818. }
  819.  
  820. /**
  821.  * Redirects client to uri.
  822.  */
  823. void redirect(const char* uri)
  824. {
  825.     char* template = "Location: %s\r\n";
  826.     char headers[strlen(template) - 2 + strlen(uri) + 1];
  827.     if (sprintf(headers, template, uri) < 0)
  828.     {
  829.         error(500);
  830.         return;
  831.     }
  832.     respond(301, headers, NULL, 0);
  833. }
  834.  
  835. /**
  836.  * Reads (without blocking) an HTTP request's headers into memory dynamically allocated on heap.
  837.  * Stores address thereof in *message and length thereof in *length.
  838.  */
  839. bool request(char** message, size_t* length)
  840. {
  841.     // ensure socket is open
  842.     if (cfd == -1)
  843.     {
  844.         return false;
  845.     }
  846.  
  847.     // initialize message and its length
  848.     *message = NULL;
  849.     *length = 0;
  850.  
  851.     // read message
  852.     while (*length < LimitRequestLine + LimitRequestFields * LimitRequestFieldSize + 4)
  853.     {
  854.         // read from socket
  855.         BYTE buffer[BYTES];
  856.         ssize_t bytes = read(cfd, buffer, BYTES);
  857.         if (bytes < 0)
  858.         {
  859.             if (*message != NULL)
  860.             {
  861.                 free(*message);
  862.                 *message = NULL;
  863.             }
  864.             *length = 0;
  865.             break;
  866.         }
  867.  
  868.         // append bytes to message
  869.         *message = realloc(*message, *length + bytes + 1);
  870.         if (*message == NULL)
  871.         {
  872.             *length = 0;
  873.             break;
  874.         }
  875.         memcpy(*message + *length, buffer, bytes);
  876.         *length += bytes;
  877.  
  878.         // null-terminate message thus far
  879.         *(*message + *length) = '\0';
  880.  
  881.         // search for CRLF CRLF
  882.         int offset = (*length - bytes < 3) ? *length - bytes : 3;
  883.         char* haystack = *message + *length - bytes - offset;
  884.         char* needle = strstr(haystack, "\r\n\r\n");
  885.         if (needle != NULL)
  886.         {
  887.             // trim to one CRLF and null-terminate
  888.             *length = needle - *message + 2;
  889.             *message = realloc(*message, *length + 1);
  890.             if (*message == NULL)
  891.             {
  892.                 break;
  893.             }
  894.             *(*message + *length) = '\0';
  895.  
  896.             // ensure request-line is no longer than LimitRequestLine
  897.             haystack = *message;
  898.             needle = strstr(haystack, "\r\n");
  899.             if (needle == NULL || (needle - haystack + 2) > LimitRequestLine)
  900.             {
  901.                 break;
  902.             }
  903.  
  904.             // count fields in message
  905.             int fields = 0;
  906.             haystack = needle + 2;
  907.             while (*haystack != '\0')
  908.             {
  909.                 // look for CRLF
  910.                 needle = strstr(haystack, "\r\n");
  911.                 if (needle == NULL)
  912.                 {
  913.                     break;
  914.                 }
  915.  
  916.                 // ensure field is no longer than LimitRequestFieldSize
  917.                 if (needle - haystack + 2 > LimitRequestFieldSize)
  918.                 {
  919.                     break;
  920.                 }
  921.  
  922.                 // look beyond CRLF
  923.                 haystack = needle + 2;
  924.             }
  925.  
  926.             // if we didn't get to end of message, we must have erred
  927.             if (*haystack != '\0')
  928.             {
  929.                 break;
  930.             }
  931.  
  932.             // ensure message has no more than LimitRequestFields
  933.             if (fields > LimitRequestFields)
  934.             {
  935.                 break;
  936.             }
  937.  
  938.             // valid
  939.             return true;
  940.         }
  941.     }
  942.  
  943.     // invalid
  944.     if (*message != NULL)
  945.     {
  946.         free(*message);
  947.     }
  948.     *message = NULL;
  949.     *length = 0;
  950.     return false;
  951. }
  952.  
  953. /**
  954.  * Responds to a client with status code, headers, and body of specified length.
  955.  */
  956. void respond(int code, const char* headers, const char* body, size_t length)
  957. {
  958.     // determine Status-Line's phrase
  959.     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
  960.     const char* phrase = reason(code);
  961.     if (phrase == NULL)
  962.     {
  963.         return;
  964.     }
  965.  
  966.     // respond with Status-Line
  967.     if (dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)
  968.     {
  969.         return;
  970.     }
  971.  
  972.     // respond with headers
  973.     if (dprintf(cfd, "%s", headers) < 0)
  974.     {
  975.         return;
  976.     }
  977.  
  978.     // respond with CRLF
  979.     if (dprintf(cfd, "\r\n") < 0)
  980.     {
  981.         return;
  982.     }
  983.  
  984.     // respond with body
  985.     if (write(cfd, body, length) == -1)
  986.     {
  987.         return;
  988.     }
  989.  
  990.     // log response line
  991.     if (code == 200)
  992.     {
  993.         // green
  994.         printf("\033[32m");
  995.     }
  996.     else
  997.     {
  998.         // red
  999.         printf("\033[33m");
  1000.     }
  1001.     printf("HTTP/1.1 %i %s", code, phrase);
  1002.     printf("\033[39m\n");
  1003. }
  1004.  
  1005. /**
  1006.  * Starts server on specified port rooted at path.
  1007.  */
  1008. void start(short port, const char* path)
  1009. {
  1010.     // path to server's root
  1011.     root = realpath(path, NULL);
  1012.     if (root == NULL)
  1013.     {
  1014.         stop();
  1015.     }
  1016.  
  1017.     // ensure root is executable
  1018.     if (access(root, X_OK) == -1)
  1019.     {
  1020.         stop();
  1021.     }
  1022.  
  1023.     // announce root
  1024.     printf("\033[33m");
  1025.     printf("Using %s for server's root", root);
  1026.     printf("\033[39m\n");
  1027.  
  1028.     // create a socket
  1029.     sfd = socket(AF_INET, SOCK_STREAM, 0);
  1030.     if (sfd == -1)
  1031.     {
  1032.         stop();
  1033.     }
  1034.  
  1035.     // allow reuse of address (to avoid "Address already in use")
  1036.     int optval = 1;
  1037.     setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  1038.  
  1039.     // assign name to socket
  1040.     struct sockaddr_in serv_addr;
  1041.     memset(&serv_addr, 0, sizeof(serv_addr));
  1042.     serv_addr.sin_family = AF_INET;
  1043.     serv_addr.sin_port = htons(port);
  1044.     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  1045.     if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
  1046.     {
  1047.         printf("\033[33m");
  1048.         printf("Port %i already in use", port);
  1049.         printf("\033[39m\n");
  1050.         stop();
  1051.     }
  1052.  
  1053.     // listen for connections
  1054.     if (listen(sfd, SOMAXCONN) == -1)
  1055.     {
  1056.         stop();
  1057.     }
  1058.  
  1059.     // announce port in use
  1060.     struct sockaddr_in addr;
  1061.     socklen_t addrlen = sizeof(addr);
  1062.     if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)
  1063.     {
  1064.         stop();
  1065.     }
  1066.     printf("\033[33m");
  1067.     printf("Listening on port %i", ntohs(addr.sin_port));
  1068.     printf("\033[39m\n");
  1069. }
  1070.  
  1071. /**
  1072.  * Stop server, deallocating any resources.
  1073.  */
  1074. void stop(void)
  1075. {
  1076.     // preserve errno across this function's library calls
  1077.     int errsv = errno;
  1078.  
  1079.     // announce stop
  1080.     printf("\033[33m");
  1081.     printf("Stopping server\n");
  1082.     printf("\033[39m");
  1083.  
  1084.     // free root, which was allocated by realpath
  1085.     if (root != NULL)
  1086.     {
  1087.         free(root);
  1088.     }
  1089.  
  1090.     // close server socket
  1091.     if (sfd != -1)
  1092.     {
  1093.         close(sfd);
  1094.     }
  1095.  
  1096.     // stop server
  1097.     exit(errsv);
  1098. }
  1099.  
  1100. /**
  1101.  * Transfers file at path with specified type to client.
  1102.  */
  1103. void transfer(const char* path, const char* type)
  1104. {
  1105.     // ensure path is readable
  1106.     if (access(path, R_OK) == -1)
  1107.     {
  1108.         error(403);
  1109.         return;
  1110.     }
  1111.  
  1112.     // open file
  1113.     FILE* file = fopen(path, "r");
  1114.     if (file == NULL)
  1115.     {
  1116.         error(500);
  1117.         return;
  1118.     }
  1119.  
  1120.     // load file's content
  1121.     BYTE* content;
  1122.     size_t length;
  1123.     if (load(file, &content, &length) == false)
  1124.     {
  1125.         error(500);
  1126.         return;
  1127.     }
  1128.  
  1129.     // close file
  1130.     fclose(file);
  1131.  
  1132.     // prepare response
  1133.     char* template = "Content-Type: %s\r\n";
  1134.     char headers[strlen(template) - 2 + strlen(type) + 1];
  1135.     if (sprintf(headers, template, type) < 0)
  1136.     {
  1137.         error(500);
  1138.         return;
  1139.     }
  1140.  
  1141.     // respond with file's content
  1142.     respond(200, headers, content, length);
  1143.  
  1144.     // free file's content
  1145.     free(content);
  1146. }
  1147.  
  1148. /**
  1149.  * URL-decodes string, returning dynamically allocated memory for decoded string
  1150.  * that must be deallocated by caller.
  1151.  */
  1152. char* urldecode(const char* s)
  1153. {
  1154.     // check whether s is NULL
  1155.     if (s == NULL)
  1156.     {
  1157.         return NULL;
  1158.     }
  1159.  
  1160.     // allocate enough (zeroed) memory for an undecoded copy of s
  1161.     char* t = calloc(strlen(s) + 1, 1);
  1162.     if (t == NULL)
  1163.     {
  1164.         return NULL;
  1165.     }
  1166.    
  1167.     // iterate over characters in s, decoding percent-encoded octets, per
  1168.     // https://www.ietf.org/rfc/rfc3986.txt
  1169.     for (int i = 0, j = 0, n = strlen(s); i < n; i++, j++)
  1170.     {
  1171.         if (s[i] == '%' && i < n - 2)
  1172.         {
  1173.             char octet[3];
  1174.             octet[0] = s[i + 1];
  1175.             octet[1] = s[i + 2];
  1176.             octet[2] = '\0';
  1177.             t[j] = (char) strtol(octet, NULL, 16);
  1178.             i += 2;
  1179.         }
  1180.         else if (s[i] == '+')
  1181.         {
  1182.             t[j] = ' ';
  1183.         }
  1184.         else
  1185.         {
  1186.             t[j] = s[i];
  1187.         }
  1188.     }
  1189.  
  1190.     // escaped string
  1191.     return t;
  1192. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement