summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorhouseofkodai <karthik@houseofkodai.in>2018-01-25 19:51:41 +0530
committerMatt Johnston <matt@ucc.asn.au>2018-01-25 22:21:41 +0800
commit917722257d11ea7a33990c170542aeff2b6061b1 (patch)
tree0825c8163b8ec62ad32039d7e97e9960302e631a
parent3d61b6eab69a9a2453e4fb807159f28c9490312a (diff)
Server chosen tcpfwd ports (#43)
Server chosen tcpfwd ports
-rw-r--r--netio.c53
-rw-r--r--netio.h1
-rw-r--r--svr-tcpfwd.c45
3 files changed, 84 insertions, 15 deletions
diff --git a/netio.c b/netio.c
index 24bb1d2..2f37e70 100644
--- a/netio.c
+++ b/netio.c
@@ -348,6 +348,37 @@ void set_sock_priority(int sock, enum dropbear_prio prio) {
}
+/* from openssh/canohost.c avoid premature-optimization */
+int get_sock_port(int sock) {
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+ char strport[NI_MAXSERV];
+ int r;
+
+ /* Get IP address of client. */
+ fromlen = sizeof(from);
+ memset(&from, 0, sizeof(from));
+ if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
+ TRACE(("getsockname failed: %d", errno))
+ return 0;
+ }
+
+ /* Work around Linux IPv6 weirdness */
+ if (from.ss_family == AF_INET6)
+ fromlen = sizeof(struct sockaddr_in6);
+
+ /* Non-inet sockets don't have a port number. */
+ if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
+ return 0;
+
+ /* Return port number. */
+ if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
+ strport, sizeof(strport), NI_NUMERICSERV)) != 0) {
+ TRACE(("netio.c/get_sock_port/getnameinfo NI_NUMERICSERV failed: %d", r))
+ }
+ return atoi(strport);
+}
+
/* Listen on address:port.
* Special cases are address of "" listening on everything,
* and address of NULL listening on localhost only.
@@ -400,11 +431,29 @@ int dropbear_listen(const char* address, const char* port,
return -1;
}
+ /*
+ * when listening on server-assigned-port 0
+ * the assigned ports may differ for address families (v4/v6)
+ * causing problems for tcpip-forward
+ * caller can do a get_socket_address to discover assigned-port
+ * hence, use same port for all address families
+ */
+ u_int16_t *allocated_lport_p = 0;
+ int allocated_lport = 0;
nsock = 0;
for (res = res0; res != NULL && nsock < sockcount;
res = res->ai_next) {
+ if (allocated_lport > 0) {
+ if (AF_INET == res->ai_family) {
+ allocated_lport_p = &((struct sockaddr_in *)res->ai_addr)->sin_port;
+ } else if (AF_INET6 == res->ai_family) {
+ allocated_lport_p = &((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
+ }
+ *allocated_lport_p = htons(allocated_lport);
+ }
+
/* Get a socket */
socks[nsock] = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
@@ -451,6 +500,10 @@ int dropbear_listen(const char* address, const char* port,
continue;
}
+ if (0 == allocated_lport) {
+ allocated_lport = get_sock_port(sock);
+ }
+
*maxfd = MAX(*maxfd, sock);
nsock++;
diff --git a/netio.h b/netio.h
index 532157c..090df3d 100644
--- a/netio.h
+++ b/netio.h
@@ -14,6 +14,7 @@ enum dropbear_prio {
void set_sock_nodelay(int sock);
void set_sock_priority(int sock, enum dropbear_prio prio);
+int get_sock_port(int sock);
void get_socket_address(int fd, char **local_host, char **local_port,
char **remote_host, char **remote_port, int host_lookup);
void getaddrstring(struct sockaddr_storage* addr,
diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c
index faf372e..c592afb 100644
--- a/svr-tcpfwd.c
+++ b/svr-tcpfwd.c
@@ -47,7 +47,7 @@ void recv_msg_global_request_remotetcp() {
#endif /* !DROPBEAR_SVR_REMOTETCPFWD */
static int svr_cancelremotetcp(void);
-static int svr_remotetcpreq(void);
+static int svr_remotetcpreq(int *allocated_listen_port);
static int newtcpdirect(struct Channel * channel);
#if DROPBEAR_SVR_REMOTETCPFWD
@@ -86,7 +86,16 @@ void recv_msg_global_request_remotetcp() {
}
if (strcmp("tcpip-forward", reqname) == 0) {
- ret = svr_remotetcpreq();
+ int allocated_listen_port;
+ ret = svr_remotetcpreq(&allocated_listen_port);
+ /* client expects-port-number-to-make-use-of-server-allocated-ports */
+ if (DROPBEAR_SUCCESS == ret) {
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
+ buf_putint(ses.writepayload, allocated_listen_port);
+ encrypt_packet();
+ wantreply = 0; //so out does not do so
+ }
} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
ret = svr_cancelremotetcp();
} else {
@@ -152,7 +161,7 @@ out:
return ret;
}
-static int svr_remotetcpreq() {
+static int svr_remotetcpreq(int *allocated_listen_port) {
int ret = DROPBEAR_FAILURE;
char * request_addr = NULL;
@@ -170,19 +179,16 @@ static int svr_remotetcpreq() {
port = buf_getint(ses.payload);
- if (port == 0) {
- dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
- goto out;
- }
-
- if (port < 1 || port > 65535) {
- TRACE(("invalid port: %d", port))
- goto out;
- }
+ if (port != 0) {
+ if (port < 1 || port > 65535) {
+ TRACE(("invalid port: %d", port))
+ goto out;
+ }
- if (!ses.allowprivport && port < IPPORT_RESERVED) {
- TRACE(("can't assign port < 1024 for non-root"))
- goto out;
+ if (!ses.allowprivport && port < IPPORT_RESERVED) {
+ TRACE(("can't assign port < 1024 for non-root"))
+ goto out;
+ }
}
tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
@@ -203,6 +209,13 @@ static int svr_remotetcpreq() {
}
ret = listen_tcpfwd(tcpinfo);
+ if (DROPBEAR_SUCCESS == ret) {
+ tcpinfo->listenport = get_sock_port(ses.listeners[0]->socks[0]);
+ *allocated_listen_port = tcpinfo->listenport;
+ dropbear_log(LOG_INFO, "tcpip-forward %s:%d '%s'",
+ ((NULL == tcpinfo->listenaddr)?"localhost":tcpinfo->listenaddr),
+ tcpinfo->listenport, ses.authstate.pw_name);
+ }
out:
if (ret == DROPBEAR_FAILURE) {
@@ -211,7 +224,9 @@ out:
m_free(request_addr);
m_free(tcpinfo);
}
+
TRACE(("leave remotetcpreq"))
+
return ret;
}