This commit is contained in:
Michał Majchrowicz 2026-08-21 00:57:32 -07:00 committed by GitHub
commit ef9adc7ac3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 147 additions and 4 deletions

View file

@ -306,6 +306,20 @@ void nsock_proxychain_delete(nsock_proxychain chain);
* nsock_pool_set_proxychain(). Existing nsock_iod will connect as normal. */
int nsock_pool_set_proxychain(nsock_pool nspool, nsock_proxychain chain);
/* Proxy type constants returned by nsock_proxychain_first_node_info(). */
#define NSOCK_PROXY_TYPE_HTTP 0
#define NSOCK_PROXY_TYPE_SOCKS4 1
/* Retrieve the address, port, and type of the first proxy node in a chain.
* ss and sslen are filled with the proxy server's sockaddr. port is filled with
* the proxy port. proxy_type is set to one of the NSOCK_PROXY_TYPE_* constants.
* Returns 1 on success, 0 if the chain is NULL or empty. */
int nsock_proxychain_first_node_info(nsock_proxychain chain,
struct sockaddr_storage *ss,
size_t *sslen,
unsigned short *port,
int *proxy_type);
/* nsock_event handles a single event. Its ID is generally returned when the
* event is created, and the event itself is included in callbacks
*

View file

@ -152,6 +152,30 @@ int nsock_pool_set_proxychain(nsock_pool nspool, nsock_proxychain chain) {
return 1;
}
int nsock_proxychain_first_node_info(nsock_proxychain chain,
struct sockaddr_storage *ss,
size_t *sslen,
unsigned short *port,
int *proxy_type) {
struct proxy_chain *pchain = (struct proxy_chain *)chain;
gh_lnode_t *first;
struct proxy_node *node;
if (!pchain)
return 0;
first = gh_list_first_elem(&pchain->nodes);
if (!first)
return 0;
node = container_of(first, struct proxy_node, nodeq);
*ss = node->ss;
*sslen = node->sslen;
*port = node->port;
*proxy_type = (int)node->spec->type;
return 1;
}
struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool) {
struct npool *nsp = (struct npool *)nspool;
struct proxy_chain_context *ctx;

View file

@ -109,6 +109,15 @@ public:
ConnectProbe();
~ConnectProbe();
int sd; /* Socket descriptor used for connection. -1 if not valid. */
/* SOCKS4 proxy state for connect scans using --proxy. */
enum ProxyState { PROXY_NONE = 0, PROXY_CONNECTING, PROXY_READING };
ProxyState proxy_state;
struct sockaddr_storage proxy_target_ss; /* original target address */
size_t proxy_target_sslen;
uint16_t proxy_target_port;
uint8_t proxy_resp_buf[8]; /* buffer for 8-byte SOCKS4 response */
int proxy_resp_len; /* bytes received so far */
};
struct IPExtraProbeData_icmp {

View file

@ -68,6 +68,7 @@
#include "scan_engine_connect.h"
#include "libnetutil/netutil.h" /* for max_sd() */
#include "NmapOps.h"
#include <nsock.h>
#include <errno.h>
@ -207,6 +208,10 @@ bool ConnectScanInfo::clearSD(int sd) {
ConnectProbe::ConnectProbe() {
sd = -1;
proxy_state = PROXY_NONE;
proxy_target_sslen = 0;
proxy_target_port = 0;
proxy_resp_len = 0;
}
ConnectProbe::~ConnectProbe() {
@ -488,6 +493,31 @@ UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss,
#if HAVE_IPV6
else sin6->sin6_port = htons(probe->pspec()->pd.tcp.dport);
#endif
/* If a SOCKS4 proxy chain is configured, connect to the proxy instead of
* the target directly and perform the SOCKS4 handshake asynchronously. */
struct sockaddr_storage proxy_ss;
size_t proxy_sslen = 0;
unsigned short proxy_port = 0;
int proxy_type = -1;
if (o.proxy_chain &&
nsock_proxychain_first_node_info(o.proxy_chain, &proxy_ss, &proxy_sslen,
&proxy_port, &proxy_type) == 1 &&
proxy_type == NSOCK_PROXY_TYPE_SOCKS4 &&
sock.ss_family == AF_INET) {
/* Save the real target so we can build the SOCKS4 request later. */
CP->proxy_state = ConnectProbe::PROXY_CONNECTING;
memcpy(&CP->proxy_target_ss, &sock, socklen);
CP->proxy_target_sslen = socklen;
CP->proxy_target_port = destport;
/* Point sin/socklen at the proxy address. */
((struct sockaddr_in *)&proxy_ss)->sin_port = htons(proxy_port);
memcpy(&sock, &proxy_ss, proxy_sslen);
socklen = proxy_sslen;
sin = (struct sockaddr_in *) &sock;
}
/* We don't record a byte count for connect probes. */
hss->probeSent(0);
rc = connect(CP->sd, (struct sockaddr *)&sock, socklen);
@ -510,6 +540,23 @@ UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss,
PacketTrace::traceConnect(IPPROTO_TCP, (sockaddr *) &sock, socklen, rc,
connect_errno, &USI->now);
USI->gstats->CSI->watchSD(CP->sd);
} else if (CP->proxy_state == ConnectProbe::PROXY_CONNECTING && connect_errno == 0) {
/* Immediate synchronous connect to proxy succeeded — send SOCKS4 request
* and watch for the response. */
uint8_t req[9];
struct sockaddr_in *tsin = (struct sockaddr_in *)&CP->proxy_target_ss;
req[0] = 4; req[1] = 1;
req[2] = (CP->proxy_target_port >> 8) & 0xff;
req[3] = CP->proxy_target_port & 0xff;
memcpy(&req[4], &tsin->sin_addr.s_addr, 4);
req[8] = 0; /* empty user id */
if (send(CP->sd, (char *)req, sizeof(req), 0) == (int)sizeof(req)) {
CP->proxy_state = ConnectProbe::PROXY_READING;
USI->gstats->CSI->watchSD(CP->sd);
} else {
handleConnectResult(USI, hss, probeI, ECONNREFUSED, true);
probe = NULL;
}
} else {
handleConnectResult(USI, hss, probeI, connect_errno, true);
probe = NULL;
@ -601,11 +648,60 @@ bool do_one_select_round(UltraScanInfo *USI, struct timeval *stime) {
checked_fd_isset(sd, &fds_wtmp) ||
checked_fd_isset(sd, &fds_xtmp))) {
numGoodSD++;
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char *) &optval,
&optlen) != 0)
optval = socket_errno(); /* Stupid Solaris ... */
ConnectProbe *CP = probe->CP();
handleConnectResult(USI, host, probeI, optval);
if (CP->proxy_state == ConnectProbe::PROXY_CONNECTING) {
/* Connected (or failed) to proxy server. Check result. */
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char *) &optval,
&optlen) != 0)
optval = socket_errno();
if (optval != 0) {
handleConnectResult(USI, host, probeI, optval);
} else {
/* Send the 9-byte SOCKS4 CONNECT request. */
uint8_t req[9];
struct sockaddr_in *tsin =
(struct sockaddr_in *)&CP->proxy_target_ss;
req[0] = 4; req[1] = 1;
req[2] = (CP->proxy_target_port >> 8) & 0xff;
req[3] = CP->proxy_target_port & 0xff;
memcpy(&req[4], &tsin->sin_addr.s_addr, 4);
req[8] = 0; /* empty user id */
if (send(sd, (char *)req, sizeof(req), 0) == (int)sizeof(req)) {
CP->proxy_state = ConnectProbe::PROXY_READING;
/* Stop watching for writability; keep read + except. */
checked_fd_clr(sd, &USI->gstats->CSI->fds_write);
} else {
handleConnectResult(USI, host, probeI, ECONNREFUSED);
}
}
} else if (CP->proxy_state == ConnectProbe::PROXY_READING) {
/* Reading the 8-byte SOCKS4 response. */
int recvd = recv(sd,
(char *)CP->proxy_resp_buf + CP->proxy_resp_len,
8 - CP->proxy_resp_len, 0);
if (recvd > 0) {
CP->proxy_resp_len += recvd;
if (CP->proxy_resp_len >= 8) {
/* Full response received: byte[1]==90 means granted. */
if (CP->proxy_resp_buf[1] == 90)
handleConnectResult(USI, host, probeI, 0);
else
handleConnectResult(USI, host, probeI, ECONNREFUSED);
}
/* Partial read: probe stays outstanding, wait for more data. */
} else if (recvd == 0) {
handleConnectResult(USI, host, probeI, ECONNREFUSED);
} else {
handleConnectResult(USI, host, probeI, socket_errno());
}
} else {
/* Normal (no proxy) connect result. */
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char *) &optval,
&optlen) != 0)
optval = socket_errno(); /* Stupid Solaris ... */
handleConnectResult(USI, host, probeI, optval);
}
}
}
}