Send srvroot listing for empty selector
This commit is contained in:
parent
fa0dbed44c
commit
5c03ed950f
138
main.c
138
main.c
@ -16,17 +16,33 @@
|
|||||||
* <https://www.gnu.org/licenses/>.
|
* <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define HOST "::1"
|
||||||
|
#define PORT 7070
|
||||||
|
|
||||||
|
#define PBUF_SIZE 1024
|
||||||
|
#define RBUF_SIZE 1024
|
||||||
#define SBUF_SIZE 1024
|
#define SBUF_SIZE 1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumeration of directory entry types.
|
||||||
|
*
|
||||||
|
* The numeric values are significant here (hence them being set
|
||||||
|
* explicitly): they match those defined in RFC 1436.
|
||||||
|
*/
|
||||||
|
enum etype { ETYPE_FILE = 0, ETYPE_DIR = 1 };
|
||||||
|
|
||||||
static bool exit_requested = false;
|
static bool exit_requested = false;
|
||||||
|
|
||||||
void handle_exit_signal(int signum)
|
void handle_exit_signal(int signum)
|
||||||
@ -35,12 +51,40 @@ void handle_exit_signal(int signum)
|
|||||||
exit_requested = true;
|
exit_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int retrying_write(int fd, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
do {
|
||||||
|
errno = 0;
|
||||||
|
n = write(fd, buf, len);
|
||||||
|
} while (errno == EINTR);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
||||||
int res = EXIT_SUCCESS;
|
int res = EXIT_SUCCESS;
|
||||||
|
static char pbuf[PBUF_SIZE], rbuf[RBUF_SIZE], sbuf[SBUF_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get srvroot path from arguments and copy into pbuf.
|
||||||
|
*
|
||||||
|
* The srvroot being at the start of pbuf should be maintained
|
||||||
|
* through the whole application.
|
||||||
|
*/
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s srvroot\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
const size_t srvroot_len = strlen(argv[1]);
|
||||||
|
if (srvroot_len > PBUF_SIZE) {
|
||||||
|
fprintf(stderr, "srvroot path is too long\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
memcpy(pbuf, argv[1], srvroot_len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register signal handler for SIGTERM and SIGINT.
|
* Register signal handler for SIGTERM and SIGINT.
|
||||||
@ -72,7 +116,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
const struct sockaddr_in6 haddr = {
|
const struct sockaddr_in6 haddr = {
|
||||||
.sin6_family = AF_INET6,
|
.sin6_family = AF_INET6,
|
||||||
.sin6_port = htons(7070),
|
.sin6_port = htons(PORT),
|
||||||
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
|
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
|
||||||
};
|
};
|
||||||
if (bind(sfd, (const struct sockaddr *)&haddr, sizeof(haddr)) == -1) {
|
if (bind(sfd, (const struct sockaddr *)&haddr, sizeof(haddr)) == -1) {
|
||||||
@ -89,8 +133,8 @@ int main(int argc, char *argv[])
|
|||||||
struct sockaddr_in6 paddr;
|
struct sockaddr_in6 paddr;
|
||||||
socklen_t paddr_size = sizeof(paddr);
|
socklen_t paddr_size = sizeof(paddr);
|
||||||
int cfd;
|
int cfd;
|
||||||
char sbuf[SBUF_SIZE];
|
|
||||||
ssize_t n, slen;
|
ssize_t n, slen;
|
||||||
|
DIR *rdir;
|
||||||
while (!exit_requested) {
|
while (!exit_requested) {
|
||||||
/*
|
/*
|
||||||
* Accept incoming connection.
|
* Accept incoming connection.
|
||||||
@ -143,16 +187,90 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write an empty response to the client.
|
* Open the requested resource.
|
||||||
|
*
|
||||||
|
* For now, this is just opening srvroot.
|
||||||
*/
|
*/
|
||||||
do {
|
if (srvroot_len + 1 > PBUF_SIZE) {
|
||||||
errno = 0;
|
fprintf(stderr, "Path buffer is too small\n");
|
||||||
n = write(cfd, ".\r\n", 3);
|
|
||||||
} while (errno == EINTR);
|
|
||||||
if (n == -1) {
|
|
||||||
fprintf(stderr, "Error sending response to client\n");
|
|
||||||
goto close_client_socket;
|
goto close_client_socket;
|
||||||
}
|
}
|
||||||
|
pbuf[srvroot_len] = '\0';
|
||||||
|
do {
|
||||||
|
errno = 0;
|
||||||
|
rdir = opendir(pbuf);
|
||||||
|
} while (errno == EINTR);
|
||||||
|
if (rdir == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open %s\n", pbuf);
|
||||||
|
goto close_client_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a line for each entry in the directory to the client.
|
||||||
|
*/
|
||||||
|
struct dirent *ent;
|
||||||
|
struct stat rstat;
|
||||||
|
unsigned namelen;
|
||||||
|
while ((ent = readdir(rdir)) != NULL) {
|
||||||
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct full path to entry in pbuf.
|
||||||
|
*
|
||||||
|
* This is needed in order to pass it to stat() later. The
|
||||||
|
* path must be null-terminated.
|
||||||
|
*/
|
||||||
|
namelen = strlen(ent->d_name);
|
||||||
|
if (srvroot_len + namelen + 2 > PBUF_SIZE) {
|
||||||
|
fprintf(stderr, "Path buffer is too small\n");
|
||||||
|
goto close_client_socket;
|
||||||
|
}
|
||||||
|
pbuf[srvroot_len] = '/';
|
||||||
|
memcpy(&pbuf[srvroot_len + 1], ent->d_name, namelen);
|
||||||
|
pbuf[srvroot_len + 1 + namelen] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Identify entry type from file inode information.
|
||||||
|
*/
|
||||||
|
if (stat(pbuf, &rstat) == -1) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Failed to stat() path \"%s\", skipping entry\n", pbuf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
enum etype type;
|
||||||
|
if (S_ISREG(rstat.st_mode))
|
||||||
|
type = ETYPE_FILE;
|
||||||
|
else if (S_ISDIR(rstat.st_mode))
|
||||||
|
type = ETYPE_DIR;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Format and send response line for current entry.
|
||||||
|
*/
|
||||||
|
n = snprintf(rbuf, RBUF_SIZE, "%1u%s\t%s\t%s\t%u\r\n", type,
|
||||||
|
ent->d_name, &pbuf[srvroot_len], HOST, PORT);
|
||||||
|
if (n >= RBUF_SIZE) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Response buffer was too small, skipping entry\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (retrying_write(cfd, rbuf, n) != n) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error sending respose line to client\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (retrying_write(cfd, ".\r\n", 3) != 3) {
|
||||||
|
fprintf(stderr, "Error sending response terminator to client\n");
|
||||||
|
goto close_client_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the resource.
|
||||||
|
*/
|
||||||
|
closedir(rdir);
|
||||||
|
|
||||||
close_client_socket:
|
close_client_socket:
|
||||||
close(cfd);
|
close(cfd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user