mirror of
https://github.com/3proxy/3proxy.git
synced 2026-09-02 21:05:49 +08:00
parent
3ff0f8b7ab
commit
efa6f6560c
@ -8,7 +8,7 @@ BUILDDIR = ../bin/
|
|||||||
PREFIX = 3proxy_
|
PREFIX = 3proxy_
|
||||||
CRYPT_PREFIX = 3proxy_
|
CRYPT_PREFIX = 3proxy_
|
||||||
CC = cl
|
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
|
COUT = /Fo
|
||||||
LN = link
|
LN = link
|
||||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||||
|
|||||||
@ -45,7 +45,7 @@ void * dnsprchild(struct clientparam* param) {
|
|||||||
memcpy(buf, param->srv->udpbuf, i);
|
memcpy(buf, param->srv->udpbuf, i);
|
||||||
_3proxy_sem_unlock(udpinit);
|
_3proxy_sem_unlock(udpinit);
|
||||||
semlocked = 0;
|
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) {
|
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||||
RETURN(818);
|
RETURN(818);
|
||||||
}
|
}
|
||||||
@ -56,6 +56,8 @@ void * dnsprchild(struct clientparam* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
/* The reply has to come from the address the query was sent to, which
|
||||||
|
is the listening socket. */
|
||||||
param->clisock = param->srv->srvsock;
|
param->clisock = param->srv->srvsock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -217,7 +219,8 @@ CLEANRET:
|
|||||||
}
|
}
|
||||||
if(bbuf)free(bbuf);
|
if(bbuf)free(bbuf);
|
||||||
if(host)free(host);
|
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;
|
param->clisock = INVALID_SOCKET;
|
||||||
#endif
|
#endif
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|||||||
15
src/proxy.h
15
src/proxy.h
@ -152,6 +152,21 @@ void daemonize(void);
|
|||||||
#endif
|
#endif
|
||||||
#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
|
#ifndef _WIN32
|
||||||
size_t threadstacksize(int extra);
|
size_t threadstacksize(int extra);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -835,10 +835,21 @@ int MODULEMAINFUNC (int argc, char** argv){
|
|||||||
port there, and it only allows another local process to bind the same
|
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
|
address and port, with undefined behaviour as to which socket receives the
|
||||||
connections. Use -olSO_EXCLUSIVEADDRUSE to prevent that instead.
|
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
|
#ifndef _WIN32
|
||||||
opt = 1;
|
opt = 1;
|
||||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
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
|
#endif
|
||||||
#ifdef SO_REUSEPORT
|
#ifdef SO_REUSEPORT
|
||||||
opt = 1;
|
opt = 1;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
"""dnspr: a caching DNS proxy, answering from what it has been told."""
|
"""dnspr: a caching DNS proxy, answering from what it has been told."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
port = t.free_port()
|
port = t.free_port()
|
||||||
@ -15,8 +17,11 @@ def run(t):
|
|||||||
allow *
|
allow *
|
||||||
dnspr -p{port}
|
dnspr -p{port}
|
||||||
""")
|
""")
|
||||||
# wait for the service: a datagram sent too early is simply lost
|
# Wait for the service: a datagram sent too early is simply lost. Bound
|
||||||
for _ in range(100):
|
# 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"):
|
if t.dns_query(port, "host.test"):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@ -190,15 +190,23 @@ def run(t):
|
|||||||
|
|
||||||
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{partial}").status,
|
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{partial}").status,
|
||||||
"denying 127.0.0.1 denies the address as written")
|
"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,
|
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,
|
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{complete}").status,
|
||||||
"naming every spelling denies the plain address")
|
"naming every spelling denies the plain address")
|
||||||
t.ne(200, t.http(mapped, proxy=f"127.0.0.1:{complete}").status,
|
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,
|
t.ne(200, t.http(url, proxy=f"127.0.0.1:{complete}").status,
|
||||||
"and the IPv6 loopback")
|
"and the IPv6 loopback")
|
||||||
|
|
||||||
|
|||||||
@ -424,15 +424,20 @@ class Tester:
|
|||||||
|
|
||||||
# ---- DNS ---------------------------------------------------------
|
# ---- DNS ---------------------------------------------------------
|
||||||
|
|
||||||
def dns_query(self, port, name, host="127.0.0.1"):
|
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."""
|
"""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)
|
query = struct.pack("!HHHHHH", 0x2A2A, 0x0100, 1, 0, 0, 0)
|
||||||
for label in name.split("."):
|
for label in name.split("."):
|
||||||
query += bytes([len(label)]) + label.encode()
|
query += bytes([len(label)]) + label.encode()
|
||||||
query += b"\x00" + struct.pack("!HH", 1, 1)
|
query += b"\x00" + struct.pack("!HH", 1, 1)
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.settimeout(self.timeout)
|
sock.settimeout(timeout)
|
||||||
try:
|
try:
|
||||||
sock.sendto(query, (host, port))
|
sock.sendto(query, (host, port))
|
||||||
data = sock.recvfrom(65536)[0]
|
data = sock.recvfrom(65536)[0]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user