Guest User

CS50 PSet6

a guest
Nov 16th, 2016
17
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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 <ctype.h>
  25. #include <dirent.h>
  26. #include <errno.h>
  27. #include <limits.h>
  28. #include <math.h>
  29. #include <signal.h>
  30. #include <stdbool.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <strings.h>
  35. #include <sys/socket.h>
  36. #include <sys/stat.h>
  37. #include <sys/types.h>
  38. #include <unistd.h>
  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.  
  449. if (access(path, R_OK) == -1)
  450. {
  451. return NULL;
  452. }
  453. int base_len = strlen(path);
  454. char* php = "/index.php";
  455. char* html = "/index.html";
  456. int len1 = strlen(php);
  457. int len2 = strlen(html);
  458. char test1[base_len + len1 + 1];
  459. strcpy(test1, path);
  460. strcat(test1, php);
  461. char test2[base_len + len2 + 1];
  462. strcpy(test2, path);
  463. strcat(test2, html);
  464.  
  465. if (access(test1, R_OK) == 0)
  466. {
  467. char* return_ptr = malloc(sizeof(test1));
  468. if (return_ptr == NULL)
  469. return false;
  470. strcpy(return_ptr, test1);
  471. return return_ptr;
  472. }
  473. if (access(test2, R_OK) == 0)
  474. {
  475. char* return_ptr = malloc(sizeof(test2));
  476. if (return_ptr == NULL)
  477. return false;
  478. strcpy(return_ptr, test2);
  479. return return_ptr;
  480. }
  481. return NULL;
  482. }
  483.  
  484. /**
  485. * Interprets PHP file at path using query string.
  486. */
  487. void interpret(const char* path, const char* query)
  488. {
  489. // ensure path is readable
  490. if (access(path, R_OK) == -1)
  491. {
  492. error(403);
  493. return;
  494. }
  495.  
  496. // open pipe to PHP interpreter
  497. char* format = "QUERY_STRING=\"%s\" REDIRECT_STATUS=200 SCRIPT_FILENAME=\"%s\" php-cgi";
  498. char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];
  499. if (sprintf(command, format, query, path) < 0)
  500. {
  501. error(500);
  502. return;
  503. }
  504. FILE* file = popen(command, "r");
  505. if (file == NULL)
  506. {
  507. error(500);
  508. return;
  509. }
  510.  
  511. // load interpreter's content
  512. char* content;
  513. size_t length;
  514. if (load(file, &content, &length) == false)
  515. {
  516. error(500);
  517. return;
  518. }
  519.  
  520. // close pipe
  521. pclose(file);
  522.  
  523. // subtract php-cgi's headers from content's length to get body's length
  524. char* haystack = content;
  525. char* needle = strstr(haystack, "\r\n\r\n");
  526. if (needle == NULL)
  527. {
  528. free(content);
  529. error(500);
  530. return;
  531. }
  532.  
  533. // extract headers
  534. char headers[needle + 2 - haystack + 1];
  535. strncpy(headers, content, needle + 2 - haystack);
  536. headers[needle + 2 - haystack] = '\0';
  537.  
  538. // respond with interpreter's content
  539. respond(200, headers, needle + 4, length - (needle - haystack + 4));
  540.  
  541. // free interpreter's content
  542. free(content);
  543. }
  544.  
  545. /**
  546. * Responds to client with directory listing of path.
  547. */
  548. void list(const char* path)
  549. {
  550. // ensure path is readable and executable
  551. if (access(path, R_OK | X_OK) == -1)
  552. {
  553. error(403);
  554. return;
  555. }
  556.  
  557. // open directory
  558. DIR* dir = opendir(path);
  559. if (dir == NULL)
  560. {
  561. return;
  562. }
  563.  
  564. // buffer for list items
  565. char* list = malloc(1);
  566. list[0] = '\0';
  567.  
  568. // iterate over directory entries
  569. struct dirent** namelist = NULL;
  570. int n = scandir(path, &namelist, NULL, alphasort);
  571. for (int i = 0; i < n; i++)
  572. {
  573. // omit . from list
  574. if (strcmp(namelist[i]->d_name, ".") == 0)
  575. {
  576. continue;
  577. }
  578.  
  579. // escape entry's name
  580. char* name = htmlspecialchars(namelist[i]->d_name);
  581. if (name == NULL)
  582. {
  583. free(list);
  584. freedir(namelist, n);
  585. error(500);
  586. return;
  587. }
  588.  
  589. // append list item to buffer
  590. char* template = "<li><a href=\"%s\">%s</a></li>";
  591. list = realloc(list, strlen(list) + strlen(template) - 2 + strlen(name) - 2 + strlen(name) + 1);
  592. if (list == NULL)
  593. {
  594. free(name);
  595. freedir(namelist, n);
  596. error(500);
  597. return;
  598. }
  599. if (sprintf(list + strlen(list), template, name, name) < 0)
  600. {
  601. free(name);
  602. freedir(namelist, n);
  603. free(list);
  604. error(500);
  605. return;
  606. }
  607.  
  608. // free escaped name
  609. free(name);
  610. }
  611.  
  612. // free memory allocated by scandir
  613. freedir(namelist, n);
  614.  
  615. // prepare response
  616. const char* relative = path + strlen(root);
  617. char* template = "<html><head><title>%s</title></head><body><h1>%s</h1><ul>%s</ul></body></html>";
  618. char body[strlen(template) - 2 + strlen(relative) - 2 + strlen(relative) - 2 + strlen(list) + 1];
  619. int length = sprintf(body, template, relative, relative, list);
  620. if (length < 0)
  621. {
  622. free(list);
  623. closedir(dir);
  624. error(500);
  625. return;
  626. }
  627.  
  628. // free buffer
  629. free(list);
  630.  
  631. // close directory
  632. closedir(dir);
  633.  
  634. // respond with list
  635. char* headers = "Content-Type: text/html\r\n";
  636. respond(200, headers, body, length);
  637. }
  638.  
  639. /**
  640. * Loads a file into memory dynamically allocated on heap.
  641. * Stores address thereof in *content and length thereof in *length.
  642. */
  643. bool load(FILE* file, BYTE** content, size_t* length)
  644. {
  645. FILE* fptr = file;
  646. if (fptr == NULL)
  647. {
  648. return false;
  649. }
  650. FILE* fptr_cpy = fptr;
  651. // Initialize location in memory
  652. BYTE* content_ptr;
  653. // initialize message and its length
  654. *content = NULL;
  655. *length = 0;
  656. int cont_len = 0;
  657.  
  658. // read message
  659. for (int c = fgetc(fptr); c != EOF; c = fgetc(fptr))
  660. {
  661. // read length of file
  662. cont_len++;
  663. }
  664.  
  665. content_ptr = malloc(cont_len);
  666. if (content_ptr == NULL)
  667. {
  668. return false;
  669. }
  670. fseek(fptr_cpy, 0, SEEK_SET);
  671.  
  672. BYTE cont_bfr[cont_len];
  673. int i = 0;
  674. for (int c = fgetc(fptr_cpy); c != EOF; c = fgetc(fptr_cpy), i++)
  675. {
  676. cont_bfr[i] = c;
  677. }
  678.  
  679. memcpy(content_ptr, cont_bfr, cont_len);
  680. *content = content_ptr;
  681. *length = cont_len;
  682. if(*content != 0 && *length != 0)
  683. {
  684. return true;
  685. }
  686. return false;
  687. }
  688.  
  689. /**
  690. * Returns MIME type for supported extensions, else NULL.
  691. */
  692. const char* lookup(const char* path)
  693. {
  694. // TODO
  695. const char* temp_path = path;
  696. // int period = (int)'.';
  697. char * period_ptr = NULL;
  698. //Check to see if the string has a period. If not, kick it out
  699. if (strrchr(temp_path, '.') == 0)
  700. {
  701. printf("Invalid path look-up");
  702. return NULL;
  703. }
  704. // if it does, set the pointer to the location of the last period
  705. else
  706. {
  707. period_ptr = strrchr(temp_path, '.');
  708. }
  709. switch(tolower(*(period_ptr + 1)))
  710. {
  711. case 'j':
  712. {
  713. switch(tolower(*(period_ptr + 2)))
  714. {
  715. case 'p':
  716. {
  717. if (tolower(*(period_ptr + 3)) == 'g')
  718. {
  719. char * lkp_type = "image/jpeg";
  720. return lkp_type;
  721. }
  722. else
  723. return NULL;
  724. }
  725. case 's':
  726. {
  727. if (tolower(*(period_ptr + 3)) != '\0')
  728. return NULL;
  729. else
  730. {
  731. char * lkp_type = "text/javascript";
  732. return lkp_type;
  733. }
  734. }
  735. }
  736.  
  737. }
  738. case 'p':
  739. {
  740. switch(tolower(*(period_ptr + 2)))
  741. {
  742. case 'n':
  743. {
  744. if (tolower(*(period_ptr + 3)) == 'g')
  745. {
  746. char * lkp_type = "image/png";
  747. return lkp_type;
  748. }
  749. else
  750. return NULL;
  751. }
  752. case 'h':
  753. {
  754. if (tolower(*(period_ptr + 3)) != 'p')
  755. return NULL;
  756. else
  757. {
  758. char * lkp_type = "text/x-php";
  759. return lkp_type;
  760. }
  761. }
  762. }
  763. }
  764. case 'h':
  765. {
  766. if (tolower(*(period_ptr + 2)) == 't' && tolower(*(period_ptr +3)) == 'm' && tolower(*(period_ptr + 4)) =='l')
  767. {
  768. char * lkp_type = "text/html";
  769. return lkp_type;
  770. }
  771. else
  772. return NULL;
  773. }
  774. case 'c':
  775. {
  776. if (tolower(*(period_ptr + 2)) == 's' && tolower(*(period_ptr +3)) == 's')
  777. {
  778. char * lkp_type = "text/css";
  779. return lkp_type;
  780. }
  781. else
  782. return NULL;
  783. }
  784. case 'g':
  785. {
  786. if (tolower(*(period_ptr + 2)) == 'i' && tolower(*(period_ptr +3)) == 'f')
  787. {
  788. char * lkp_type = "image/gif";
  789. return lkp_type;
  790. }
  791. else
  792. return NULL;
  793. }
  794. case 'i':
  795. {
  796. if (tolower(*(period_ptr + 2)) == 'c' && tolower(*(period_ptr +3)) == 'o')
  797. {
  798. char * lkp_type = "image/x-icon";
  799. return lkp_type;
  800. }
  801. else
  802. return NULL;
  803. }
  804. default:
  805. printf("invalid path passed");
  806. return NULL;
  807. }
  808. }
  809.  
  810. /**
  811. * Parses a request-line, storing its absolute-path at abs_path
  812. * and its query string at query, both of which are assumed
  813. * to be at least of length LimitRequestLine + 1.
  814. */
  815. bool parse(const char* line, char* abs_path, char* query)
  816. {
  817. // make sure there is a valid string in the 'line'
  818. int parse_len = strlen(line);
  819. if (strlen(line) <= 0)
  820. {
  821. error(400);
  822. return false;
  823. }
  824.  
  825. // make sure there is a CLRF in the line;
  826. char* clrf = strstr(line, "\r\n");
  827. if (clrf == NULL)
  828. {
  829. error(400);
  830. return false;
  831. }
  832.  
  833. // Make sure there are 2 spaces in the line
  834. int spaces = 0;
  835. for (int i = 0; i < parse_len; i++)
  836. {
  837. if (line[i] == ' ')
  838. {
  839. spaces++;
  840. }
  841. }
  842. //Check if there is a quote
  843. if(strchr(line, '\"') != NULL)
  844. // using single character quotes here
  845. {
  846. error(400);
  847. return false;
  848. }
  849. //Check for the correct number of spaces
  850. if (spaces != 2)
  851. {
  852. error(405);
  853. return false;
  854. }
  855.  
  856. char method[6];
  857. char * get_method = "GET ";
  858. int i = 0;
  859. do
  860. {
  861. method[i] = *(line + i);
  862. i++;
  863. }
  864. while(method[i] != ' ' && i < 6);
  865. method[i+1] = '\0';
  866.  
  867. if(strncmp(method, get_method, 4) != false)
  868. {
  869. error(405);
  870. return false;
  871. }
  872.  
  873. if (strchr(line, '/') == NULL)
  874. {
  875. error(501);
  876. return false;
  877. }
  878.  
  879. char* target_ptr = strchr(line, '/');
  880.  
  881.  
  882. if (*(target_ptr -1) != ' ')
  883. {
  884. error(501);
  885. return false;
  886. }
  887.  
  888. // Check for HTTP Version
  889. if(strstr(line, "HTTP") == NULL)
  890. {
  891. error(400);
  892. return false;
  893. }
  894. else
  895. {
  896. // If there's an HTTP version, make sure it's the right one
  897. char* http_ver = strstr(line, "HTTP");
  898. if (*(http_ver + 7) != '1')
  899. {
  900. error(505);
  901. return false;
  902. }
  903.  
  904. /*
  905. char* accepted_ver = "HTTP/1.1";
  906. char version[9];
  907. version[8] = '\0';
  908. for (int j = 0; j < 8; j++)
  909. {
  910. // check for segfaults?
  911. version[j] = *(http_ver + j);
  912. }
  913. if(accepted_ver[7] != version[7])
  914. {
  915. error(505);
  916. return false;
  917. }
  918. */
  919. }
  920.  
  921. //count the size of the request line
  922. int tgt_size = 0;
  923. // Copy using pointers to the absolute path limit
  924. while (*(target_ptr + tgt_size) != ' ' && *(target_ptr + tgt_size) != '?' && tgt_size < LimitRequestLine)
  925. {
  926. *(abs_path + tgt_size) = tolower(*(target_ptr + tgt_size));
  927. tgt_size++;
  928. }
  929. //null terminate abs path
  930. *(abs_path + tgt_size + 1 ) = '\0';
  931.  
  932. if (strchr(line, '?') == NULL)
  933. // look for a query; if there is none - set it equal to null terminator as spec'ed
  934. {
  935. *query = '\0';
  936. return true;
  937.  
  938. }
  939. char* query_ptr = strchr(line, '?');
  940. // otherwise, you should have ? and a valid pointer to it with strchr
  941. if (query_ptr == NULL)
  942. {
  943. // make sure you get a valid pointer;
  944. error(501);
  945. return false;
  946.  
  947. }
  948.  
  949. else if (*(query_ptr + 1) == ' ')
  950. // check if there is nothing after the question mark - if so, again query is ""
  951. {
  952. *query = '\0';
  953. }
  954. else
  955. // otherwise, start from 1 after the question mark and copy the lowercase query into the index and null terminate
  956. {
  957. query_ptr++;
  958. int query_idx = 0;
  959. while(*(query_ptr + query_idx) != ' ')
  960. {
  961. *(query + query_idx) = *(query_ptr + query_idx);
  962. query_idx++;
  963. }
  964. *(query + query_idx) = '\0';
  965. return true;
  966. }
  967.  
  968. error(501);
  969. return false;
  970. }
  971.  
  972. /**
  973. * Returns status code's reason phrase.
  974. *
  975. * http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
  976. * https://tools.ietf.org/html/rfc2324
  977. */
  978. const char* reason(unsigned short code)
  979. {
  980. switch (code)
  981. {
  982. case 200: return "OK";
  983. case 301: return "Moved Permanently";
  984. case 400: return "Bad Request";
  985. case 403: return "Forbidden";
  986. case 404: return "Not Found";
  987. case 405: return "Method Not Allowed";
  988. case 414: return "Request-URI Too Long";
  989. case 418: return "I'm a teapot";
  990. case 500: return "Internal Server Error";
  991. case 501: return "Not Implemented";
  992. case 505: return "HTTP Version Not Supported";
  993. default: return NULL;
  994. }
  995. }
  996.  
  997. /**
  998. * Redirects client to uri.
  999. */
  1000. void redirect(const char* uri)
  1001. {
  1002. char* template = "Location: %s\r\n";
  1003. char headers[strlen(template) - 2 + strlen(uri) + 1];
  1004. if (sprintf(headers, template, uri) < 0)
  1005. {
  1006. error(500);
  1007. return;
  1008. }
  1009. respond(301, headers, NULL, 0);
  1010. }
  1011.  
  1012. /**
  1013. * Reads (without blocking) an HTTP request's headers into memory dynamically allocated on heap.
  1014. * Stores address thereof in *message and length thereof in *length.
  1015. */
  1016. bool request(char** message, size_t* length)
  1017. {
  1018. // ensure socket is open
  1019. if (cfd == -1)
  1020. {
  1021. return false;
  1022. }
  1023.  
  1024. // initialize message and its length
  1025. *message = NULL;
  1026. *length = 0;
  1027.  
  1028. // read message
  1029. while (*length < LimitRequestLine + LimitRequestFields * LimitRequestFieldSize + 4)
  1030. {
  1031. // read from socket
  1032. BYTE buffer[BYTES];
  1033. ssize_t bytes = read(cfd, buffer, BYTES);
  1034. if (bytes < 0)
  1035. {
  1036. if (*message != NULL)
  1037. {
  1038. free(*message);
  1039. *message = NULL;
  1040. }
  1041. *length = 0;
  1042. break;
  1043. }
  1044.  
  1045. // append bytes to message
  1046. *message = realloc(*message, *length + bytes + 1);
  1047. if (*message == NULL)
  1048. {
  1049. *length = 0;
  1050. break;
  1051. }
  1052. memcpy(*message + *length, buffer, bytes);
  1053. *length += bytes;
  1054.  
  1055. // null-terminate message thus far
  1056. *(*message + *length) = '\0';
  1057.  
  1058. // search for CRLF CRLF
  1059. int offset = (*length - bytes < 3) ? *length - bytes : 3;
  1060. char* haystack = *message + *length - bytes - offset;
  1061. char* needle = strstr(haystack, "\r\n\r\n");
  1062. if (needle != NULL)
  1063. {
  1064. // trim to one CRLF and null-terminate
  1065. *length = needle - *message + 2;
  1066. *message = realloc(*message, *length + 1);
  1067. if (*message == NULL)
  1068. {
  1069. break;
  1070. }
  1071. *(*message + *length) = '\0';
  1072.  
  1073. // ensure request-line is no longer than LimitRequestLine
  1074. haystack = *message;
  1075. needle = strstr(haystack, "\r\n");
  1076. if (needle == NULL || (needle - haystack + 2) > LimitRequestLine)
  1077. {
  1078. break;
  1079. }
  1080.  
  1081. // count fields in message
  1082. int fields = 0;
  1083. haystack = needle + 2;
  1084. while (*haystack != '\0')
  1085. {
  1086. // look for CRLF
  1087. needle = strstr(haystack, "\r\n");
  1088. if (needle == NULL)
  1089. {
  1090. break;
  1091. }
  1092.  
  1093. // ensure field is no longer than LimitRequestFieldSize
  1094. if (needle - haystack + 2 > LimitRequestFieldSize)
  1095. {
  1096. break;
  1097. }
  1098.  
  1099. // look beyond CRLF
  1100. haystack = needle + 2;
  1101. }
  1102.  
  1103. // if we didn't get to end of message, we must have erred
  1104. if (*haystack != '\0')
  1105. {
  1106. break;
  1107. }
  1108. // ensure message has no more than LimitRequestFields
  1109. if (fields > LimitRequestFields)
  1110. {
  1111. break;
  1112. }
  1113.  
  1114. // valid
  1115. return true;
  1116. }
  1117. }
  1118.  
  1119. // invalid
  1120. if (*message != NULL)
  1121. {
  1122. free(*message);
  1123. }
  1124. *message = NULL;
  1125. *length = 0;
  1126. return false;
  1127. }
  1128.  
  1129. /**
  1130. * Responds to a client with status code, headers, and body of specified length.
  1131. */
  1132. void respond(int code, const char* headers, const char* body, size_t length)
  1133. {
  1134. // determine Status-Line's phrase
  1135. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
  1136. const char* phrase = reason(code);
  1137. if (phrase == NULL)
  1138. {
  1139. return;
  1140. }
  1141.  
  1142. // respond with Status-Line
  1143. if (dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)
  1144. {
  1145. return;
  1146. }
  1147.  
  1148. // respond with headers
  1149. if (dprintf(cfd, "%s", headers) < 0)
  1150. {
  1151. return;
  1152. }
  1153.  
  1154. // respond with CRLF
  1155. if (dprintf(cfd, "\r\n") < 0)
  1156. {
  1157. return;
  1158. }
  1159.  
  1160. // respond with body
  1161. if (write(cfd, body, length) == -1)
  1162. {
  1163. return;
  1164. }
  1165.  
  1166. // log response line
  1167. if (code == 200)
  1168. {
  1169. // green
  1170. printf("\033[32m");
  1171. }
  1172. else
  1173. {
  1174. // red
  1175. printf("\033[33m");
  1176. }
  1177. printf("HTTP/1.1 %i %s", code, phrase);
  1178. printf("\033[39m\n");
  1179. }
  1180.  
  1181. /**
  1182. * Starts server on specified port rooted at path.
  1183. */
  1184. void start(short port, const char* path)
  1185. {
  1186. // path to server's root
  1187. root = realpath(path, NULL);
  1188. if (root == NULL)
  1189. {
  1190. stop();
  1191. }
  1192.  
  1193. // ensure root is executable
  1194. if (access(root, X_OK) == -1)
  1195. {
  1196. stop();
  1197. }
  1198.  
  1199. // announce root
  1200. printf("\033[33m");
  1201. printf("Using %s for server's root", root);
  1202. printf("\033[39m\n");
  1203.  
  1204. // create a socket
  1205. sfd = socket(AF_INET, SOCK_STREAM, 0);
  1206. if (sfd == -1)
  1207. {
  1208. stop();
  1209. }
  1210.  
  1211. // allow reuse of address (to avoid "Address already in use")
  1212. int optval = 1;
  1213. setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  1214.  
  1215. // assign name to socket
  1216. struct sockaddr_in serv_addr;
  1217. memset(&serv_addr, 0, sizeof(serv_addr));
  1218. serv_addr.sin_family = AF_INET;
  1219. serv_addr.sin_port = htons(port);
  1220. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  1221. if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
  1222. {
  1223. printf("\033[33m");
  1224. printf("Port %i already in use", port);
  1225. printf("\033[39m\n");
  1226. stop();
  1227. }
  1228.  
  1229. // listen for connections
  1230. if (listen(sfd, SOMAXCONN) == -1)
  1231. {
  1232. stop();
  1233. }
  1234.  
  1235. // announce port in use
  1236. struct sockaddr_in addr;
  1237. socklen_t addrlen = sizeof(addr);
  1238. if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)
  1239. {
  1240. stop();
  1241. }
  1242. printf("\033[33m");
  1243. printf("Listening on port %i", ntohs(addr.sin_port));
  1244. printf("\033[39m\n");
  1245. }
  1246.  
  1247. /**
  1248. * Stop server, deallocating any resources.
  1249. */
  1250. void stop(void)
  1251. {
  1252. // preserve errno across this function's library calls
  1253. int errsv = errno;
  1254.  
  1255. // announce stop
  1256. printf("\033[33m");
  1257. printf("Stopping server\n");
  1258. printf("\033[39m");
  1259.  
  1260. // free root, which was allocated by realpath
  1261. if (root != NULL)
  1262. {
  1263. free(root);
  1264. }
  1265.  
  1266. // close server socket
  1267. if (sfd != -1)
  1268. {
  1269. close(sfd);
  1270. }
  1271.  
  1272. // stop server
  1273. exit(errsv);
  1274. }
  1275.  
  1276. /**
  1277. * Transfers file at path with specified type to client.
  1278. */
  1279. void transfer(const char* path, const char* type)
  1280. {
  1281. // ensure path is readable
  1282. if (access(path, R_OK) == -1)
  1283. {
  1284. error(403);
  1285. return;
  1286. }
  1287.  
  1288. // open file
  1289. FILE* file = fopen(path, "r");
  1290. if (file == NULL)
  1291. {
  1292. error(500);
  1293. return;
  1294. }
  1295.  
  1296. // load file's content
  1297. BYTE* content;
  1298. size_t length;
  1299. if (load(file, &content, &length) == false)
  1300. {
  1301. error(500);
  1302. return;
  1303. }
  1304.  
  1305. // close file
  1306. fclose(file);
  1307.  
  1308. // prepare response
  1309. char* template = "Content-Type: %s\r\n";
  1310. char headers[strlen(template) - 2 + strlen(type) + 1];
  1311. if (sprintf(headers, template, type) < 0)
  1312. {
  1313. error(500);
  1314. return;
  1315. }
  1316.  
  1317. // respond with file's content
  1318. respond(200, headers, content, length);
  1319.  
  1320. // free file's content
  1321. free(content);
  1322. }
  1323.  
  1324. /**
  1325. * URL-decodes string, returning dynamically allocated memory for decoded string
  1326. * that must be deallocated by caller.
  1327. */
  1328. char* urldecode(const char* s)
  1329. {
  1330. // check whether s is NULL
  1331. if (s == NULL)
  1332. {
  1333. return NULL;
  1334. }
  1335.  
  1336. // allocate enough (zeroed) memory for an undecoded copy of s
  1337. char* t = calloc(strlen(s) + 1, 1);
  1338. if (t == NULL)
  1339. {
  1340. return NULL;
  1341. }
  1342.  
  1343. // iterate over characters in s, decoding percent-encoded octets, per
  1344. // https://www.ietf.org/rfc/rfc3986.txt
  1345. for (int i = 0, j = 0, n = strlen(s); i < n; i++, j++)
  1346. {
  1347. if (s[i] == '%' && i < n - 2)
  1348. {
  1349. char octet[3];
  1350. octet[0] = s[i + 1];
  1351. octet[1] = s[i + 2];
  1352. octet[2] = '\0';
  1353. t[j] = (char) strtol(octet, NULL, 16);
  1354. i += 2;
  1355. }
  1356. else if (s[i] == '+')
  1357. {
  1358. t[j] = ' ';
  1359. }
  1360. else
  1361. {
  1362. t[j] = s[i];
  1363. }
  1364. }
  1365.  
  1366. // escaped string
  1367. return t;
  1368. }
RAW Paste Data