mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-20 10:12:15 +01:00
r8894@Kushana: nickm | 2006-09-21 18:30:42 -0400
Specify and implement SOCKS5 interface for reverse hostname lookup. svn:r8451
This commit is contained in:
parent
9bc8d69dfc
commit
213658f117
@ -6,6 +6,8 @@ Changes in version 0.1.2.2-alpha - 2006-??-??
|
|||||||
previously never implemented. This is only supported by eventdns;
|
previously never implemented. This is only supported by eventdns;
|
||||||
servers now announce in their descriptors whether they support
|
servers now announce in their descriptors whether they support
|
||||||
eventdns.
|
eventdns.
|
||||||
|
- Specify and implement client-side SOCKS5 interface for reverse DNS
|
||||||
|
lookups; see doc/socks-extensions.txt for full information.
|
||||||
|
|
||||||
o Minor features:
|
o Minor features:
|
||||||
- Check for name servers (like Earthlink's) that hijack failing DNS
|
- Check for name servers (like Earthlink's) that hijack failing DNS
|
||||||
|
@ -32,15 +32,14 @@ def socks5Hello():
|
|||||||
def socks5ParseHello(response):
|
def socks5ParseHello(response):
|
||||||
if response != "\x05\x00":
|
if response != "\x05\x00":
|
||||||
raise ValueError("Bizarre socks5 response")
|
raise ValueError("Bizarre socks5 response")
|
||||||
def socks5ResolveRequest(hostname):
|
def socks5ResolveRequest(hostname, atype=0x03, command=0xF0):
|
||||||
version = 5
|
version = 5
|
||||||
command = 0xF0
|
|
||||||
rsv = 0
|
rsv = 0
|
||||||
port = 0
|
port = 0
|
||||||
atype = 0x03
|
|
||||||
reqheader = struct.pack("!BBBBB",version, command, rsv, atype, len(hostname))
|
reqheader = struct.pack("!BBBBB",version, command, rsv, atype, len(hostname))
|
||||||
portstr = struct.pack("!H",port)
|
portstr = struct.pack("!H",port)
|
||||||
return "%s%s%s"%(reqheader,hostname,portstr)
|
return "%s%s%s"%(reqheader,hostname,portstr)
|
||||||
|
|
||||||
def socks5ParseResponse(r):
|
def socks5ParseResponse(r):
|
||||||
if len(r)<8:
|
if len(r)<8:
|
||||||
return None
|
return None
|
||||||
@ -49,18 +48,27 @@ def socks5ParseResponse(r):
|
|||||||
assert rsv==0
|
assert rsv==0
|
||||||
if reply != 0x00:
|
if reply != 0x00:
|
||||||
return "ERROR",reply
|
return "ERROR",reply
|
||||||
assert atype in (0x01,0x04)
|
assert atype in (0x01,0x03,0x04)
|
||||||
expected_len = 4 + ({1:4,4:16}[atype]) + 2
|
if atype != 0x03:
|
||||||
if len(r) < expected_len:
|
expected_len = 4 + ({1:4,4:16}[atype]) + 2
|
||||||
return None
|
if len(r) < expected_len:
|
||||||
elif len(r) > expected_len:
|
return None
|
||||||
raise ValueError("Overlong socks5 reply!")
|
elif len(r) > expected_len:
|
||||||
addr = r[4:-2]
|
raise ValueError("Overlong socks5 reply!")
|
||||||
if atype == 0x01:
|
addr = r[4:-2]
|
||||||
return "%d.%d.%d.%d"%tuple(map(ord,addr))
|
if atype == 0x01:
|
||||||
|
return "%d.%d.%d.%d"%tuple(map(ord,addr))
|
||||||
|
else:
|
||||||
|
# not really the right way to format IPv6
|
||||||
|
return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
|
||||||
else:
|
else:
|
||||||
# not really the right way to format IPv6
|
nul = r.index('\0',4)
|
||||||
return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
|
return r[4:nul]
|
||||||
|
|
||||||
|
def socks5ResolvePTRRequest(hostname):
|
||||||
|
return socks5ResolveRequest(socket.inet_aton(hostname),
|
||||||
|
atype=1, command = 0xF1)
|
||||||
|
|
||||||
|
|
||||||
def parseHostAndPort(h):
|
def parseHostAndPort(h):
|
||||||
host, port = "localhost", 9050
|
host, port = "localhost", 9050
|
||||||
@ -80,14 +88,18 @@ def parseHostAndPort(h):
|
|||||||
|
|
||||||
return host, port
|
return host, port
|
||||||
|
|
||||||
def resolve(hostname, sockshost, socksport, socksver=4):
|
def resolve(hostname, sockshost, socksport, socksver=4, reverse=0):
|
||||||
assert socksver in (4,5)
|
assert socksver in (4,5)
|
||||||
if socksver == 4:
|
if socksver == 4:
|
||||||
fmt = socks4AResolveRequest
|
fmt = socks4AResolveRequest
|
||||||
parse = socks4AParseResponse
|
parse = socks4AParseResponse
|
||||||
else:
|
elif not reverse:
|
||||||
fmt = socks5ResolveRequest
|
fmt = socks5ResolveRequest
|
||||||
parse = socks5ParseResponse
|
parse = socks5ParseResponse
|
||||||
|
else:
|
||||||
|
fmt = socks5ResolvePTRRequest
|
||||||
|
parse = socks5ParseResponse
|
||||||
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
s.connect((sockshost,socksport))
|
s.connect((sockshost,socksport))
|
||||||
if socksver == 5:
|
if socksver == 5:
|
||||||
@ -114,14 +126,25 @@ if __name__ == '__main__':
|
|||||||
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
|
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
socksver = 4
|
socksver = 4
|
||||||
if sys.argv[1] in ("-4", "-5"):
|
reverse = 0
|
||||||
socksver = int(sys.argv[1][1])
|
while sys.argv[1] == '-':
|
||||||
del sys.argv[1]
|
if sys.argv[1] in ("-4", "-5"):
|
||||||
if len(sys.argv) == 4:
|
socksver = int(sys.argv[1][1])
|
||||||
|
del sys.argv[1]
|
||||||
|
elif sys.argv[1] == '-x':
|
||||||
|
reverse = 1
|
||||||
|
del sys.argv[1]
|
||||||
|
elif sys.argv[1] == '--':
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(sys.argv) >= 4:
|
||||||
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
|
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if len(sys.argv) == 3:
|
if len(sys.argv) == 3:
|
||||||
sh,sp = parseHostAndPort(sys.argv[2])
|
sh,sp = parseHostAndPort(sys.argv[2])
|
||||||
else:
|
else:
|
||||||
sh,sp = parseHostAndPort("")
|
sh,sp = parseHostAndPort("")
|
||||||
resolve(sys.argv[1], sh, sp, socksver)
|
|
||||||
|
if reverse and socksver == 4:
|
||||||
|
socksver = 5
|
||||||
|
resolve(sys.argv[1], sh, sp, socksver, reverse)
|
||||||
|
7
doc/TODO
7
doc/TODO
@ -109,7 +109,12 @@ d - Special-case localhost?
|
|||||||
o Connect to resolve cells, server-side.
|
o Connect to resolve cells, server-side.
|
||||||
o Add element to routerinfo to note routers that aren't using eventdns,
|
o Add element to routerinfo to note routers that aren't using eventdns,
|
||||||
so we can avoid sending them reverse DNS etc.
|
so we can avoid sending them reverse DNS etc.
|
||||||
- Add client-side interface
|
. Add client-side interface
|
||||||
|
o SOCKS interface: specify
|
||||||
|
o SOCKS interface: implement
|
||||||
|
- Cache answers client-side
|
||||||
|
o Add to Tor-resolve.py
|
||||||
|
- Add to tor-resolve
|
||||||
|
|
||||||
- Performance improvements
|
- Performance improvements
|
||||||
|
|
||||||
|
@ -46,6 +46,12 @@ Tor's extensions to the SOCKS protocol
|
|||||||
|
|
||||||
(We support RESOLVE in SOCKS4 too, even though it is unnecessary.)
|
(We support RESOLVE in SOCKS4 too, even though it is unnecessary.)
|
||||||
|
|
||||||
|
For SOCKS5 only, we support reverse resolution with a new command value,
|
||||||
|
"RESOLVE_PTR". In response to a "RESOLVE_PTR" SOCKS5 command with an IPv4
|
||||||
|
address as its target, Tor attempts to find the canonical hostname for that
|
||||||
|
IPv4 record, and returns it in the "server bound address" portion of the
|
||||||
|
reply. (This was not supported before Tor 0.1.2.2-alpha)
|
||||||
|
|
||||||
3. HTTP-resistance
|
3. HTTP-resistance
|
||||||
|
|
||||||
Tor checks the first byte of each SOCKS request to see whether it looks
|
Tor checks the first byte of each SOCKS request to see whether it looks
|
||||||
|
@ -974,8 +974,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
|||||||
return 0; /* not yet */
|
return 0; /* not yet */
|
||||||
req->command = (unsigned char) *(buf->cur+1);
|
req->command = (unsigned char) *(buf->cur+1);
|
||||||
if (req->command != SOCKS_COMMAND_CONNECT &&
|
if (req->command != SOCKS_COMMAND_CONNECT &&
|
||||||
req->command != SOCKS_COMMAND_RESOLVE) {
|
req->command != SOCKS_COMMAND_RESOLVE &&
|
||||||
/* not a connect or resolve? we don't support it. */
|
req->command != SOCKS_COMMAND_RESOLVE_PTR) {
|
||||||
|
/* not a connect or resolve or a resolve_ptr? we don't support it. */
|
||||||
log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
|
log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
|
||||||
req->command);
|
req->command);
|
||||||
return -1;
|
return -1;
|
||||||
@ -999,7 +1000,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
|||||||
strlcpy(req->address,tmpbuf,sizeof(req->address));
|
strlcpy(req->address,tmpbuf,sizeof(req->address));
|
||||||
req->port = ntohs(*(uint16_t*)(buf->cur+8));
|
req->port = ntohs(*(uint16_t*)(buf->cur+8));
|
||||||
buf_remove_from_front(buf, 10);
|
buf_remove_from_front(buf, 10);
|
||||||
if (!addressmap_have_mapping(req->address) &&
|
if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
|
||||||
|
!addressmap_have_mapping(req->address) &&
|
||||||
!have_warned_about_unsafe_socks) {
|
!have_warned_about_unsafe_socks) {
|
||||||
log_warn(LD_APP,
|
log_warn(LD_APP,
|
||||||
"Your application (using socks5 on port %d) is giving "
|
"Your application (using socks5 on port %d) is giving "
|
||||||
@ -1025,6 +1027,11 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
|||||||
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
|
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
|
||||||
|
log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
|
||||||
|
"hostname type. Rejecting.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
memcpy(req->address,buf->cur+5,len);
|
memcpy(req->address,buf->cur+5,len);
|
||||||
req->address[len] = 0;
|
req->address[len] = 0;
|
||||||
req->port = ntohs(get_uint16(buf->cur+5+len));
|
req->port = ntohs(get_uint16(buf->cur+5+len));
|
||||||
@ -1059,7 +1066,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
|||||||
req->command = (unsigned char) *(buf->cur+1);
|
req->command = (unsigned char) *(buf->cur+1);
|
||||||
if (req->command != SOCKS_COMMAND_CONNECT &&
|
if (req->command != SOCKS_COMMAND_CONNECT &&
|
||||||
req->command != SOCKS_COMMAND_RESOLVE) {
|
req->command != SOCKS_COMMAND_RESOLVE) {
|
||||||
/* not a connect or resolve? we don't support it. */
|
/* not a connect or resolve? we don't support it. (No resolve_ptr with
|
||||||
|
* socks4.) */
|
||||||
log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
|
log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
|
||||||
req->command);
|
req->command);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1120,7 +1120,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socks->command == SOCKS_COMMAND_RESOLVE) {
|
if (socks->command == SOCKS_COMMAND_RESOLVE) { // resolve_ptr XXXX NM
|
||||||
uint32_t answer;
|
uint32_t answer;
|
||||||
struct in_addr in;
|
struct in_addr in;
|
||||||
/* Reply to resolves immediately if we can. */
|
/* Reply to resolves immediately if we can. */
|
||||||
@ -1181,7 +1181,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
|||||||
rend_cache_entry_t *entry;
|
rend_cache_entry_t *entry;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (socks->command == SOCKS_COMMAND_RESOLVE) {
|
if (socks->command != SOCKS_COMMAND_CONNECT) {
|
||||||
/* if it's a resolve request, fail it right now, rather than
|
/* if it's a resolve request, fail it right now, rather than
|
||||||
* building all the circuits and then realizing it won't work. */
|
* building all the circuits and then realizing it won't work. */
|
||||||
log_warn(LD_APP,
|
log_warn(LD_APP,
|
||||||
@ -1524,13 +1524,17 @@ int
|
|||||||
connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
|
connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
|
||||||
origin_circuit_t *circ)
|
origin_circuit_t *circ)
|
||||||
{
|
{
|
||||||
int payload_len;
|
int payload_len, command;
|
||||||
const char *string_addr;
|
const char *string_addr;
|
||||||
|
char inaddr_buf[32];
|
||||||
|
|
||||||
|
command = ap_conn->socks_request->command;
|
||||||
|
|
||||||
tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
|
tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
|
||||||
tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
|
tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
|
||||||
tor_assert(ap_conn->socks_request);
|
tor_assert(ap_conn->socks_request);
|
||||||
tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
|
tor_assert(command == SOCKS_COMMAND_RESOLVE ||
|
||||||
|
command == SOCKS_COMMAND_RESOLVE_PTR);
|
||||||
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
|
||||||
|
|
||||||
ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
|
ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
|
||||||
@ -1540,9 +1544,27 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
string_addr = ap_conn->socks_request->address;
|
if (command == SOCKS_COMMAND_RESOLVE) {
|
||||||
payload_len = strlen(string_addr)+1;
|
string_addr = ap_conn->socks_request->address;
|
||||||
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
|
payload_len = strlen(string_addr)+1;
|
||||||
|
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
|
||||||
|
} else {
|
||||||
|
struct in_addr in;
|
||||||
|
uint32_t a;
|
||||||
|
if (tor_inet_aton(ap_conn->socks_request->address, &in) == 0) {
|
||||||
|
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
a = ntohl(in.s_addr);
|
||||||
|
tor_snprintf(inaddr_buf, sizeof(inaddr_buf), "%d.%d.%d.%d.in-addr.arpa",
|
||||||
|
(int)(uint8_t)((a )&0xff),
|
||||||
|
(int)(uint8_t)((a>>8 )&0xff),
|
||||||
|
(int)(uint8_t)((a>>16)&0xff),
|
||||||
|
(int)(uint8_t)((a>>24)&0xff));
|
||||||
|
string_addr = inaddr_buf;
|
||||||
|
payload_len = strlen(inaddr_buf)+1;
|
||||||
|
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
log_debug(LD_APP,
|
log_debug(LD_APP,
|
||||||
"Sending relay cell to begin stream %d.", ap_conn->stream_id);
|
"Sending relay cell to begin stream %d.", ap_conn->stream_id);
|
||||||
@ -1625,7 +1647,7 @@ connection_ap_make_bridge(char *address, uint16_t port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Send an answer to an AP connection that has requested a DNS lookup
|
/** Send an answer to an AP connection that has requested a DNS lookup
|
||||||
* via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6) or
|
* via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6|HOSTNAME) or
|
||||||
* -1 for unreachable; the answer should be in the format specified
|
* -1 for unreachable; the answer should be in the format specified
|
||||||
* in the socks extensions document.
|
* in the socks extensions document.
|
||||||
**/
|
**/
|
||||||
@ -1636,7 +1658,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
|
|||||||
const char *answer,
|
const char *answer,
|
||||||
int ttl)
|
int ttl)
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[384];
|
||||||
size_t replylen;
|
size_t replylen;
|
||||||
|
|
||||||
if (answer_type == RESOLVED_TYPE_IPV4) {
|
if (answer_type == RESOLVED_TYPE_IPV4) {
|
||||||
@ -1675,6 +1697,14 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
|
|||||||
memcpy(buf+4, answer, 16); /* address */
|
memcpy(buf+4, answer, 16); /* address */
|
||||||
set_uint16(buf+20, 0); /* port == 0. */
|
set_uint16(buf+20, 0); /* port == 0. */
|
||||||
replylen = 22;
|
replylen = 22;
|
||||||
|
} else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
|
||||||
|
buf[1] = SOCKS5_SUCCEEDED;
|
||||||
|
buf[2] = 0; /* reserved */
|
||||||
|
buf[3] = 0x03; /* Domainname address type */
|
||||||
|
memcpy(buf+4, answer, answer_len); /* address */
|
||||||
|
buf[4+answer_len] = '\0';
|
||||||
|
set_uint16(buf+4+answer_len+1, 0); /* port == 0. */
|
||||||
|
replylen = 4+answer_len+1+2;
|
||||||
} else {
|
} else {
|
||||||
buf[1] = SOCKS5_HOST_UNREACHABLE;
|
buf[1] = SOCKS5_HOST_UNREACHABLE;
|
||||||
memset(buf+2, 0, 8);
|
memset(buf+2, 0, 8);
|
||||||
@ -1699,7 +1729,8 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
|
|||||||
void
|
void
|
||||||
connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
|
connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
|
||||||
size_t replylen,
|
size_t replylen,
|
||||||
socks5_reply_status_t status) {
|
socks5_reply_status_t status)
|
||||||
|
{
|
||||||
char buf[256];
|
char buf[256];
|
||||||
tor_assert(conn->socks_request); /* make sure it's an AP stream */
|
tor_assert(conn->socks_request); /* make sure it's an AP stream */
|
||||||
|
|
||||||
@ -2072,7 +2103,7 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->socks_request->command != SOCKS_COMMAND_RESOLVE) {
|
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
|
||||||
struct in_addr in;
|
struct in_addr in;
|
||||||
uint32_t addr = 0;
|
uint32_t addr = 0;
|
||||||
addr_policy_result_t r;
|
addr_policy_result_t r;
|
||||||
@ -2082,7 +2113,13 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
|
|||||||
exit->exit_policy);
|
exit->exit_policy);
|
||||||
if (r == ADDR_POLICY_REJECTED || r == ADDR_POLICY_PROBABLY_REJECTED)
|
if (r == ADDR_POLICY_REJECTED || r == ADDR_POLICY_PROBABLY_REJECTED)
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else { /* Some kind of a resolve. */
|
||||||
|
|
||||||
|
/* Can't support reverse lookups without eventdns. */
|
||||||
|
if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
|
||||||
|
exit->has_old_dnsworkers)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Don't send DNS requests to non-exit servers by default. */
|
/* Don't send DNS requests to non-exit servers by default. */
|
||||||
if (policy_is_reject_star(exit->exit_policy))
|
if (policy_is_reject_star(exit->exit_policy))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1612,6 +1612,7 @@ typedef struct {
|
|||||||
#define MAX_SOCKS_ADDR_LEN 256
|
#define MAX_SOCKS_ADDR_LEN 256
|
||||||
#define SOCKS_COMMAND_CONNECT 0x01
|
#define SOCKS_COMMAND_CONNECT 0x01
|
||||||
#define SOCKS_COMMAND_RESOLVE 0xF0
|
#define SOCKS_COMMAND_RESOLVE 0xF0
|
||||||
|
#define SOCKS_COMMAND_RESOLVE_PTR 0xF1
|
||||||
/** State of a SOCKS request from a user to an OP */
|
/** State of a SOCKS request from a user to an OP */
|
||||||
struct socks_request_t {
|
struct socks_request_t {
|
||||||
char socks_version; /**< Which version of SOCKS did the client use? */
|
char socks_version; /**< Which version of SOCKS did the client use? */
|
||||||
|
Loading…
Reference in New Issue
Block a user