PY-50689: WSL proxy.

Windows firewall blocks access from WSL by default.
This proxy make reverse connection (Win --> Lin).

See README.txt for more info

GitOrigin-RevId: 2f181faa7c66aee7e766daf007c03877612597be
This commit is contained in:
Ilya.Kazakevich
2021-08-25 03:51:23 +03:00
committed by intellij-monorepo-bot
parent b8c732c2b3
commit 719ee22a13
6 changed files with 181 additions and 0 deletions

BIN
bin/win/wslproxy Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
project(wslproxy C)
set(CMAKE_C_STANDARD 11)
add_executable(wslproxy wslproxy.c)
target_compile_options(wslproxy PRIVATE -Wall -Wextra -pedantic -Werror -Os)
target_link_libraries(wslproxy pthread)

3
native/WslProxy/Makefile Normal file
View File

@@ -0,0 +1,3 @@
CPPFLAGS = -Wall -Wextra -pedantic -Werror -Os
LDLIBS = -pthread -static
wslproxy: $(SOURCES)

View File

@@ -0,0 +1,7 @@
To avoid connecting from WSL to Windows (such connections may be blocked by firewall) we connect from Windows to WSL instead.
This proxy accepts two clients: one for external (eth0) and one for local (loopback). It then passes data between them with two threads.
Client disconnection, signal or any byte written to the stdin kills process.
CMakeLists is here for CLion.
To build tool use Makefile. We link it statically because WSL may lack glibc. Kernel ABI is backward compatible, so use some old Linux

162
native/WslProxy/wslproxy.c Normal file
View File

@@ -0,0 +1,162 @@
#include <stdio.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
// Output: [linux-ip] [external-port] [internal-port]
// Connect to the external from Windows and to the internal from Linux
// Sockets are connected from that moment until byte written to the stdin or SIGINT/SIGHUP sent
#define JB_WSL_INTERFACE "eth0"
// Bind to eth0 only
static in_addr_t jb_get_wsl_public_ip() {
struct ifaddrs *ifaddrs_p_base;
if (getifaddrs(&ifaddrs_p_base) != 0) {
perror("getifaddrs failed");
exit(-1);
}
for (struct ifaddrs *ifaddrs_p = ifaddrs_p_base; ifaddrs_p != NULL; ifaddrs_p = ifaddrs_p->ifa_next) {
// Search for inteface and ipv4
if ((ifaddrs_p->ifa_addr->sa_family != AF_INET) ||
((strcmp(ifaddrs_p->ifa_name, JB_WSL_INTERFACE) != 0))) {
continue;
}
const struct sockaddr_in *in_addr = (struct sockaddr_in *) ifaddrs_p->ifa_addr;
const in_addr_t result = in_addr->sin_addr.s_addr;
freeifaddrs(ifaddrs_p_base);
return result;
}
freeifaddrs(ifaddrs_p_base);
fprintf(stderr, "No interface %s found", JB_WSL_INTERFACE);
exit(-1);
}
// Open internal or external sock, print addr if print_addr
static int jb_open_socket(const in_addr_t listen_to, const bool print_addr) {
const int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("can't open socket");
exit(-1);
}
struct sockaddr_in addr_p = {0};
addr_p.sin_family = AF_INET;
addr_p.sin_addr.s_addr = listen_to;
if (bind(sock, (struct sockaddr *) &addr_p, sizeof(struct sockaddr_in)) != 0) {
perror("can't bind to port");
exit(-1);
}
if (listen(sock, 1) != 0) {
perror("socket can't be listen");
exit(-1);
}
bzero(&addr_p, sizeof(addr_p));
socklen_t size = sizeof(addr_p);
if (getsockname(sock, (struct sockaddr *) &addr_p, &size) != 0) {
perror("getsockname failed");
exit(-1);
}
if (print_addr) {
printf("%s ", inet_ntoa(addr_p.sin_addr));
}
printf("%d ", ntohs(addr_p.sin_port));
return sock;
}
struct jb_socket_info {
int server_sock_fd; // Listening server socket
int client_sock_fd; // Connected (accepted) client
};
// public (external) socket and local (internal)
static struct jb_socket_info jb_public = {0}, jb_local = {0};
// marks socket for the next "select" call if client hasn't been connected yet
static void jb_set_select_socket(const struct jb_socket_info *sock, fd_set *sock_set, int *max_sock) {
if (sock->client_sock_fd != 0) { // Client already connected
return;
}
FD_SET(sock->server_sock_fd, sock_set); // select socket
if (sock->server_sock_fd > *max_sock) {
*max_sock = sock->server_sock_fd;
}
}
static void jb_set_client_socket(const fd_set *sock_set, struct jb_socket_info *sock) {
if (sock->client_sock_fd == 0 && FD_ISSET(sock->server_sock_fd, sock_set)) {
if ((sock->client_sock_fd = accept(sock->server_sock_fd, NULL, NULL)) < 0) {
perror("accept call failed");
exit(-1);
}
close(sock->server_sock_fd); // Client connected, close listening socket
sock->server_sock_fd = 0;
}
}
// Waits for both clients to connect
static void jb_connect_accept_clients() {
fd_set sock_set;
struct timeval time = {.tv_sec = 120, .tv_usec = 0};
const int stdin_fd = fileno(stdin);
while (jb_public.client_sock_fd == 0 || jb_local.client_sock_fd == 0) {
FD_ZERO(&sock_set);
FD_SET(stdin_fd, &sock_set);
int max_sock = stdin_fd;
jb_set_select_socket(&jb_local, &sock_set, &max_sock);
jb_set_select_socket(&jb_public, &sock_set, &max_sock);
if (select(max_sock + 1, &sock_set, NULL, NULL, &time) < 0) {
perror("select call failed");
exit(-1);
}
if (FD_ISSET(stdin_fd, &sock_set)) {
exit(0);
}
jb_set_client_socket(&sock_set, &jb_local);
jb_set_client_socket(&sock_set, &jb_public);
}
}
// Connect source (sock) and destination (another socket)
static void *jb_connect(const struct jb_socket_info *sock) {
const int source = sock->client_sock_fd;
const int dest = (sock == &jb_local ? jb_public : jb_local).client_sock_fd;
char buf[4096];
size_t bytes;
while ((bytes = read(source, buf, sizeof(buf))) > 0) {
write(dest, buf, bytes);
}
exit(0);
}
static void jb_create_thread(struct jb_socket_info *sock) {
pthread_t thread;
const int result = pthread_create(&thread, NULL, (void *(*)(void *)) &jb_connect, sock);
if (result != 0) {
fprintf(stderr, "pthread_create failed: %d", result);
exit(result);
}
}
int main() {
jb_public.server_sock_fd = jb_open_socket(jb_get_wsl_public_ip(), true);
jb_local.server_sock_fd = jb_open_socket(htonl(INADDR_LOOPBACK), false);
puts("\n");
fflush(stdout);
jb_connect_accept_clients();
jb_create_thread(&jb_public);
jb_create_thread(&jb_local);
getchar();
return 0;
}

View File

@@ -709,6 +709,7 @@ final class CommunityLibraryLicenses {
jetbrainsLibrary("intellij-coverage"),
jetbrainsLibrary("intellij-markdown"),
jetbrainsLibrary("intellij-test-discovery"),
jetbrainsLibrary("io.ktor.network.jvm"),
jetbrainsLibrary("jps-build-script-dependencies-bootstrap"),
jetbrainsLibrary("jshell-frontend"),
jetbrainsLibrary("kotlin-script-runtime"),