Viktor Dukhovni via Postfix-users wrote in <zsy46vegakuoh...@chardros.imrryr.org>: |On Mon, Aug 26, 2024 at 04:59:47PM +0000, Ren Jyan via Postfix-users wrote: |> I still want to try using a SOCKS5 proxy. | |I can try, but I personally prefer to not provide the kind of help which |I balance I consider to be harming the real interests of the person |seeking help. A socks proxy for mail would give you no end of trouble. | |Seek a hosting arrangement that preferably let's you send mail directly, |or otherwise provides an authenticated relay service and keeps a clean |reputation via effective anti-abuse controls.
There is a super minimal socks5 LD_PRELOAD thing by Gaetan Bisson (ex-ArchLinux committer, a french math professor of Tahiti). I will attach it (still current, just looked). Manual is by me and reads NAME usocks — detour network traffic through SOCKS5 proxy SYNOPSIS usocks proxy-address proxy-port command [:argument:] DESCRIPTION usocks can be used to detour stream-based network traffic of command through the specified SOCKS5 proxy. This is realized by preloading a shared library which overwrites the connect(2) system call. Because of this simple approach the SOCKS5 provided DNS (name) lookup mechanism is not used. proxy-address must be an IPv4 internet address (like ‘127.0.0.1’). For example # Login as USER on HOST, create a local SOCKS proxy on port 10000 $ ssh -D 10000 USER@HOST # Thereafter use usocks(1) to proxy any command over it $ usocks 127.0.0.1 10000 irssi It seems to me it requires a bit of fiddling to make this "be postfix". Btw, dear Wietse and Viktor, SOCKS5 proxying is pretty easy, i support it for the MUA i maintain, and a pretty similar patch was also accepted for lynx by Thomas Dickey. (He does not like jumps, and all that. But otherwise.) All in all it is hard to imagine that this can be made fit for the postfix machinery and all its bells and whistles, though. But here is it all: char const *cp; boole rv; rv = FAL0; if((cp = xok_vlook(socks_proxy, urlp, OXM_ALL)) == NULL) rv = a_netso_open(sop, urlp); Ie plain socket open if config option *socks-proxy* is not set. (Should have been SOCKS5-proxy, though.) else{ u8 pbuf[4 + 1 + 255 + 2]; uz i; char const *emsg; struct mx_url url2; if(!mx_url_parse(&url2, CPROTO_SOCKS, cp)){ (socks:// or socks5://, default port 1080.) n_err(_("Failed to parse *socks-proxy*: %s\n"), cp); goto jleave; } if(urlp->url_host.l > 255){ n_err(_("*socks-proxy*: hostname too long: %s\n"), urlp->url_input); goto jleave; } if(n_poption & n_PO_D_V) n_err(_("Connecting to *socks-proxy*=%s\n"), cp); if(!a_netso_open(sop, &url2)){ Connecting to the SOCKS5 proxy. n_err(_("Failed to connect to *socks-proxy*: %s\n"), cp); goto jleave; } /* RFC 1928: version identifier/method selection message */ pbuf[0] = 0x05; /* VER: protocol version: X'05' */ pbuf[1] = 0x01; /* NMETHODS: 1 */ pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */ Only supporting this, without authentication. if(write(sop->s_fd, pbuf, 3) != 3){ jerrsocks: n_perr("*socks-proxy*", su_err_by_errno()); jesocks: mx_socket_close(sop); goto jleave; } /* Receive greeting */ if(read(sop->s_fd, pbuf, 2) != 2) goto jerrsocks; if(pbuf[0] != 0x05 || pbuf[1] != 0x00){ jesocksreply: emsg = N_("unexpected reply\n"); jesocksreplymsg: /* I18N: error message and failing URL */ n_err(_("*socks-proxy*: %s: %s\n"), V_(emsg), cp); goto jesocks; } /* RFC 1928: CONNECT request */ if(n_poption & n_PO_D_V) n_err(_("Through *socks-proxy*, connecting to %s:%s ...\n"), urlp->url_host.s, (urlp->url_port != NULL ? urlp->url_port : urlp->url_proto)); pbuf[0] = 0x05; /* VER: protocol version: X'05' */ pbuf[1] = 0x01; /* CMD: CONNECT X'01' */ pbuf[2] = 0x00; /* RESERVED */ pbuf[3] = 0x03; /* ATYP: domain name */ pbuf[4] = (u8)urlp->url_host.l; su_mem_copy(&pbuf[i = 5], urlp->url_host.s, urlp->url_host.l); We do not resolve DNS ourselves if SOCKS5 proxy is used. Like this DNS resolution, except for the proxy itself, is solely performed by the proxy. /* C99 */{ u16 x; x = htons(urlp->url_portno); su_mem_copy(&pbuf[i += urlp->url_host.l], (u8*)&x, sizeof x); i += sizeof x; } if(write(sop->s_fd, pbuf, i) != (sz)i) goto jerrsocks; This is of course all blocking I/O. (How i hate how this MUA is implemented, i never have worked with blocking I/O and without dedicated event loop all my life.) /* Connect result */ if((i = read(sop->s_fd, pbuf, 4)) != 4) goto jerrsocks; /* Version 5, reserved must be 0 */ if(pbuf[0] != 0x05 || pbuf[2] != 0x00) goto jesocksreply; /* Result */ switch(pbuf[1]){ case 0x00: emsg = NULL; break; case 0x01: emsg = N_("SOCKS server failure"); break; case 0x02: emsg = N_("connection not allowed by ruleset"); break; case 0x03: emsg = N_("network unreachable"); break; case 0x04: emsg = N_("host unreachable"); break; case 0x05: emsg = N_("connection refused"); break; case 0x06: emsg = N_("TTL expired"); break; case 0x07: emsg = N_("command not supported"); break; case 0x08: emsg = N_("address type not supported"); break; default: emsg = N_("unknown SOCKS error code"); break; } if(emsg != NULL) goto jesocksreplymsg; /* Address type variable; read the BND.PORT with it. * This is actually false since RFC 1928 says that the BND.ADDR reply to * CONNECT contains the IP address, so only 0x01 and 0x04 are allowed */ switch(pbuf[3]){ case 0x01: i = 4; break; case 0x03: i = 1; break; case 0x04: i = 16; break; default: goto jesocksreply; } i += sizeof(u16); if(read(sop->s_fd, pbuf, i) != (sz)i) goto jerrsocks; if(i == 1 + sizeof(u16)){ i = pbuf[0]; if(read(sop->s_fd, pbuf, i) != (sz)i) goto jerrsocks; } /* SSL/TLS upgrade? */ #ifdef mx_HAVE_TLS if(urlp->url_flags & mx_URL_TLS_MASK) rv = (a_netso_open_tls_maybe(sop, urlp) >= 0); else #endif Handing over to TLS I/O as appropriate. rv = TRU1; } jleave: a_netso_test_sig(); return rv; --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt)
/* This is https://fenua.org/gaetan/src/usocks-0.7.c */ /* * Copyright (C) 2013-2018, Gaetan Bisson <bis...@archlinux.org>. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * USocks. Minimalistic SOCKS5 proxying library. * * USocks implements a connect() function over the system one in order to * forward connections through a prescribed SOCKS5 proxy; its design focuses * are code clarity and conciseness. * * Compile with: * * cc -O2 -fPIC -ldl -shared -o usocks.so usocks.c * * Use by exporting: * * USOCKS_PORT=7772 * USOCKS_ADDR=127.0.0.1 * LD_PRELOAD=`pwd`/usocks.so */ /* **************************************************************************** * * HEADERS * */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <dlfcn.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* **************************************************************************** * * INITIALIZE PROXY DATA AND LOCATE SYSTEM FUNCTIONS * */ struct sockaddr_in us_proxy; typedef int (*connect_t)(int, const struct sockaddr *, socklen_t); connect_t sys_connect; int us_init (void) { char *port = getenv("USOCKS_PORT"); char *addr = getenv("USOCKS_ADDR"); if (!port) return -1; if (!addr) return -1; memset(&us_proxy, 0, sizeof(us_proxy)); us_proxy.sin_family = AF_INET; us_proxy.sin_port = htons(atoi(port)); us_proxy.sin_addr.s_addr = inet_addr(addr); sys_connect = (connect_t)(intptr_t)dlsym(RTLD_NEXT, "connect"); return 0; } /* **************************************************************************** * * LEAVE NO BYTES BEHIND * */ int us_sendall (int socket, const char *buffer, size_t length, int flags) { int r, off=0; while(off<length) { r = send(socket, buffer+off, length-off, flags); if (r<0) return -1; off += r; } return off; } int us_recvall (int socket, char *buffer, size_t length, int flags) { int r, off=0; while (off<length) { r = recv(socket, buffer+off, length-off, flags); if (r<0) return -1; off += r; } return off; } /* **************************************************************************** * * REDEFINE CONNECT() * */ const unsigned char l4[] = { 0x7f }; /* matches loopback IPv4 addresses */ const unsigned char l6[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; /* matches loopback IPv6 address */ const unsigned char l64[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f }; /* matches loopback IPv6to4 addresses */ int connect (int sock, const struct sockaddr *addr, socklen_t len) { int p, t, v, f=sizeof(t); char b[256]; if (!sys_connect) if (us_init()) return -1; /* let unix domain sockets and loopback traffic through */ switch (addr->sa_family) { case AF_UNIX: return sys_connect(sock,addr,len); case AF_INET6: #if 0 if (!memcmp(&(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), l64, 13) || !memcmp(&(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), l6, 16)) return sys_connect(sock,addr,len); #endif v=16; break; case AF_INET: #if 0 /*if (!memcmp(&(((struct sockaddr_in *)addr)->sin_addr.s_addr), l4, 1)) return sys_connect(sock,addr,len);*/ #endif v=4; break; default: return -1; } /* let non-TCP through */ getsockopt(sock, SOL_SOCKET, SO_TYPE, &t, (socklen_t *)&f); if (t!=SOCK_STREAM) return sys_connect(sock,addr,len); /* open blocking connection to proxy */ f = fcntl(sock, F_GETFL); p = socket(AF_INET, SOCK_STREAM, 0); fcntl(p, F_SETFL, f & ~O_NONBLOCK); if (sys_connect(p,(struct sockaddr*)&us_proxy,sizeof(us_proxy))) return -1; /* protocol version and authentication method */ memcpy(b, "\x05\x01\x00", 3); if (us_sendall(p,b,3,0)!=3) goto err; if (us_recvall(p,b,2,0)!=2) goto err; if (memcmp(b,"\x05\x00",2)) goto err; /* connection request */ memcpy(b, "\x05\x01\x00", 3); if (v==4) { b[3] = '\x01'; memcpy(b+4, &(((struct sockaddr_in *)addr)->sin_addr.s_addr), 4); memcpy(b+8, &(((struct sockaddr_in *)addr)->sin_port), 2); } else { b[3] = '\x04'; memcpy(b+4, &(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), 16); memcpy(b+20, &(((struct sockaddr_in6 *)addr)->sin6_port), 2); } if (us_sendall(p,b,v+6,0)!=v+6) goto err; if (us_recvall(p,b,4,0)!=4) goto err; if (memcmp(b,"\x05\x00\x00",3)) goto err; if (us_recvall(p,b,v+2,0)!=v+2) goto err; /* return proxy socket */ close(sock); fcntl(p, F_SETFL, f); fcntl(p, F_DUPFD, sock); return 0; err: close(p); return -1; }
_______________________________________________ Postfix-users mailing list -- postfix-users@postfix.org To unsubscribe send an email to postfix-users-le...@postfix.org