Handle file requests

This commit is contained in:
Camden Dixie O'Brien 2022-10-14 11:24:39 +01:00
parent 5c21a0a8e2
commit 06d92b3d25

78
main.c
View File

@ -18,6 +18,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdbool.h>
@ -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)) {