shithub: riscv

Download patch

ref: 634292c2f83c9f60440e6c56f5a380baea1d9351
parent: 7a3ceb58fc7fec964b251686dd87d4aca46d4573
author: cinap_lenrek <[email protected]>
date: Wed Apr 3 18:15:47 EDT 2019

sshnet: implement listen (port forwarding)

--- a/sys/man/4/sshnet
+++ b/sys/man/4/sshnet
@@ -45,8 +45,6 @@
 as is.
 .SH SOURCE
 .B /sys/src/cmd/sshnet.c
-.SH BUGS
-Incoming connections are not supported.
 .SH "SEE ALSO"
 .IR ssh (1),
 .IR ip (3)
--- a/sys/src/cmd/sshnet.c
+++ b/sys/src/cmd/sshnet.c
@@ -26,6 +26,7 @@
 	Qlocal,
 	Qremote,
 	Qstatus,
+	Qlisten,
 };
 
 #define PATH(type, n)		((type)|((n)<<8))
@@ -44,6 +45,8 @@
 {
 	Closed,
 	Dialing,
+	Announced,
+	Listen,
 	Established,
 	Teardown,
 	Finished,
@@ -52,6 +55,8 @@
 char *statestr[] = {
 	"Closed",
 	"Dialing",
+	"Announced",
+	"Listen",
 	"Established",
 	"Teardown",
 	"Finished",
@@ -63,8 +68,11 @@
 	int state;
 	int num;
 	int servernum;
-	char *connect;
 
+	int rport, lport;
+	char *rhost;
+	char *lhost;
+
 	int sendpkt;
 	int sendwin;
 	int recvwin;
@@ -83,6 +91,8 @@
 };
 
 enum {
+	MSG_GLOBAL_REQUEST = 80,
+
 	MSG_CHANNEL_OPEN = 90,
 	MSG_CHANNEL_OPEN_CONFIRMATION,
 	MSG_CHANNEL_OPEN_FAILURE,
@@ -119,8 +129,6 @@
 Client **client;
 char *mtpt;
 int sshfd;
-int localport;
-char localip[] = "::";
 
 int
 vpack(uchar *p, int n, char *fmt, va_list a)
@@ -301,6 +309,19 @@
 	return client[num];
 }
 
+Client*
+getlistener(char *host, int port)
+{
+	int i;
+
+	USED(host);
+	for(i = 0; i < nclient; i++){
+		if(client[i]->state == Listen && client[i]->lport == port)
+			return client[i];
+	}
+	return nil;
+}
+
 void
 adjustwin(Client *c, int len)
 {
@@ -472,6 +493,16 @@
 		c->state = Closed;
 		sendmsg(pack(nil, "bu", MSG_CHANNEL_CLOSE, c->servernum));
 		break;
+	case Announced:
+		sendmsg(pack(nil, "bsbsu", MSG_GLOBAL_REQUEST,
+			"cancel-tcpip-forward", 20,
+			0,
+			c->lhost, strlen(c->lhost),
+			c->lport));
+		/* wet floor */
+	case Listen:
+		c->state = Closed;
+		break;
 	}
 	while((m = c->mq) != nil){
 		c->mq = m->link;
@@ -514,6 +545,7 @@
 	"local",	0444,
 	"remote",	0444,
 	"status",	0444,
+	"listen",	0666,
 };
 
 static void
@@ -768,7 +800,7 @@
 ctlwrite(Req *r, Client *c)
 {
 	char *f[3], *s;
-	int nf;
+	int nf, port;
 
 	s = emalloc9p(r->ifcall.count+1);
 	r->ofcall.count = r->ifcall.count;
@@ -790,15 +822,18 @@
 		teardownclient(c);
 		respond(r, nil);
 	}else if(strcmp(f[0], "connect") == 0){
-		if(c->state != Closed)
+		if(nf != 2 || c->state != Closed)
 			goto Badarg;
-		if(nf != 2)
+		if(getfields(f[1], f, nelem(f), 0, "!") != 2)
 			goto Badarg;
-		free(c->connect);
-		c->connect = estrdup9p(f[1]);
-		nf = getfields(f[1], f, nelem(f), 0, "!");
-		if(nf != 2)
+		if((port = ndbfindport(f[1])) < 0)
 			goto Badarg;
+		free(c->lhost);
+		c->lhost = estrdup9p("::");
+		c->lport = 0;
+		free(c->rhost);
+		c->rhost = estrdup9p(f[0]);
+		c->rport = port;
 		c->recvwin = WinPackets*MaxPacket;
 		c->recvacc = 0;
 		c->state = Dialing;
@@ -807,8 +842,28 @@
 		sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN,
 			"direct-tcpip", 12,
 			c->num, c->recvwin, MaxPacket,
-			f[0], strlen(f[0]), ndbfindport(f[1]),
-			localip, strlen(localip), localport));
+			c->rhost, strlen(c->rhost), c->rport,
+			c->lhost, strlen(c->lhost), c->lport));
+	}else if(strcmp(f[0], "announce") == 0){
+		if(nf != 2 || c->state != Closed)
+			goto Badarg;
+		if(getfields(f[1], f, nelem(f), 0, "!") != 2)
+			goto Badarg;
+		if((port = ndbfindport(f[1])) < 0)
+			goto Badarg;
+		if(strcmp(f[0], "*") == 0)
+			f[0] = "";
+		free(c->lhost);
+		c->lhost = estrdup9p(f[0]);
+		c->lport = port;
+		free(c->rhost);
+		c->rhost = estrdup9p("::");
+		c->rport = 0;
+		c->state = Announced;
+		sendmsg(pack(nil, "bsbsu", MSG_GLOBAL_REQUEST,
+			"tcpip-forward", 13, 0,
+			c->lhost, strlen(c->lhost), c->lport));
+		respond(r, nil);
 	}else{
 	Badarg:
 		respond(r, "bad or inappropriate tcp control message");
@@ -844,11 +899,16 @@
 }
 
 static void
