Advertisement
Guest User

Untitled

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