From 781f267bbe50e7f4fb5300d394190edb8f1c7981 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Sun, 27 Jul 2025 14:50:54 +0200 Subject: [PATCH] Permit use of AF_VSOCK using a %vsock hostname postfix Optionally, one may give the CID to connect to or listen on before the %vsock. This works both for listening and connecting and also for "TCP" forwarding. --- configure | 8 ++++ configure.ac | 1 + src/config.h.in | 3 ++ src/includes.h | 4 ++ src/netio.c | 106 ++++++++++++++++++++++++++++++++++++++++++----- src/svr-tcpfwd.c | 3 ++ src/tcp-accept.c | 7 ++++ 7 files changed, 121 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 13c911ef5..9daae1459 100755 --- a/configure +++ b/configure @@ -6444,6 +6444,14 @@ then : fi +ac_fn_c_check_header_compile "$LINENO" "linux/vm_sockets.h" "ac_cv_header_linux_vm_sockets_h" "#include +" +if test "x$ac_cv_header_linux_vm_sockets_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_VM_SOCKETS_H 1" >>confdefs.h + +fi + # Checks for typedefs, structures, and compiler characteristics. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 diff --git a/configure.ac b/configure.ac index 674fd4d8d..f67d6dec8 100644 --- a/configure.ac +++ b/configure.ac @@ -393,6 +393,7 @@ AC_CHECK_HEADERS([netinet/in.h netinet/tcp.h \ utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h \ pam/pam_appl.h netinet/in_systm.h sys/uio.h linux/pkt_sched.h \ sys/random.h sys/prctl.h]) +AC_CHECK_HEADERS([linux/vm_sockets.h], , , [#include ]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/src/config.h.in b/src/config.h.in index 0590e0c6d..b193f4087 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -174,6 +174,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_PKT_SCHED_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_VM_SOCKETS_H + /* Have login() function */ #undef HAVE_LOGIN diff --git a/src/includes.h b/src/includes.h index 9f386247b..d872be5a9 100644 --- a/src/includes.h +++ b/src/includes.h @@ -180,6 +180,10 @@ typedef u_int32_t uint32_t; #include #endif +#ifdef HAVE_LINUX_VM_SOCKETS_H +#include +#endif + #if DROPBEAR_PLUGIN #include #endif diff --git a/src/netio.c b/src/netio.c index d5aa0c688..77b69c92b 100644 --- a/src/netio.c +++ b/src/netio.c @@ -5,6 +5,63 @@ #include "debug.h" #include "runopts.h" +#ifdef HAVE_LINUX_VM_SOCKETS_H +void +dropbear_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + /* For AF_VSOCK, we allocated it ourselves, so free it here as we + * cannot be sure that the stock freeaddrinfo free's it in the same + * way as we allocated it. + */ + if (ai && ai->ai_family != AF_VSOCK) + return freeaddrinfo(ai); + + for(; ai != NULL;) { + next = ai->ai_next; + free(ai); + ai = next; + } +} + +int dropbear_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + const char *vsock = strstr(hostname, "%vsock"); + if (vsock && (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_VSOCK)) { + struct addrinfo *vsock_res; + struct sockaddr_vm *vsockaddr; + + vsock_res = calloc(1, sizeof(struct addrinfo) + sizeof(*vsockaddr)); + vsockaddr = (void *)(vsock_res + 1); + vsock_res->ai_family = AF_VSOCK; + vsock_res->ai_socktype = hints->ai_socktype; + vsock_res->ai_addr = (struct sockaddr *)vsockaddr; + vsock_res->ai_addrlen = sizeof(*vsockaddr); + vsockaddr->svm_family = AF_VSOCK; + if (vsock != hostname) + vsockaddr->svm_cid = atoi(hostname); + else + vsockaddr->svm_cid = VMADDR_CID_ANY; + vsockaddr->svm_port = atoi(servname); + + if (res) + *res = vsock_res; + else + dropbear_freeaddrinfo(vsock_res); + + return 0; + } + + return getaddrinfo(hostname, servname, hints, res); +} +#else +#define dropbear_freeaddrinfo freeaddrinfo +#define dropbear_getaddrinfo getaddrinfo +#endif + + struct dropbear_progress_connection { struct addrinfo *res; struct addrinfo *res_iter; @@ -92,7 +149,7 @@ static void connect_try_next(struct dropbear_progress_connection *c) { hints.ai_family = r->ai_family; hints.ai_flags = AI_PASSIVE; - err = getaddrinfo(c->bind_address, c->bind_port, &hints, &bindaddr); + err = dropbear_getaddrinfo(c->bind_address, c->bind_port, &hints, &bindaddr); if (err) { int len = 100 + strlen(gai_strerror(err)); m_free(c->errstring); @@ -127,7 +184,8 @@ static void connect_try_next(struct dropbear_progress_connection *c) { setnonblocking(c->sock); #if DROPBEAR_CLIENT_TCP_FAST_OPEN - fastopen = (c->writequeue != NULL && r->ai_family != AF_UNIX); + fastopen = (c->writequeue != NULL && + (r->ai_family == AF_INET || r->ai_family == AF_INET6); if (fastopen) { memset(&message, 0x0, sizeof(message)); @@ -214,7 +272,7 @@ struct dropbear_progress_connection *connect_remote(const char* remotehost, cons hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; - err = getaddrinfo(remotehost, remoteport, &hints, &c->res); + err = dropbear_getaddrinfo(remotehost, remoteport, &hints, &c->res); if (err) { int len; len = 100 + strlen(gai_strerror(err)); @@ -507,6 +565,11 @@ int get_sock_port(int sock) { return 0; } +#ifdef HAVE_LINUX_VM_SOCKETS_H + if (from.ss_family == AF_VSOCK) + return ((struct sockaddr_vm *)&from)->svm_port; +#endif + /* Work around Linux IPv6 weirdness */ if (from.ss_family == AF_INET6) fromlen = sizeof(struct sockaddr_in6); @@ -537,7 +600,6 @@ int dropbear_listen(const char* address, const char* port, unsigned int nsock; int val; int sock; - uint16_t *allocated_lport_p = NULL; int allocated_lport = 0; TRACE(("enter dropbear_listen")) @@ -569,7 +631,8 @@ int dropbear_listen(const char* address, const char* port, } hints.ai_flags = AI_PASSIVE; } - err = getaddrinfo(address, port, &hints, &res0); + + err = dropbear_getaddrinfo(address, port, &hints, &res0); if (err) { if (errstring != NULL && *errstring == NULL) { @@ -579,7 +642,7 @@ int dropbear_listen(const char* address, const char* port, snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); } if (res0) { - freeaddrinfo(res0); + dropbear_freeaddrinfo(res0); res0 = NULL; } TRACE(("leave dropbear_listen: failed resolving")) @@ -597,11 +660,17 @@ int dropbear_listen(const char* address, const char* port, 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; + ((struct sockaddr_in *)res->ai_addr)->sin_port = + htons(allocated_lport); } else if (AF_INET6 == res->ai_family) { - allocated_lport_p = &((struct sockaddr_in6 *)res->ai_addr)->sin6_port; + ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = + htons(allocated_lport); +#ifdef HAVE_LINUX_VM_SOCKETS_H + } else if (AF_VSOCK == res->ai_family) { + ((struct sockaddr_vm *)res->ai_addr)->svm_port = + allocated_lport; +#endif } - *allocated_lport_p = htons(allocated_lport); } /* Get a socket */ @@ -659,7 +728,7 @@ int dropbear_listen(const char* address, const char* port, } if (res0) { - freeaddrinfo(res0); + dropbear_freeaddrinfo(res0); res0 = NULL; } @@ -722,7 +791,22 @@ void getaddrstring(struct sockaddr_storage* addr, #if !DO_HOST_LOOKUP host_lookup = 0; #endif - + +#ifdef HAVE_LINUX_VM_SOCKETS_H + if (addr->ss_family == AF_VSOCK) { + struct sockaddr_vm *vsockaddr = (void *)addr; + + snprintf(host, NI_MAXHOST, "%u%%vsock", vsockaddr->svm_cid); + if (ret_host) + *ret_host = m_strdup(host); + snprintf(serv, NI_MAXSERV, "%u", vsockaddr->svm_port); + if (ret_port) + *ret_port = m_strdup(serv); + + return; + } +#endif + if (host_lookup) { flags = NI_NUMERICSERV; } diff --git a/src/svr-tcpfwd.c b/src/svr-tcpfwd.c index e6902ea99..bbf18b303 100644 --- a/src/svr-tcpfwd.c +++ b/src/svr-tcpfwd.c @@ -286,6 +286,9 @@ static int newtcpdirect(struct Channel * channel) { origport = buf_getint(ses.payload); /* best be sure */ +#ifdef HAVE_LINUX_VM_SOCKETS_H + if (strstr(orighost, "%vsock") != NULL) {} else +#endif if (origport > 65535 || destport > 65535) { TRACE(("leave newtcpdirect: port > 65535")) goto out; diff --git a/src/tcp-accept.c b/src/tcp-accept.c index 5998236b9..768d2aaa9 100644 --- a/src/tcp-accept.c +++ b/src/tcp-accept.c @@ -60,6 +60,13 @@ static void tcp_acceptor(const struct Listener *listener, int sock) { return; } +#ifdef HAVE_LINUX_VM_SOCKETS_H + if (sa.ss_family == AF_VSOCK) { + struct sockaddr_vm *vsockaddr = (void *)&sa; + snprintf(ipstring, NI_MAXHOST - 1, "%u%%vsock", vsockaddr->svm_cid); + snprintf(portstring, NI_MAXSERV - 1, "%u", vsockaddr->svm_port); + } else +#endif if (getnameinfo((struct sockaddr*)&sa, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {