From 06d92b3d25e02ace59f62b6d8411e63cbe125507 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 14 Oct 2022 11:24:39 +0100 Subject: [PATCH] Handle file requests --- main.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index a2933d8..077ada5 100644 --- a/main.c +++ b/main.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -31,9 +32,13 @@ #define HOST "::1" #define PORT 7070 +#define RESP_TERM ".\r\n" +#define RESP_TERM_LEN 3 /* strlen(".\r\n") */ + #define PBUF_SIZE 1024 #define RBUF_SIZE 1024 #define SBUF_SIZE 1024 +#define FBUF_SIZE 1024 /* * Enumeration of directory entry types. @@ -87,7 +92,8 @@ int main(int argc, char *argv[]) (void)argv; int res = EXIT_SUCCESS; - static char pbuf[PBUF_SIZE], rbuf[RBUF_SIZE], sbuf[SBUF_SIZE]; + static char pbuf[PBUF_SIZE], rbuf[RBUF_SIZE], sbuf[SBUF_SIZE], + fbuf[FBUF_SIZE]; /* * Get srvroot path from arguments and copy into pbuf. @@ -211,7 +217,7 @@ int main(int argc, char *argv[]) /* * Construct the full path to the requested resource. * - * This is needed in order to pass it to stat() and open() or + * This is needed in order to pass it to stat() and fopen() or * opendir() later. It must be null-terminated. We also need * to make sure that none of the path's parts are "..", as * this could be used to escape the srvroot directory. @@ -254,11 +260,71 @@ int main(int argc, char *argv[]) } if (S_ISREG(rstat.st_mode)) { /* - * Send hard-coded response for files for now. + * Open the file. + * + * The path is already null-terminated from the earlier call + * to stat(). */ - const char *resp = "Files don't work yet\r\n.\r\n"; - if (retrying_write(cfd, resp, strlen(resp)) == -1) { - fprintf(stderr, "Couldn't write to client socket\n"); + FILE *rf; + do { + errno = 0; + rf = fopen(pbuf, "r"); + } while (errno == EINTR); + if (rf == NULL) { + fprintf(stderr, "Failed to open %s\n", pbuf); + goto close_client_socket; + } + + /* + * Construct response in rbuf. + * + * The response should be the file contents, but with CRLF + * line endings instead of just LF. Replacing the line + * endings is done with a buffered reading approach to + * minimize syscalls (as opposed to using fgetc()). + */ + unsigned rpos = 0; + while (true) { + /* Fill fbuf from file. */ + n = fread(fbuf, sizeof(*fbuf), FBUF_SIZE, rf); + if (n != FBUF_SIZE && ferror(rf)) { + fprintf(stderr, "Failed to read from file %s\n", pbuf); + goto close_client_socket; + } + + /* Copy from fbuf to rbuf, replacing LF with CRLF. */ + for (unsigned fpos = 0; fpos < n; ++fpos) { + if (fbuf[fpos] == '\n') { + if (RBUF_SIZE - rpos < 2) { + fprintf(stderr, "Response buffer is too small"); + goto close_client_socket; + } + rbuf[rpos++] = '\r'; + rbuf[rpos++] = '\n'; + } else { + if (RBUF_SIZE - rpos < 1) { + fprintf(stderr, "Response buffer is too small"); + goto close_client_socket; + } + rbuf[rpos++] = fbuf[fpos]; + } + } + + if (feof(rf)) + break; + } + + /* + * Terminate and send the response. + */ + if (RBUF_SIZE - rpos < RESP_TERM_LEN) { + fprintf(stderr, "Response buffer is too small"); + goto close_client_socket; + } + memcpy(&rbuf[rpos], RESP_TERM, RESP_TERM_LEN); + rpos += RESP_TERM_LEN; + if (retrying_write(cfd, rbuf, rpos) == -1) { + fprintf(stderr, "Failed to write response to client\n"); goto close_client_socket; } } else if (S_ISDIR(rstat.st_mode)) {