about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-11-16 15:04:39 -0500
committerJune McEnroe <june@causal.agency>2020-11-16 15:04:39 -0500
commit09af6dcd618701bc5994b4a146e6df34e1cf9015 (patch)
treecbc26cac234aaf730cdd169736b02f8ee58c75e3
parentSwap localAccept parameter order (diff)
downloadpounce-09af6dcd618701bc5994b4a146e6df34e1cf9015.tar.gz
pounce-09af6dcd618701bc5994b4a146e6df34e1cf9015.zip
Set client sockets non-blocking
Except for during writes. This prevents pounce getting blocked on a
client sending only a partial TLS record, for example.

Writes still need to block because pounce doesn't have a way to resume
them. (And it would do so by having a buffer, but sockets already have a
send buffer, so what would be the point of that?) I don't think it
should be a problem since outside of stateSync, writes only happen when
poll returns POLLOUT. I feel like ideally SO_SNDLOWAT would be set to
guarantee a full IRC message can always be written on POLLOUT, but since
it's actually TLS records being sent, it's not obvious what the size
would be.

I'm also making an assumption here that tls_read returning
TLS_WANT_POLLOUT is unlikely to happen, since I don't actually set
pollfd.events based on that. I'm not sure how wanting to resume a
tls_read after a POLLOUT could be cleanly handled. I'm just going to
hope that if it does happen, the regular poll loop will eventually sort
it out...
-rw-r--r--bounce.c6
-rw-r--r--bounce.h3
-rw-r--r--client.c9
3 files changed, 12 insertions, 6 deletions
diff --git a/bounce.c b/bounce.c
index b0cdca2..a96f753 100644
--- a/bounce.c
+++ b/bounce.c
@@ -481,12 +481,12 @@ int main(int argc, char *argv[]) {
 
 			if (!event.clients[i]) {
 				struct tls *tls;
-				int fd = localAccept(&tls, event.fds[i].fd);
-				if (fd < 0) {
+				int sock = localAccept(&tls, event.fds[i].fd);
+				if (sock < 0) {
 					warn("accept");
 					continue;
 				}
-				eventAdd(fd, clientAlloc(tls));
+				eventAdd(sock, clientAlloc(sock, tls));
 				continue;
 			}
 
diff --git a/bounce.h b/bounce.h
index 77e0164..5d6c4a2 100644
--- a/bounce.h
+++ b/bounce.h
@@ -193,6 +193,7 @@ enum Need {
 };
 struct Client {
 	bool error;
+	int sock;
 	struct tls *tls;
 	enum Need need;
 	enum Cap caps;
@@ -204,7 +205,7 @@ struct Client {
 extern enum Cap clientCaps;
 extern char *clientPass;
 extern char *clientAway;
-struct Client *clientAlloc(struct tls *tls);
+struct Client *clientAlloc(int sock, struct tls *tls);
 void clientFree(struct Client *client);
 void clientRecv(struct Client *client);
 void clientSend(struct Client *client, const char *ptr, size_t len);
diff --git a/client.c b/client.c
index 4327a89..6c12405 100644
--- a/client.c
+++ b/client.c
@@ -27,6 +27,7 @@
 
 #include <assert.h>
 #include <err.h>
+#include <fcntl.h>
 #include <regex.h>
 #include <stdarg.h>
 #include <stdbool.h>
@@ -47,9 +48,11 @@ char *clientAway;
 
 static size_t active;
 
-struct Client *clientAlloc(struct tls *tls) {
+struct Client *clientAlloc(int sock, struct tls *tls) {
 	struct Client *client = calloc(1, sizeof(*client));
 	if (!client) err(EX_OSERR, "calloc");
+	fcntl(sock, F_SETFL, O_NONBLOCK);
+	client->sock = sock;
 	client->tls = tls;
 	client->need = NeedHandshake | NeedNick | NeedUser;
 	if (clientPass) client->need |= NeedPass;
@@ -83,17 +86,19 @@ void clientFree(struct Client *client) {
 
 void clientSend(struct Client *client, const char *ptr, size_t len) {
 	if (verbose) fprintf(stderr, "\x1B[34m%.*s\x1B[m", (int)len, ptr);
+	fcntl(client->sock, F_SETFL, 0);
 	while (len) {
 		ssize_t ret = tls_write(client->tls, ptr, len);
 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue;
 		if (ret < 0) {
 			warnx("client tls_write: %s", tls_error(client->tls));
 			client->error = true;
-			return;
+			break;
 		}
 		ptr += ret;
 		len -= ret;
 	}
+	fcntl(client->sock, F_SETFL, O_NONBLOCK);
 }
 
 void clientFormat(struct Client *client, const char *format, ...) {