about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bounce.c11
-rw-r--r--bounce.h2
-rw-r--r--pounce.111
-rw-r--r--state.c28
4 files changed, 31 insertions, 21 deletions
diff --git a/bounce.c b/bounce.c
index 1dd9138..8ed4234 100644
--- a/bounce.c
+++ b/bounce.c
@@ -285,8 +285,8 @@ int main(int argc, char *argv[]) {
 	const char *host = NULL;
 	const char *port = "6697";
 	char *pass = NULL;
-	bool sasl = false;
 	char *plain = NULL;
+	enum Cap blindReq = 0;
 	const char *nick = NULL;
 	const char *user = NULL;
 	const char *real = NULL;
@@ -303,6 +303,7 @@ int main(int argc, char *argv[]) {
 		{ .val = 'N', .name = "no-names", no_argument },
 		{ .val = 'P', .name = "local-port", required_argument },
 		{ .val = 'Q', .name = "queue-interval", required_argument },
+		{ .val = 'R', .name = "blind-req", required_argument },
 		{ .val = 'S', .name = "bind", required_argument },
 		{ .val = 'T', .name = "no-sts", no_argument },
 		{ .val = 'U', .name = "local-path", required_argument },
@@ -353,13 +354,14 @@ int main(int argc, char *argv[]) {
 			break; case 'N': stateNoNames = true;
 			break; case 'P': bindPort = optarg;
 			break; case 'Q': serverQueueInterval = parseInterval(optarg);
+			break; case 'R': blindReq |= capParse(optarg, NULL);
 			break; case 'S': serverBindHost = optarg;
 			break; case 'T': clientSTS = false;
 			break; case 'U': strlcpy(bindPath, optarg, sizeof(bindPath));
 			break; case 'W': clientPass = optarg;
-			break; case 'a': sasl = true; plain = optarg;
+			break; case 'a': blindReq |= CapSASL; plain = optarg;
 			break; case 'c': clientCert = optarg;
-			break; case 'e': sasl = true;
+			break; case 'e': blindReq |= CapSASL;
 			break; case 'f': savePath = optarg;
 			break; case 'g': genPath = optarg;
 			break; case 'h': host = optarg;
@@ -378,6 +380,7 @@ int main(int argc, char *argv[]) {
 			break; default:  return EX_USAGE;
 		}
 	}
+	if (blindReq & CapUnsupported) errx(EX_CONFIG, "unsupported capability");
 	if (genPath) genCert(genPath, caPath);
 
 	if (bindPath[0]) {
@@ -460,7 +463,7 @@ int main(int argc, char *argv[]) {
 	capLimit(server, &sockRights);
 #endif
 
-	stateLogin(pass, sasl, plain, nick, user, real);
+	stateLogin(pass, blindReq, plain, nick, user, real);
 	if (pass) explicit_bzero(pass, strlen(pass));
 	if (plain) explicit_bzero(plain, strlen(plain));
 
diff --git a/bounce.h b/bounce.h
index 39d1e1f..a58eca1 100644
--- a/bounce.h
+++ b/bounce.h
@@ -198,7 +198,7 @@ void clientConsume(struct Client *client);
 extern bool stateNoNames;
 extern enum Cap stateCaps;
 void stateLogin(
-	const char *pass, bool sasl, const char *plain,
+	const char *pass, enum Cap blind, const char *plain,
 	const char *nick, const char *user, const char *real
 );
 bool stateReady(void);
diff --git a/pounce.1 b/pounce.1
index cf0e10b..6190d6d 100644
--- a/pounce.1
+++ b/pounce.1
@@ -1,4 +1,4 @@
-.Dd May 19, 2020
+.Dd July  6, 2020
 .Dt POUNCE 1
 .Os
 .
@@ -15,6 +15,7 @@
 .Op Fl K Ar priv
 .Op Fl P Ar port
 .Op Fl Q Ar time
+.Op Fl R Ar caps
 .Op Fl S Ar bind
 .Op Fl U Ar unix
 .Op Fl W Ar pass
@@ -160,6 +161,14 @@ Messages from clients
 are sent to the server immediately.
 The default interval is 200 milliseconds.
 .
+.It Fl R Ar caps , Cm blind-req = Ar caps
+Blindly request the IRCv3 capabilities
+.Ar caps .
+This can be used to enable hidden capabilities,
+such as
+.Sy userhost-in-names
+on freenode.
+.
 .It Fl S Ar host , Cm bind = Ar host
 Bind to source address
 .Ar host
diff --git a/state.c b/state.c
index ff05638..f35f29f 100644
--- a/state.c
+++ b/state.c
@@ -56,26 +56,24 @@ enum { AuthLen = 299 };
 static char plainBase64[BASE64_SIZE(AuthLen)];
 
 void stateLogin(
-	const char *pass, bool sasl, const char *plain,
+	const char *pass, enum Cap blind, const char *plain,
 	const char *nick, const char *user, const char *real
 ) {
 	serverFormat("CAP LS 302\r\n");
 	if (pass) serverFormat("PASS :%s\r\n", pass);
-	if (sasl) {
-		serverFormat("CAP REQ :%s\r\n", capList(CapSASL, NULL));
-		if (plain) {
-			byte buf[AuthLen];
-			size_t len = 1 + strlen(plain);
-			if (sizeof(buf) < len) {
-				errx(EX_SOFTWARE, "SASL PLAIN is too long");
-			}
-			buf[0] = 0;
-			for (size_t i = 0; plain[i]; ++i) {
-				buf[1 + i] = (plain[i] == ':' ? 0 : plain[i]);
-			}
-			base64(plainBase64, buf, len);
-			explicit_bzero(buf, sizeof(buf));
+	if (blind) serverFormat("CAP REQ :%s\r\n", capList(blind, NULL));
+	if (plain) {
+		byte buf[AuthLen];
+		size_t len = 1 + strlen(plain);
+		if (sizeof(buf) < len) {
+			errx(EX_SOFTWARE, "SASL PLAIN is too long");
 		}
+		buf[0] = 0;
+		for (size_t i = 0; plain[i]; ++i) {
+			buf[1 + i] = (plain[i] == ':' ? 0 : plain[i]);
+		}
+		base64(plainBase64, buf, len);
+		explicit_bzero(buf, sizeof(buf));
 	}
 	serverFormat("NICK %s\r\n", nick);
 	serverFormat("USER %s 0 * :%s\r\n", user, real);