-localread(Req *r)
+localread(Req *r, Client *c)
 {
-	char buf[128];
+	char buf[128], *s;
 
-	snprint(buf, sizeof buf, "%s!%d\n", localip, localport);
+	s = c->lhost;
+	if(s == nil)
+		s = "::";
+	else if(*s == 0)
+		s = "*";
+	snprint(buf, sizeof buf, "%s!%d\n", s, c->lport);
 	readstr(r, buf);
 	respond(r, nil);
 }
@@ -856,13 +916,12 @@
 static void
 remoteread(Req *r, Client *c)
 {
-	char *s;
-	char buf[128];
+	char buf[128], *s;
 
-	s = c->connect;
+	s = c->rhost;
 	if(s == nil)
-		s = "::!0";
-	snprint(buf, sizeof buf, "%s\n", s);
+		s = "::";
+	snprint(buf, sizeof buf, "%s!%d\n", s, c->rport);
 	readstr(r, buf);
 	respond(r, nil);
 }
@@ -918,7 +977,7 @@
 		break;
 
 	case Qlocal:
-		localread(r);
+		localread(r, client[NUM(path)]);
 		break;
 
 	case Qremote:
@@ -985,6 +1044,22 @@
 		r->fid->aux = cs;
 		respond(r, nil);
 		break;
+	case Qlisten:
+		if(client[NUM(path)]->state != Announced){
+			respond(r, "not announced");
+			break;
+		}
+		n = newclient();
+		free(client[n]->lhost);
+		client[n]->lhost = estrdup9p(client[NUM(path)]->lhost);
+		client[n]->lport = client[NUM(path)]->lport;
+		r->fid->qid.path = PATH(Qctl, n);
+		r->ofcall.qid.path = r->fid->qid.path;
+		r->aux = nil;
+		client[n]->wq = r;
+		client[n]->ref++;
+		client[n]->state = Listen;
+		break;
 	case Qclone:
 		n = newclient();
 		path = PATH(Qctl, n);
@@ -1016,9 +1091,9 @@
 static void
 handlemsg(Msg *m)
 {
-	int chan, win, pkt, n;
+	int chan, win, pkt, lport, rport, n, ln, rn;
+	char *s, *lhost, *rhost;
 	Client *c;
-	char *s;
 
 	switch(m->rp[0]){
 	case MSG_CHANNEL_WINDOW_ADJUST:
@@ -1108,6 +1183,50 @@
 			hangupclient(c, s);
 		}
 		free(s);
+		break;
+	case MSG_CHANNEL_OPEN:
+		if(unpack(m, "_suuususu", &s, &n, &chan,
+			&win, &pkt,
+			&lhost, &ln, &lport,
+			&rhost, &rn, &rport) < 0)
+			break;
+		if(n != 15 || strncmp(s, "forwarded-tcpip", 15) != 0){
+			n = 3, s = "unknown open type";
+		Reject:
+			sendmsg(pack(nil, "buus", MSG_CHANNEL_OPEN_FAILURE,
+				chan, n, s, strlen(s)));
+			break;
+		}
+		lhost = smprint("%.*s", utfnlen(lhost, ln), lhost);
+		rhost = smprint("%.*s", utfnlen(rhost, rn), rhost);
+		c = getlistener(lhost, lport);
+		if(c == nil){
+			free(lhost);
+			free(rhost);
+			n = 2, s = "connection refused";
+			goto Reject;
+		}
+		free(c->lhost);
+		c->lhost = lhost;
+		c->lport = lport;
+		free(c->rhost);
+		c->rhost = rhost;
+		c->rport = rport;
+		c->servernum = chan;
+		c->recvwin = WinPackets*MaxPacket;
+		c->recvacc = 0;
+		c->eof = 0;
+		c->sendpkt = pkt;
+		c->sendwin = win;
+		c->state = Established;
+		sendmsg(pack(nil, "buuuu", MSG_CHANNEL_OPEN_CONFIRMATION,
+			c->servernum, c->num, c->recvwin, MaxPacket));
+		if(c->wq == nil){
+			teardownclient(c);
+			break;
+		}
+		respond(c->wq, nil);
+		c->wq = nil;
 		break;
 	}
 	free(m);