commit - 9c56b0a78a8194849c8d3b0f3e9727407b03dda0
commit + f28f9311393eb43145c15dae01a440f1d0c9064c
blob - 741d9e7eb62711e0f234c8679bd35404494d7561
blob + c82e9e4dd6399c72e5819e976302740aa49c7db8
--- gmid.c
+++ gmid.c
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
/* large enough to hold a copy of a gemini URL and still have extra room */
#define PATHBUF 2048
-#define FILEBUF 1024
-
#define SUCCESS 20
#define NOT_FOUND 51
#define BAD_REQUEST 59
struct client {
struct tls *ctx;
- int state;
- int code;
+ int state;
+ int code;
const char *meta;
- int fd;
+ int fd;
+ void *buf, *i;
+ ssize_t len, off;
};
struct etm { /* file extension to mime */
int url_trim(char*);
void adjust_path(char*);
int path_isdir(char*);
+ssize_t filesize(int);
int start_reply(struct pollfd*, struct client*, int, const char*);
int isdir(int);
const char *path_ext(const char*);
const char *mime(const char*);
+int open_file(char*, struct pollfd*, struct client*);
void send_file(char*, struct pollfd*, struct client*);
void send_dir(char*, struct pollfd*, struct client*);
void handle(struct pollfd*, struct client*);
char *s;
size_t len;
- /* /.. -> / */
+ /* /.. -> / */
len = strlen(path);
if (len >= 3) {
if (!strcmp(&path[len-3], "/..")) {
return S_ISDIR(sb.st_mode);
}
+ssize_t
+filesize(int fd)
+{
+ ssize_t len;
+
+ if ((len = lseek(fd, 0, SEEK_END)) == -1)
+ return -1;
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ return -1;
+ return len;
+}
+
const char *
path_ext(const char *path)
{
return def;
}
-void
-send_file(char *path, struct pollfd *fds, struct client *client)
+int
+open_file(char *path, struct pollfd *fds, struct client *c)
{
char fpath[PATHBUF];
- char buf[FILEBUF];
- size_t off;
- ssize_t ret, len;
- if (client->fd == -1) {
- assert(path != NULL);
+ assert(path != NULL);
- bzero(fpath, sizeof(fpath));
+ bzero(fpath, sizeof(fpath));
- if (*path != '.')
- fpath[0] = '.';
- strlcat(fpath, path, PATHBUF);
+ if (*path != '.')
+ fpath[0] = '.';
+ strlcat(fpath, path, PATHBUF);
- if ((client->fd = openat(dirfd, fpath, O_RDONLY | O_NOFOLLOW)) == -1) {
- warn("open: %s", fpath);
- if (!start_reply(fds, client, NOT_FOUND, "not found"))
- return;
- goodbye(fds, client);
- return;
- }
+ if ((c->fd = openat(dirfd, fpath, O_RDONLY | O_NOFOLLOW)) == -1) {
+ warn("open: %s", fpath);
+ if (!start_reply(fds, c, NOT_FOUND, "not found"))
+ return 0;
+ goodbye(fds, c);
+ return 0;
+ }
- if (isdir(client->fd)) {
- warnx("%s is a directory, trying %s/index.gmi", fpath, fpath);
- close(client->fd);
- client->fd = -1;
- send_dir(fpath, fds, client);
- return;
- }
+ if (isdir(c->fd)) {
+ warnx("%s is a directory, trying %s/index.gmi", fpath, fpath);
+ close(c->fd);
+ c->fd = -1;
+ send_dir(fpath, fds, c);
+ return 0;
+ }
- if (!start_reply(fds, client, SUCCESS, mime(fpath)))
- return;
+ if ((c->len = filesize(c->fd)) == -1) {
+ warn("filesize: %s", fpath);
+ goodbye(fds, c);
+ return 0;
+ }
+
+ if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE,
+ c->fd, 0)) == MAP_FAILED) {
+ warn("mmap: %s", fpath);
+ goodbye(fds, c);
+ return 0;
}
+ c->i = c->buf;
- while (1) {
- len = read(client->fd, buf, sizeof(buf));
- if (len == -1)
- warn("read");
- if (len == 0 || len == -1) {
- goodbye(fds, client);
- return;
- }
+ return start_reply(fds, c, SUCCESS, mime(fpath));
+}
- off = 0;
- while (len > 0) {
- ret = tls_write(client->ctx, buf+off, len);
- switch (ret) {
- case -1:
- warnx("tls_write: %s", tls_error(client->ctx));
- goodbye(fds, client);
- return;
+void
+send_file(char *path, struct pollfd *fds, struct client *c)
+{
+ ssize_t ret, len;
- case TLS_WANT_POLLIN:
- case TLS_WANT_POLLOUT:
- fds->events = ret == TLS_WANT_POLLIN ? POLLIN : POLLOUT;
- if (lseek(client->fd, -1 * len, SEEK_CUR) == -1) {
- warnx("lseek");
- goodbye(fds, client);
- }
- return;
+ if (c->fd == -1) {
+ if (!open_file(path, fds, c))
+ return;
+ c->state = S_SENDING;
+ }
- default:
- off += ret;
- len -= ret;
- break;
- }
+ len = (c->buf + c->len) - c->i;
+
+ while (len > 0) {
+ switch (ret = tls_write(c->ctx, c->i, len)) {
+ case -1:
+ warnx("tls_write: %s", tls_error(c->ctx));
+ goodbye(fds, c);
+ return;
+
+ case TLS_WANT_POLLIN:
+ fds->events = POLLIN;
+ return;
+
+ case TLS_WANT_POLLOUT:
+ fds->events = POLLOUT;
+ return;
+
+ default:
+ c->i += ret;
+ len -= ret;
+ break;
}
}
+
+ goodbye(fds, c);
}
void
struct sockaddr_in addr;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- err(1, "socket");
+ err(1, "socket");
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1)
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
- err(1, "bind");
+ err(1, "bind");
if (listen(sock, 16) == -1)
- err(1, "listen");
+ err(1, "listen");
return sock;
}
clients[i].state = S_OPEN;
clients[i].fd = -1;
+ clients[i].buf = MAP_FAILED;
return;
}
tls_free(c->ctx);
c->ctx = NULL;
+ if (c->buf != MAP_FAILED)
+ munmap(c->buf, c->len);
+
if (c->fd != -1)
close(c->fd);
fds[0].fd = sock;
for (;;) {
- if ((todo = poll(fds, MAX_USERS, INFTIM)) == -1)
+ if ((todo = poll(fds, MAX_USERS, INFTIM)) == -1)
err(1, "poll");
for (i = 0; i < MAX_USERS; i++) {