mirror of
https://github.com/3proxy/3proxy.git
synced 2026-09-02 12:55:49 +08:00
Fix dnspr for Windows
This commit is contained in:
parent
845df7d196
commit
a194f207ba
@ -8,7 +8,7 @@ BUILDDIR = ../bin/
|
||||
PREFIX = 3proxy_
|
||||
CRYPT_PREFIX = 3proxy_
|
||||
CC = cl
|
||||
CFLAGS = /D "WITH_HTTPSRV" /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
||||
CFLAGS = /D "WITH_HTTPSRV" /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "NO_SHARE_UDP_SOCKET" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
||||
COUT = /Fo
|
||||
LN = link
|
||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||
|
||||
@ -45,7 +45,7 @@ void * dnsprchild(struct clientparam* param) {
|
||||
memcpy(buf, param->srv->udpbuf, i);
|
||||
_3proxy_sem_unlock(udpinit);
|
||||
semlocked = 0;
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !defined(SHARE_UDP_SOCKET)
|
||||
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||
RETURN(818);
|
||||
}
|
||||
@ -56,6 +56,8 @@ void * dnsprchild(struct clientparam* param) {
|
||||
}
|
||||
|
||||
#else
|
||||
/* The reply has to come from the address the query was sent to, which
|
||||
is the listening socket. */
|
||||
param->clisock = param->srv->srvsock;
|
||||
#endif
|
||||
|
||||
@ -217,7 +219,8 @@ CLEANRET:
|
||||
}
|
||||
if(bbuf)free(bbuf);
|
||||
if(host)free(host);
|
||||
#ifndef _WIN32
|
||||
#if !defined(_WIN32) || defined(SHARE_UDP_SOCKET)
|
||||
/* The socket belongs to the service, so the caller must not close it. */
|
||||
param->clisock = INVALID_SOCKET;
|
||||
#endif
|
||||
return (NULL);
|
||||
|
||||
15
src/proxy.h
15
src/proxy.h
@ -152,6 +152,21 @@ void daemonize(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* A UDP service answers from the address the client sent to, so the reply has
|
||||
to leave the listening socket. Sharing that socket with the request handler
|
||||
is the simple way, and what every Unix build does.
|
||||
|
||||
Older Windows cannot have two operations in flight on one socket, so a
|
||||
build for it binds a second socket to the same address instead, which needs
|
||||
SO_REUSEADDR on the listening socket as well. A build for those versions
|
||||
asks for that with NO_SHARE_UDP_SOCKET, as Makefile.watcom does.
|
||||
*/
|
||||
#ifndef SHARE_UDP_SOCKET
|
||||
#ifndef NO_SHARE_UDP_SOCKET
|
||||
#define SHARE_UDP_SOCKET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
size_t threadstacksize(int extra);
|
||||
#endif
|
||||
|
||||
@ -835,10 +835,21 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
port there, and it only allows another local process to bind the same
|
||||
address and port, with undefined behaviour as to which socket receives the
|
||||
connections. Use -olSO_EXCLUSIVEADDRUSE to prevent that instead.
|
||||
|
||||
A Windows build which does not share the listening socket is the exception:
|
||||
its UDP services bind a second socket to the same address to answer from,
|
||||
and Windows only allows that when both sockets ask for it.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
opt = 1;
|
||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||
#else
|
||||
#ifndef SHARE_UDP_SOCKET
|
||||
if(isudp){
|
||||
opt = 1;
|
||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SO_REUSEPORT
|
||||
opt = 1;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
"""dnspr: a caching DNS proxy, answering from what it has been told."""
|
||||
|
||||
import time
|
||||
|
||||
|
||||
def run(t):
|
||||
port = t.free_port()
|
||||
@ -15,8 +17,11 @@ def run(t):
|
||||
allow *
|
||||
dnspr -p{port}
|
||||
""")
|
||||
# wait for the service: a datagram sent too early is simply lost
|
||||
for _ in range(100):
|
||||
# Wait for the service: a datagram sent too early is simply lost. Bound
|
||||
# by the clock, not by a number of attempts, so a server that answers
|
||||
# nothing costs seconds rather than minutes.
|
||||
deadline = time.time() + 5
|
||||
while time.time() < deadline:
|
||||
if t.dns_query(port, "host.test"):
|
||||
break
|
||||
|
||||
|
||||
@ -190,15 +190,23 @@ def run(t):
|
||||
|
||||
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{partial}").status,
|
||||
"denying 127.0.0.1 denies the address as written")
|
||||
t.eq(200, t.http(mapped, proxy=f"127.0.0.1:{partial}").status,
|
||||
"but the same host asked for as ::ffff:127.0.0.1 is still reached")
|
||||
|
||||
# Whether the mapped form reaches the same host is up to the stack: it
|
||||
# does where a mapped address is routed to IPv4, and that is the hazard
|
||||
# the security notes describe. Where it does not, there is nothing to
|
||||
# assert, but the rule that names every spelling still has to hold.
|
||||
if t.http(mapped, proxy=f"127.0.0.1:{partial}").status == 200:
|
||||
t.ok("the same host asked for as ::ffff:127.0.0.1 is still reached")
|
||||
else:
|
||||
t.skip("the mapped form (this stack does not route it to IPv4)")
|
||||
|
||||
t.eq(200, t.http(url, proxy=f"127.0.0.1:{partial}").status,
|
||||
"and so is ::1, which the rule never mentioned")
|
||||
"and ::1 is reached, which the rule never mentioned")
|
||||
|
||||
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{complete}").status,
|
||||
"naming every spelling denies the plain address")
|
||||
t.ne(200, t.http(mapped, proxy=f"127.0.0.1:{complete}").status,
|
||||
"and the mapped one")
|
||||
"and the mapped one, whether or not it would have been reachable")
|
||||
t.ne(200, t.http(url, proxy=f"127.0.0.1:{complete}").status,
|
||||
"and the IPv6 loopback")
|
||||
|
||||
|
||||
@ -424,15 +424,20 @@ class Tester:
|
||||
|
||||
# ---- DNS ---------------------------------------------------------
|
||||
|
||||
def dns_query(self, port, name, host="127.0.0.1"):
|
||||
"""Ask for an A record and return the addresses in the answer."""
|
||||
def dns_query(self, port, name, host="127.0.0.1", timeout=2.0):
|
||||
"""Ask for an A record and return the addresses in the answer.
|
||||
|
||||
The default timeout is short: a name server on the loopback answers
|
||||
at once or not at all, and waiting the full request timeout on every
|
||||
attempt turns a server that answers nothing into a very slow run.
|
||||
"""
|
||||
query = struct.pack("!HHHHHH", 0x2A2A, 0x0100, 1, 0, 0, 0)
|
||||
for label in name.split("."):
|
||||
query += bytes([len(label)]) + label.encode()
|
||||
query += b"\x00" + struct.pack("!HH", 1, 1)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.settimeout(self.timeout)
|
||||
sock.settimeout(timeout)
|
||||
try:
|
||||
sock.sendto(query, (host, port))
|
||||
data = sock.recvfrom(65536)[0]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user