mirror of
https://github.com/3proxy/3proxy.git
synced 2026-08-13 12:19:17 +08:00
Support acls for UDP ASSOC, fix udppm via SOCKSv5
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
This commit is contained in:
parent
ddb74a111c
commit
bebc3894e2
@ -428,6 +428,139 @@ use 3proxy's built-in resolver (nserver / nscache / nscache6).
|
||||
Public resolvers like those from Google have rate limits. For a large number of
|
||||
requests, install a local caching recursor (ISC bind named, PowerDNS recursor, etc).
|
||||
|
||||
<h4>Use the Authentication Cache</h4>
|
||||
|
||||
Authentication and authorization are performed for every request. Password
|
||||
lookup itself is a hash lookup and is cheap, but the ACL is walked linearly,
|
||||
and with RADIUS or plugin authentication every request costs a network round
|
||||
trip. 'authcache' keeps the result of a successful authentication for a number
|
||||
of seconds:
|
||||
<pre>
|
||||
authcache <cachetype> <cachetime> <cachesize>
|
||||
</pre>
|
||||
'cachetype' lists the fields the entry is keyed by, 'cachetime' is the lifetime
|
||||
in seconds, and the optional 'cachesize' limits the number of entries. The
|
||||
cached type must be placed before the real authentication type in the 'auth'
|
||||
command: a cache miss falls through to the next type in the chain, and a hit
|
||||
skips it.
|
||||
|
||||
<p>There are two cached types:
|
||||
<ul>
|
||||
<li>'cache' caches the authentication only; the ACL is still checked on every
|
||||
request. Use it when ACLs must take effect immediately, or when the
|
||||
authentication method returns per-user parameters that the authorization step
|
||||
has to apply.
|
||||
<li>'cacheacl' also caches the authorization result, so the ACL is not walked
|
||||
at all on a hit. This is the one that helps with long ACLs, but the key must
|
||||
cover every field the ACL limits, and it can not be used with redirections.
|
||||
</ul>
|
||||
|
||||
<p><b>'cacheacl' does not work with parent proxies.</b> Redirections are applied
|
||||
while the ACL is walked, so on a 'cacheacl' hit no redirection happens at all:
|
||||
the request is sent directly instead of through the 'parent' proxy of the
|
||||
matching ACL entry, and nothing is reported. The first request for a given key
|
||||
uses the parent and the rest do not, which makes it look intermittent. Use
|
||||
'cache' with parent proxies:
|
||||
<pre>
|
||||
authcache user,pass 60
|
||||
auth cache strong
|
||||
allow user1
|
||||
parent 1000 socks5 10.0.0.1 1080
|
||||
proxy
|
||||
</pre>
|
||||
The one exception is 'parent ... extip': the external address is stored in the
|
||||
cache entry and restored on a hit, but only if 'ext' is a part of the key:
|
||||
<pre>
|
||||
authcache user,pass,ext 60
|
||||
auth cacheacl strong
|
||||
allow user1
|
||||
parent 1000 extip 1.2.3.4 0
|
||||
proxy
|
||||
</pre>
|
||||
Without 'ext' in the key the external address falls back to the default one on
|
||||
every cache hit.
|
||||
|
||||
<p><b>The key must match the ACL.</b> On a 'cacheacl' hit the cached result is
|
||||
reused as is, so any field the ACL limits but the key does not contain makes
|
||||
the cached result apply to a request it was not computed for. Use 'user' and
|
||||
'pass' for the userlist, 'ip' for the sourcelist, 'dstaddr' and 'dsthost' for
|
||||
the targetlist, 'dstport' for the targetportlist and 'dstoper' for the
|
||||
operationlist. For this ACL:
|
||||
<pre>
|
||||
allow user1,user2 * * 80,443
|
||||
deny *
|
||||
</pre>
|
||||
the key needs 'user', 'pass' and 'dstport':
|
||||
<pre>
|
||||
users $/etc/3proxy/passwd
|
||||
authcache user,pass,dstport 60 65536
|
||||
auth cacheacl strong
|
||||
allow user1,user2 * * 80,443
|
||||
deny *
|
||||
proxy
|
||||
</pre>
|
||||
|
||||
<p>With RADIUS the round trip is the expensive part, so cache by whatever
|
||||
identifies the client:
|
||||
<pre>
|
||||
radius secret 1.2.3.4
|
||||
authcache ip,user,pass 600
|
||||
auth cache radius
|
||||
proxy
|
||||
</pre>
|
||||
'cache' rather than 'cacheacl' is used here because RADIUS may return per-user
|
||||
parameters such as the outgoing IP; add 'ext' to the key to cache the external
|
||||
address returned by RADIUS as well.
|
||||
|
||||
<p>Choose 'cachetime' to balance the load against how quickly a password or ACL
|
||||
change has to take effect. Cache entries are <b>not</b> invalidated by a
|
||||
configuration reload, so a long lifetime keeps applying the old decision to
|
||||
existing keys after the new configuration is loaded.
|
||||
|
||||
<p>Note that 'ip' alone, or 'ip,user' without 'pass', assigns every connection
|
||||
from the same address to the same user for the lifetime of the entry without
|
||||
checking the password. That is a deliberate trade-off for high load; do not use
|
||||
it where clients share addresses.
|
||||
|
||||
<h4>Tune UDP Destination Handling with '-U'</h4>
|
||||
|
||||
Within a SOCKSv5 UDP association the destination is taken from every datagram,
|
||||
so 3proxy authorizes and logs every new destination. One association can reach
|
||||
any number of destinations: a DNS forwarder produces one record, but a
|
||||
peer-to-peer or QUIC client can produce hundreds, each costing an authorization
|
||||
(a network round trip with RADIUS or plugin authentication) and a log record.
|
||||
The 'socks' service option '-U' controls this:
|
||||
<pre>
|
||||
socks -U3 # default: log and authorize every new destination
|
||||
socks -U2 # authorize, do not log
|
||||
socks -U1 # log, do not authorize
|
||||
socks -U0 # neither (same as bare -U)
|
||||
</pre>
|
||||
The destination of the first datagram is always authorized and logged, so the
|
||||
association as a whole is still subject to ACLs, authentication and the choice
|
||||
of the parent proxy.
|
||||
|
||||
<p>Before reaching for the option, note that the cost is already avoided when no
|
||||
ACL entry reachable by the client limits the destination: in that case the
|
||||
result can not depend on the destination and no re-authorization is performed
|
||||
whatever '-U' says. '-U' only matters for configurations that do limit the
|
||||
destination.
|
||||
|
||||
<p>For high load:
|
||||
<ul>
|
||||
<li><b>'-U2'</b> is the safe one. Destination ACLs keep working, only the
|
||||
per-destination log records are dropped. Use it when log volume, not CPU, is
|
||||
the problem.
|
||||
<li><b>'-U' ('-U0')</b> removes both costs and is the fastest, but destination
|
||||
ACLs then apply only to the first destination of each association, and the
|
||||
parent proxy and external address chosen for it are kept for the whole
|
||||
association. Use it when UDP destinations are not restricted anyway, or when
|
||||
the restriction is enforced elsewhere (a firewall, or the parent proxy).
|
||||
<li>Keep the default '-U3' when destination ACLs are a security boundary, and
|
||||
make the authorization itself cheap with 'authcache' instead, see "Use the
|
||||
Authentication Cache" above.
|
||||
</ul>
|
||||
|
||||
<h4>Avoid Large Lists</h4>
|
||||
|
||||
Currently, 3proxy is not optimized to use large ACLs, user lists, etc. All lists
|
||||
|
||||
@ -1338,6 +1338,82 @@ the second hop is 192.168.20.1, and the 3rd one is either 192.168.30.1 with a pr
|
||||
of 30% or 192.168.40.1 with a probability of 70%.
|
||||
</p>
|
||||
|
||||
<li><a name="UDPACL">How to apply ACLs to UDP traffic</a>
|
||||
<p>
|
||||
A SOCKSv5 UDP ASSOCIATE request only asks 3proxy to open a relay port, it
|
||||
carries no destination. The destination is taken from every datagram the client
|
||||
sends afterwards, so a single association may reach any number of destinations.
|
||||
3proxy authorizes the destination of every datagram, so ACLs limiting the
|
||||
destination address, host name or port do apply to UDP traffic.
|
||||
</p>
|
||||
<p>
|
||||
Differences from TCP:
|
||||
</p>
|
||||
<ul>
|
||||
<li>A datagram to a denied destination is dropped, the association is not
|
||||
terminated.
|
||||
<li>The parent proxy and the external address are selected for the destination
|
||||
of the datagram and not for the ASSOCIATE request, and are re-established if a
|
||||
later destination selects another ACL entry. Only <code>socks5</code> and
|
||||
<code>socks5+</code> parents can be used for UDP.
|
||||
<li>ACLs are checked for the datagrams sent by the client only. A datagram
|
||||
received on the association is relayed to the client without an ACL check: with
|
||||
a parent proxy only the datagrams from the parent are accepted, an association
|
||||
without a parent accepts a datagram from any source. The source reported to the
|
||||
client is the actual source of the datagram. The address in the log record is the destination the
|
||||
client sent the datagrams to, the source of the received datagrams is not
|
||||
logged.
|
||||
<li>A client may use a single association to reach many destinations. An ACL
|
||||
entry which limits the destination silently drops the datagrams to every other
|
||||
destination, so limiting the destination of UDP traffic can break such clients
|
||||
in a way which is hard to diagnose. Limit it only when it is really required.
|
||||
<li>Bandwidth limiters and traffic counters which limit the destination are
|
||||
selected when the destination is authorized, so they are selected for the
|
||||
destination of the first datagram and are not re-selected for every following
|
||||
destination. They can not be applied per destination to incoming UDP traffic at
|
||||
all.
|
||||
<li>Every change of the destination host name, address or port is authorized
|
||||
again and logged. Use <code>nscache</code> for host names and the
|
||||
authentication cache for anything more expensive than 'iponly'. The
|
||||
<code>-U</code> option of the <code>socks</code> service turns the logging
|
||||
and/or the re-authorization off; with <code>-U0</code> or <code>-U1</code>
|
||||
only the first destination of an association is authorized.
|
||||
</ul>
|
||||
<p>
|
||||
Limit the destinations a client may send datagrams to:
|
||||
</p><pre>
|
||||
auth iponly
|
||||
allow * * 8.8.8.8 53
|
||||
deny *
|
||||
socks -p1080
|
||||
</pre>
|
||||
<p>
|
||||
The ASSOCIATE request succeeds, datagrams to 8.8.8.8:53 are relayed, datagrams
|
||||
to any other destination are dropped.
|
||||
</p>
|
||||
<p>
|
||||
The same with 'strong' (or RADIUS, or plugin) authentication, where every new
|
||||
destination would otherwise be authenticated again:
|
||||
</p><pre>
|
||||
users user1:CL:password
|
||||
authcache user,pass,dstaddr,dstport 60
|
||||
auth cacheacl strong
|
||||
allow user1 * 8.8.8.8 53
|
||||
deny *
|
||||
socks -p1080
|
||||
</pre>
|
||||
<p>
|
||||
With 'cacheacl' the ACL result is not re-evaluated on a cache hit, so every
|
||||
field the ACL limits must be a part of the cache key: 'user' and 'pass' for the
|
||||
userlist, 'dstaddr' and 'dstport' for the targetlist and the targetportlist,
|
||||
'ip' for the sourcelist, 'dsthost' for host names in the targetlist, 'dstoper'
|
||||
for the operationlist. Without the destination in the key the result cached for
|
||||
one destination is applied to any other destination and the destination is not
|
||||
checked at all. Use plain 'cache' instead of 'cacheacl' if ACLs may change
|
||||
during the cache lifetime, or if a parent proxy is used: on a 'cacheacl' hit
|
||||
the ACL is not walked, so no parent proxy is selected and datagrams are sent
|
||||
directly.
|
||||
</p>
|
||||
<li><A NAME="BANDLIM">How to limit bandwidth</a>
|
||||
<p>
|
||||
3proxy supports bandwidth filters. Use the bandlimin/bandlimout and
|
||||
|
||||
@ -1371,6 +1371,85 @@ pcre_extend deny * 192.168.0.1/16
|
||||
192.168.10.1, второе - 192.168.20.1, а третье - либо 192.168.30.1 с
|
||||
вероятностью 0.3 либо 192.168.40.1 с вероятностью 0.7
|
||||
</p>
|
||||
<li><a name="UDPACL"><i>Как применять ACL к UDP трафику</i></a>
|
||||
<p>
|
||||
Запрос SOCKSv5 UDP ASSOCIATE лишь просит 3proxy открыть порт для ретрансляции
|
||||
и не содержит адреса назначения. Адрес назначения берется из каждой
|
||||
датаграммы, отправленной клиентом, поэтому одна ассоциация может обращаться
|
||||
к любому числу адресов назначения. 3proxy авторизует адрес назначения каждой
|
||||
датаграммы, поэтому ACL, ограничивающие адрес, имя хоста или порт
|
||||
назначения, применяются и к UDP трафику.
|
||||
</p>
|
||||
<p>
|
||||
Отличия от TCP:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Датаграмма к запрещенному адресу назначения отбрасывается, ассоциация
|
||||
при этом не завершается.
|
||||
<li>Вышестоящий прокси и внешний адрес выбираются для адреса назначения
|
||||
датаграммы, а не для запроса ASSOCIATE, и устанавливаются заново, если
|
||||
последующий адрес назначения соответствует другой записи ACL. Для UDP
|
||||
могут использоваться только родители типа <code>socks5</code> и
|
||||
<code>socks5+</code>.
|
||||
<li>ACL проверяются только для датаграмм, отправленных клиентом. Датаграмма,
|
||||
полученная в рамках ассоциации, передается клиенту без проверки ACL: при
|
||||
использовании вышестоящего прокси принимаются только датаграммы от него, а
|
||||
ассоциация без вышестоящего прокси принимает датаграмму от любого источника.
|
||||
Клиенту сообщается фактический адрес источника датаграммы. В журнал записывается адрес назначения, на который клиент
|
||||
отправлял датаграммы, а не адрес источника полученных датаграмм.
|
||||
<li>Клиент может использовать одну ассоциацию для обращения ко многим адресам
|
||||
назначения. Запись ACL, ограничивающая адрес назначения, молча отбрасывает
|
||||
датаграммы ко всем остальным адресам назначения, поэтому ограничение адреса
|
||||
назначения для UDP может нарушить работу таких клиентов, и причину трудно
|
||||
установить. Ограничивайте его, только если это действительно необходимо.
|
||||
<li>Ограничители полосы и счетчики трафика, ограничивающие адрес назначения,
|
||||
выбираются при авторизации адреса назначения, поэтому они выбираются для
|
||||
адреса назначения первой датаграммы и не выбираются заново для каждого
|
||||
последующего адреса назначения. К входящему UDP трафику они не могут быть
|
||||
применены по адресу назначения вообще.
|
||||
<li>Каждое изменение имени хоста, адреса или порта назначения
|
||||
авторизуется заново и записывается в журнал. Используйте <code>nscache</code>
|
||||
для имен хостов и кэш аутентификации для всего, что дороже 'iponly'. Опция
|
||||
<code>-U</code> сервиса <code>socks</code> отключает журналирование и/или
|
||||
повторную авторизацию; при <code>-U0</code> или <code>-U1</code>
|
||||
авторизуется только первый адрес назначения ассоциации.
|
||||
</ul>
|
||||
<p>
|
||||
Ограничить адреса назначения, куда клиент может отправлять датаграммы:
|
||||
</p><pre>
|
||||
auth iponly
|
||||
allow * * 8.8.8.8 53
|
||||
deny *
|
||||
socks -p1080
|
||||
</pre>
|
||||
<p>
|
||||
Запрос ASSOCIATE выполняется успешно, датаграммы к 8.8.8.8:53 передаются,
|
||||
датаграммы к любому другому адресу назначения отбрасываются.
|
||||
</p>
|
||||
<p>
|
||||
То же самое с аутентификацией 'strong' (или RADIUS, или через плагин), при
|
||||
которой иначе каждый новый адрес назначения аутентифицировался бы заново:
|
||||
</p><pre>
|
||||
users user1:CL:password
|
||||
authcache user,pass,dstaddr,dstport 60
|
||||
auth cacheacl strong
|
||||
allow user1 * 8.8.8.8 53
|
||||
deny *
|
||||
socks -p1080
|
||||
</pre>
|
||||
<p>
|
||||
При 'cacheacl' результат проверки ACL не вычисляется заново при попадании
|
||||
в кэш, поэтому каждое поле, ограничиваемое в ACL, должно входить в ключ
|
||||
кэша: 'user' и 'pass' для списка пользователей, 'dstaddr' и 'dstport' для списка
|
||||
адресов и списка портов назначения, 'ip' для списка источников, 'dsthost'
|
||||
для имен хостов в списке назначения, 'dstoper' для списка операций. Без
|
||||
адреса назначения в ключе результат, закэшированный для одного адреса
|
||||
назначения, применяется к любому другому, и адрес назначения не
|
||||
проверяется вообще. Используйте 'cache' вместо 'cacheacl', если ACL могут
|
||||
изменяться в течение времени жизни кэша или если используется вышестоящий
|
||||
прокси: при попадании в кэш 'cacheacl' обход ACL не выполняется, поэтому
|
||||
вышестоящий прокси не выбирается и датаграммы отправляются напрямую.
|
||||
</p>
|
||||
<li><a name="BANDLIM"><i>Как ограничивать скорости приема</i></a>
|
||||
<p>
|
||||
3proxy позволяет устанавливать фильтры ширины потребляемого канала. Для этого
|
||||
|
||||
@ -25,6 +25,26 @@ with an unreachable DNS server (because gethostbyname will block other threads).
|
||||
user requests can be logged.
|
||||
<li>Use -xyz+A character filtering sequences for 'logformat', especially with
|
||||
ODBC logging, to prevent SQL and log record injections.
|
||||
<li>With the 'cacheacl' authentication type the ACL result is cached and is not
|
||||
re-evaluated on a cache hit, so every field the ACL limits must be a part of
|
||||
the 'authcache' key. Any field the ACL limits but the key does not contain
|
||||
makes the cached result apply to a request it was not computed for, and that
|
||||
limitation is not enforced. Use 'user' and 'pass' for the userlist, 'ip' for
|
||||
the sourcelist, 'dstaddr' and 'dsthost' for the targetlist, 'dstport' for the
|
||||
targetportlist and 'dstoper' for the operationlist. Use plain 'cache' instead
|
||||
of 'cacheacl' if ACLs may change during the cache lifetime; cache entries are
|
||||
not invalidated by a configuration reload.
|
||||
<li>'cacheacl' must not be used together with parent proxies. The ACL is not
|
||||
walked on a cache hit, so no redirection is applied and the request is sent
|
||||
directly instead of through the 'parent' proxy of the matching ACL entry,
|
||||
without any error. If the parent proxy is the only sanctioned path to the
|
||||
network, this silently bypasses it. Use 'cache' instead. 'parent ... extip'
|
||||
is the only redirection which survives, and only if 'ext' is a part of the
|
||||
'authcache' key.
|
||||
<li>The destination of every datagram of a SOCKSv5 UDP association is
|
||||
authorized, but only with the default '-U3' (or '-U2') of the 'socks' service.
|
||||
With '-U0' or '-U1' only the first destination of an association is authorized,
|
||||
so ACLs limiting the destination do not restrict the rest of the association.
|
||||
<li>Immediately report all service crashes to the developers.
|
||||
<li>Participate in code audit :)
|
||||
</ul>
|
||||
|
||||
@ -209,6 +209,16 @@ sending side and expects the answer, but it keeps the socket of the side which
|
||||
has already closed the connection in CLOSE_WAIT state. Use -C to close both
|
||||
sockets as soon as any of the sides closes the connection, -C1 to request
|
||||
the default behaviour explicitly.
|
||||
.br
|
||||
.B -U\fI[MODE]\fR
|
||||
(for socks) what to do when the destination changes within an UDP association:
|
||||
\fB1\fR - log it, \fB2\fR - authorize it, \fB3\fR - both, the default,
|
||||
\fB0\fR (same as bare \fB-U\fR) - neither. The destination of the first datagram
|
||||
is always authorized and logged. With \fB-U0\fR and \fB-U1\fR the destination is
|
||||
not authorized again, so ACLs limiting the destination only apply to the first
|
||||
destination of the association, and the parent proxy and the external address
|
||||
selected for it are kept for the whole association. Use \fB-U2\fR to keep the
|
||||
authorization and to drop the per-destination log records only.
|
||||
.br
|
||||
(for dnspr) simple, do not use resolver and 3proxy cache, always use external DNS server.
|
||||
.br
|
||||
@ -638,6 +648,13 @@ SOCKSv5, FTP, POP3 and HTTP proxy.
|
||||
authorization result is also cached and not re-evaluated on each request. Faster
|
||||
than \fBcache\fR, but ACL changes do not take effect for cached users until the
|
||||
cache entry expires. Use \fBcache\fR if ACLs may change during the cache lifetime.
|
||||
Because the ACL is not walked on a cache hit, no redirection is applied to a
|
||||
request served from the cache: it is sent directly instead of through the
|
||||
\fBparent\fR proxy of the matching ACL entry, with no error reported. Do not use
|
||||
\fBcacheacl\fR together with parent proxies, use \fBcache\fR instead. The only
|
||||
redirection type which survives is \fBextip\fR, and only if \fBext\fR is a part
|
||||
of the \fBauthcache\fR key, because the external address is then restored from
|
||||
the cache entry.
|
||||
.br
|
||||
\fBradius\fR - authentication with RADIUS.
|
||||
.br
|
||||
@ -693,6 +710,17 @@ assigned to the same user without actual authentication.
|
||||
.br
|
||||
Multiple types can be combined (e.g. \fBip,user,dstaddr,dstport\fR).
|
||||
.br
|
||||
With \fBcacheacl\fR the ACL result is not re-evaluated on a cache hit, so every
|
||||
field the ACL limits must be a part of the cache key, otherwise the result
|
||||
cached for one request is applied to a different one: \fBuser\fR/\fBpass\fR
|
||||
for the userlist, \fBip\fR for the sourcelist, \fBdstaddr\fR/\fBdsthost\fR
|
||||
for the targetlist, \fBdstport\fR for the targetportlist and \fBdstoper\fR
|
||||
for the operationlist. It is especially important for SOCKSv5 UDP traffic,
|
||||
where the destination of every datagram is authorized: without \fBdstaddr\fR
|
||||
and \fBdstport\fR in the key the result cached for one destination is applied
|
||||
to any other destination and the destination is not checked at all, see
|
||||
\fBallow\fR.
|
||||
.br
|
||||
Use auth type \fBcache\fR (or \fBcacheacl\fR) for cached authentication
|
||||
|
||||
.br
|
||||
@ -777,6 +805,39 @@ non-privileged (1024-65535) ports on the remote side.
|
||||
.br
|
||||
Timeperiodlists is a list of time
|
||||
periods in HH:MM:SS-HH:MM:SS format. For example, 00:00:00-08:00:00,17:00:00-24:00:00 lists non-working hours.
|
||||
.br
|
||||
A SOCKSv5 UDP ASSOCIATE request carries no destination, the destination of
|
||||
every datagram is taken from the datagram itself. 3proxy authorizes the
|
||||
destination of every datagram, so ACLs limiting the destination address, the
|
||||
destination host name or the destination port do apply to UDP traffic, unless
|
||||
the \fB-U\fR option of the \fBsocks\fR service says otherwise.
|
||||
.br
|
||||
ACLs are only checked for the datagrams sent by the client. A datagram received
|
||||
on the association is relayed to the client without an ACL check: with a parent
|
||||
proxy only the datagrams from the parent are accepted, an association without a
|
||||
parent accepts a datagram from any source. The source reported to the client is
|
||||
the actual source of the datagram. The address in the log record is the destination the
|
||||
client sent the datagrams to, the source of the datagrams received on the
|
||||
association is not logged.
|
||||
.br
|
||||
A client may use a single association to reach many destinations. An ACL entry
|
||||
which limits the destination silently drops the datagrams to every other
|
||||
destination, so limiting the destination of UDP traffic can break such clients
|
||||
in a way which is hard to diagnose. Limit it only when it is really required.
|
||||
.br
|
||||
Bandwidth limiters (\fBbandlimin\fR, \fBbandlimout\fR) and traffic counters
|
||||
(\fBcountin\fR, \fBcountout\fR) which limit the destination are selected when
|
||||
the destination is authorized, so within an UDP association they are selected
|
||||
for the destination of the first datagram and are not re-selected for every
|
||||
following destination. They can not be applied per destination to the incoming
|
||||
UDP traffic at all, because an incoming datagram is not matched against any
|
||||
destination.
|
||||
.br
|
||||
Only \fBsocks5\fR and \fBsocks5+\fR parents can be used for UDP. Because
|
||||
every new destination is authorized, use \fBauthcache\fR, especially with
|
||||
\'strong\', RADIUS or plugin authentication: \fBcacheacl\fR with the
|
||||
destination in the cache key if no parent proxy is used, \fBcache\fR
|
||||
otherwise, see \fBcacheacl\fR.
|
||||
|
||||
.br
|
||||
.BR parent
|
||||
|
||||
20
src/acl.c
20
src/acl.c
@ -36,6 +36,8 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
||||
struct hostname * hstentry=NULL;
|
||||
int i;
|
||||
int match = 0;
|
||||
int dstdep = 0;
|
||||
int preauth = (param->preauth == 1 && acentry->action <= REDIRECT);
|
||||
|
||||
username = param->username?param->username:(unsigned char *)"-";
|
||||
if(acentry->src) {
|
||||
@ -45,7 +47,10 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
||||
}
|
||||
if(!ipentry) return 0;
|
||||
}
|
||||
if((acentry->dst && (!SAISNULL(¶m->req) || param->operation==BIND)) || (acentry->dstnames && param->hostname)) {
|
||||
if(preauth && (acentry->dst || acentry->dstnames)) {
|
||||
dstdep = 1;
|
||||
}
|
||||
else if((acentry->dst && (!SAISNULL(¶m->req) || param->operation==BIND)) || (acentry->dstnames && param->hostname)) {
|
||||
for(ipentry = acentry->dst; ipentry; ipentry = ipentry->next)
|
||||
if(IPInentry((struct sockaddr *)¶m->req, ipentry)) {
|
||||
break;
|
||||
@ -93,7 +98,10 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
||||
}
|
||||
if(!ipentry && !hstentry) return 0;
|
||||
}
|
||||
if(acentry->ports && (*SAPORT(¶m->req) || param->operation == BIND)) {
|
||||
if(preauth && acentry->ports) {
|
||||
dstdep = 1;
|
||||
}
|
||||
else if(acentry->ports && (*SAPORT(¶m->req) || param->operation == BIND)) {
|
||||
for (portentry = acentry->ports; portentry; portentry = portentry->next)
|
||||
if(ntohs(*SAPORT(¶m->req)) >= portentry->startport &&
|
||||
ntohs(*SAPORT(¶m->req)) <= portentry->endport) {
|
||||
@ -125,6 +133,10 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
||||
}
|
||||
}
|
||||
if(acentry->weight && (acentry->weight < param->weight)) return 0;
|
||||
if(dstdep) {
|
||||
param->dstindep = 0;
|
||||
return acentry->action != DENY;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -132,6 +144,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
||||
int checkACL(struct clientparam * param){
|
||||
struct ace* acentry;
|
||||
|
||||
if(param->preauth == 1) param->dstindep = 1;
|
||||
if(!param->srv->acl) {
|
||||
return 0;
|
||||
}
|
||||
@ -150,6 +163,8 @@ int checkACL(struct clientparam * param){
|
||||
if(param->redirected && acentry->chains && SAISNULL(&acentry->chains->addr) && !*SAPORT(&acentry->chains->addr)) {
|
||||
continue;
|
||||
}
|
||||
param->lastace = acentry;
|
||||
if(param->preauth) return 2;
|
||||
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
|
||||
return 0;
|
||||
}
|
||||
@ -166,6 +181,7 @@ int checkACL(struct clientparam * param){
|
||||
}
|
||||
return res;
|
||||
}
|
||||
param->lastace = acentry;
|
||||
return acentry->action;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ int cacheauth(struct clientparam * param){
|
||||
*(SAFAMILY(¶m->sinsl)) = ac.sinsl_family;
|
||||
memcpy(SAADDR(¶m->sinsl), ac.sinsl_addr, SAADDRLEN(¶m->sinsl));
|
||||
}
|
||||
if(param->preauth == 1 && param->srv->acl) param->dstindep = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -110,6 +111,7 @@ int doauth(struct clientparam * param){
|
||||
struct auth *authfuncs;
|
||||
int ret = 0;
|
||||
|
||||
if(param->preauth == 1 && !param->srv->acl) param->dstindep = 1;
|
||||
for(authfuncs=param->srv->authfuncs; authfuncs; authfuncs=authfuncs->next){
|
||||
res = authfuncs->authenticate?(*authfuncs->authenticate)(param):0;
|
||||
if(!res) {
|
||||
@ -148,7 +150,7 @@ int doauth(struct clientparam * param){
|
||||
if(ret > 9) return ret;
|
||||
}
|
||||
if(!res){
|
||||
ret = alwaysauth(param);
|
||||
ret = (param->preauth == 2)? 0 : alwaysauth(param);
|
||||
if (param->afterauthfilters){
|
||||
FILTER_ACTION action;
|
||||
|
||||
|
||||
@ -9,6 +9,15 @@
|
||||
|
||||
|
||||
#include "proxy.h"
|
||||
#ifdef __linux__
|
||||
#include <sched.h>
|
||||
|
||||
int switch_ns(struct srvparam *srv, int target_fd) {
|
||||
if(target_fd < 0) return 0;
|
||||
if(srv->saved_nsfd >= 0 && setns(srv->saved_nsfd, CLONE_NEWNET)) return -1;
|
||||
return setns(target_fd, CLONE_NEWNET);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
char * copyright = COPYRIGHT;
|
||||
|
||||
@ -204,6 +204,10 @@ extern int timeouts[12];
|
||||
|
||||
int sockmap(struct clientparam * param, int timeo, int usesplice);
|
||||
int udpsockmap(struct clientparam * param, int timeo);
|
||||
int udpbind(struct clientparam * param);
|
||||
#ifdef __linux__
|
||||
int switch_ns(struct srvparam *srv, int target_fd);
|
||||
#endif
|
||||
int socksend(struct clientparam *param, SOCKET sock, unsigned char * buf, int bufsize, int to);
|
||||
int socksendto(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
|
||||
int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
|
||||
|
||||
@ -331,6 +331,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
" -s Use splice() - no filtering for data, off by default\n"
|
||||
" -C connection closed by any of the sides terminates the session, by default\n"
|
||||
" the session is kept until both sides close it (TCP half-close)\n"
|
||||
" -U(0-3) (for socks) what to do when the destination changes within an UDP\n"
|
||||
" association: 1 - log, 2 - authorize, 3 - both (default), 0 (same as -U) -\n"
|
||||
" neither. The first destination is always authorized and logged\n"
|
||||
#endif
|
||||
"-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n"
|
||||
" -fFORMAT logging format (see documentation)\n"
|
||||
@ -601,6 +604,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
case 'C':
|
||||
srv.halfclose = *(argv[i]+2)? atoi(argv[i]+2) : 0;
|
||||
break;
|
||||
case 'U':
|
||||
srv.udpauth = *(argv[i]+2)? atoi(argv[i]+2) : 0;
|
||||
break;
|
||||
case 's':
|
||||
#ifdef WITHSPLICE
|
||||
if(isudp || srv.service == S_ADMIN)
|
||||
@ -1253,6 +1259,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){
|
||||
srv->usesplice = 0;
|
||||
#endif
|
||||
srv->halfclose = 1;
|
||||
srv->udpauth = 3;
|
||||
memset(param, 0, sizeof(struct clientparam));
|
||||
param->srv = srv;
|
||||
param->version = srv->version;
|
||||
@ -1355,7 +1362,6 @@ void srvfree(struct srvparam * srv){
|
||||
|
||||
|
||||
void freeparam(struct clientparam * param) {
|
||||
if(param->res == 2) return;
|
||||
if(param->srv){
|
||||
if(param->srv->so.freefunc) param->srv->so.freefunc(param->sostate);
|
||||
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||
|
||||
32
src/socks.c
32
src/socks.c
@ -7,15 +7,6 @@
|
||||
*/
|
||||
|
||||
#include "proxy.h"
|
||||
#ifdef __linux__
|
||||
#include <sched.h>
|
||||
|
||||
static int switch_ns(struct srvparam *srv, int target_fd) {
|
||||
if(target_fd < 0) return 0;
|
||||
if(srv->saved_nsfd >= 0 && setns(srv->saved_nsfd, CLONE_NEWNET)) return -1;
|
||||
return setns(target_fd, CLONE_NEWNET);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
||||
|
||||
@ -219,7 +210,11 @@ void * sockschild(struct clientparam* param) {
|
||||
RETURN(997);
|
||||
}
|
||||
|
||||
if((res = (*param->srv->authfunc)(param))) {
|
||||
if(command == 3) param->preauth = 1;
|
||||
res = (*param->srv->authfunc)(param);
|
||||
param->preauth = 0;
|
||||
if(command == 3 && res == 2) res = 0;
|
||||
if(res) {
|
||||
RETURN(res);
|
||||
}
|
||||
|
||||
@ -238,18 +233,9 @@ void * sockschild(struct clientparam* param) {
|
||||
#endif
|
||||
|
||||
if(command == 3) {
|
||||
#ifdef __linux__
|
||||
if(switch_ns(param->srv, param->srv->o_nsfd)) {RETURN(11);}
|
||||
#endif
|
||||
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->req), SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {RETURN (11);}
|
||||
#ifdef _WIN32
|
||||
{ unsigned long ul = 1; ioctlsocket(param->remsock, FIONBIO, &ul); }
|
||||
#else
|
||||
fcntl(param->remsock, F_SETFL, O_NONBLOCK | fcntl(param->remsock, F_GETFL));
|
||||
#endif
|
||||
if((res = udpbind(param))) {RETURN(res);}
|
||||
}
|
||||
|
||||
if(command > 1) {
|
||||
else if(command == 2) {
|
||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) {
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12);
|
||||
@ -260,7 +246,8 @@ fflush(stderr);
|
||||
}
|
||||
sasize = SASIZE(¶m->sinsl);
|
||||
param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsl, &sasize);
|
||||
if(command == 3) {
|
||||
}
|
||||
if(command == 3) {
|
||||
param->ctrlsock = param->clisock;
|
||||
#ifdef __linux__
|
||||
if(switch_ns(param->srv, param->srv->i_nsfd)) {RETURN(11);}
|
||||
@ -283,7 +270,6 @@ fprintf(stderr, "%hu binded to communicate with client\n",
|
||||
);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
param->res = 0;
|
||||
|
||||
|
||||
@ -552,6 +552,7 @@ struct srvparam {
|
||||
int usesplice;
|
||||
#endif
|
||||
int halfclose;
|
||||
int udpauth;
|
||||
unsigned bufsize;
|
||||
unsigned authcachetype, authcachetime;
|
||||
unsigned logdumpsrv, logdumpcli;
|
||||
@ -644,7 +645,9 @@ struct clientparam {
|
||||
paused,
|
||||
version,
|
||||
connlim,
|
||||
predatdone;
|
||||
predatdone,
|
||||
preauth,
|
||||
dstindep;
|
||||
|
||||
unsigned char *hostname,
|
||||
*username,
|
||||
@ -679,6 +682,7 @@ struct clientparam {
|
||||
|
||||
PROXYSOCKADDRTYPE udp_relay[3];
|
||||
int udp_nhops;
|
||||
struct ace *lastace;
|
||||
time_t time_start;
|
||||
};
|
||||
|
||||
|
||||
17
src/udppm.c
17
src/udppm.c
@ -36,26 +36,19 @@ void * udppmchild(struct clientparam* param) {
|
||||
int i;
|
||||
int len = 0;
|
||||
|
||||
if(parsehostname((char *)param->srv->target, param, ntohs(param->srv->targetport))) { RETURN(201) }
|
||||
if(parsehostname((char *)param->srv->target, param, ntohs(param->srv->targetport))) { RETURN(100) }
|
||||
|
||||
#ifndef NOIPV6
|
||||
memcpy(¶m->sinsl, *SAFAMILY(¶m->req) == AF_INET6 ? (struct sockaddr *)¶m->srv->extsa6 : (struct sockaddr *)¶m->srv->extsa, SASIZE(¶m->req));
|
||||
param->sinsl = *SAFAMILY(¶m->req) == AF_INET6? param->srv->extsa6 : param->srv->extsa;
|
||||
#else
|
||||
memcpy(¶m->sinsl, (struct sockaddr *)¶m->srv->extsa, SASIZE(¶m->req));
|
||||
param->sinsl = param->srv->extsa;
|
||||
#endif
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
param->remsock = param->srv->so._socket(param->srv->so.state, SASOCK(¶m->sinsl), SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(param->remsock == INVALID_SOCKET) { RETURN(202); }
|
||||
if(param->srv->so._bind(param->srv->so.state, param->remsock, (struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) { RETURN(203); }
|
||||
#ifdef _WIN32
|
||||
{ unsigned long ul2 = 1; ioctlsocket(param->remsock, FIONBIO, &ul2); }
|
||||
#else
|
||||
fcntl(param->remsock, F_SETFL, O_NONBLOCK | fcntl(param->remsock, F_GETFL));
|
||||
#endif
|
||||
memcpy(¶m->sinsr, ¶m->req, sizeof(param->req));
|
||||
param->sinsr = param->req;
|
||||
param->operation = UDPASSOC;
|
||||
authres = (*param->srv->authfunc)(param);
|
||||
if(authres) { RETURN(authres); }
|
||||
if((authres = udpbind(param))) { RETURN(authres); }
|
||||
if(!param->srv->s_option)hashadd(&udp_table, param, ¶m, MAX_COUNTER_TIME);
|
||||
if(!param->srvbuf){
|
||||
if(!(param->srvbuf = malloc(UDPBUFSIZE)))RETURN(11);
|
||||
|
||||
352
src/udpsockmap.c
352
src/udpsockmap.c
@ -8,6 +8,10 @@
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
/* space reserved in front of the received datagram to prepend the headers
|
||||
of the second and the third hop of a SOCKS5 UDP chain */
|
||||
#define UDPHDROFF 48
|
||||
|
||||
int socks5_udp_build_hdr(unsigned char *buf, PROXYSOCKADDRTYPE *addr)
|
||||
{
|
||||
buf[0] = buf[1] = buf[2] = 0;
|
||||
@ -48,69 +52,192 @@ static int socks5_udp_skip_hdr(unsigned char *buf, int len)
|
||||
* param->srv->s_option non-zero: return after first datagram sent to client
|
||||
* param->ctrlsock TCP control socket from the client; INVALID_SOCKET if none.
|
||||
*/
|
||||
|
||||
struct udppoll {
|
||||
struct pollfd fds[4];
|
||||
int nfds;
|
||||
int cli, ctrl, ctrlsrv;
|
||||
};
|
||||
|
||||
static void udpfds(struct clientparam *param, struct udppoll *p)
|
||||
{
|
||||
memset(p->fds, 0, sizeof(p->fds));
|
||||
p->nfds = 0;
|
||||
p->cli = p->ctrl = p->ctrlsrv = -1;
|
||||
|
||||
p->fds[p->nfds].fd = param->remsock; /* always index 0 */
|
||||
p->fds[p->nfds].events = POLLIN;
|
||||
p->nfds++;
|
||||
|
||||
if (!param->waitserver64) {
|
||||
p->fds[p->nfds].fd = param->clisock;
|
||||
p->fds[p->nfds].events = POLLIN;
|
||||
p->cli = p->nfds++;
|
||||
}
|
||||
if (param->ctrlsock != INVALID_SOCKET) {
|
||||
p->fds[p->nfds].fd = param->ctrlsock;
|
||||
p->fds[p->nfds].events = POLLIN;
|
||||
p->ctrl = p->nfds++;
|
||||
}
|
||||
if (param->ctrlsocksrv != INVALID_SOCKET) {
|
||||
p->fds[p->nfds].fd = param->ctrlsocksrv;
|
||||
p->fds[p->nfds].events = POLLIN;
|
||||
p->ctrlsrv = p->nfds++;
|
||||
}
|
||||
}
|
||||
|
||||
static void udplog(struct clientparam *param)
|
||||
{
|
||||
unsigned char buf[400];
|
||||
int len;
|
||||
|
||||
if(!param->srv->logfunc) return;
|
||||
len = sprintf((char *)buf, "UDPMAP ");
|
||||
if(param->hostname) len += sprintf((char *)buf + len, "%.256s", param->hostname);
|
||||
else len += myinet_ntop(*SAFAMILY(¶m->req), SAADDR(¶m->req), (char *)buf + len, 64);
|
||||
sprintf((char *)buf + len, ":%hu", ntohs(*SAPORT(¶m->req)));
|
||||
param->srv->logfunc(param, buf);
|
||||
}
|
||||
|
||||
int udpbind(struct clientparam *param)
|
||||
{
|
||||
SOCKET s;
|
||||
SASIZETYPE sasize;
|
||||
|
||||
#ifdef __linux__
|
||||
if (switch_ns(param->srv, param->srv->o_nsfd)) return 11;
|
||||
#endif
|
||||
s = param->srv->so._socket(param->sostate, SASOCK(¶m->sinsl), SOCK_DGRAM, IPPROTO_UDP);
|
||||
#ifdef __linux__
|
||||
if (switch_ns(param->srv, param->srv->i_nsfd)) {
|
||||
if (s != INVALID_SOCKET) param->srv->so._closesocket(param->sostate, s);
|
||||
return 11;
|
||||
}
|
||||
#endif
|
||||
if (s == INVALID_SOCKET) return 11;
|
||||
#ifdef _WIN32
|
||||
{ unsigned long ul = 1; ioctlsocket(s, FIONBIO, &ul); }
|
||||
#else
|
||||
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
|
||||
#endif
|
||||
param->remsock = s;
|
||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
sasize = SASIZE(¶m->sinsl);
|
||||
param->srv->so._getsockname(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, &sasize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void udpreset(struct clientparam *param)
|
||||
{
|
||||
param->redirected = 0;
|
||||
param->udp_nhops = 0;
|
||||
memset(param->udp_relay, 0, sizeof(param->udp_relay));
|
||||
#ifndef NOIPV6
|
||||
param->sinsl = *SAFAMILY(¶m->req) == AF_INET6? param->srv->extsa6 : param->srv->extsa;
|
||||
#else
|
||||
param->sinsl = param->srv->extsa;
|
||||
#endif
|
||||
param->sinsr = param->req;
|
||||
}
|
||||
|
||||
/*
|
||||
* Authorize param->req and (re)build the server side of the association:
|
||||
* a parent proxy chain and/or an external address may be selected by the ACL.
|
||||
* Returns the authorization result. param->remsock is INVALID_SOCKET if the
|
||||
* socket can not be created, in this case the association can not continue.
|
||||
*/
|
||||
static int udpreconnect(struct clientparam *param)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (param->ctrlsocksrv != INVALID_SOCKET) {
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
|
||||
param->ctrlsocksrv = INVALID_SOCKET;
|
||||
}
|
||||
if (param->remsock != INVALID_SOCKET) {
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
}
|
||||
udpreset(param);
|
||||
res = (*param->srv->authfunc)(param);
|
||||
if (res) {
|
||||
if (param->ctrlsocksrv != INVALID_SOCKET) {
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
|
||||
param->ctrlsocksrv = INVALID_SOCKET;
|
||||
}
|
||||
if (param->remsock != INVALID_SOCKET) {
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
}
|
||||
udpreset(param);
|
||||
}
|
||||
if (udpbind(param)) return res? res : 11;
|
||||
return res;
|
||||
}
|
||||
|
||||
int udpsockmap(struct clientparam *param, int timeo)
|
||||
{
|
||||
PROXYSOCKADDRTYPE sin;
|
||||
PROXYSOCKADDRTYPE cliaddr;
|
||||
PROXYSOCKADDRTYPE from;
|
||||
struct pollfd fds[4];
|
||||
PROXYSOCKADDRTYPE lastdst;
|
||||
struct udppoll p;
|
||||
SASIZETYPE sasize;
|
||||
int len, res, nfds;
|
||||
int nhops = param->udp_nhops;
|
||||
int clisock_idx = -1, ctrlsock_idx = -1, ctrlsocksrv_idx = -1;
|
||||
int len, res, nhops;
|
||||
int firstpacket = 1;
|
||||
int havedst = 0, lastres = 0;
|
||||
char lastname[256] = "";
|
||||
struct ace *lastace = NULL;
|
||||
|
||||
if(param->srv->service == S_UDPPM) nhops++;
|
||||
if (param->srvbufsize < UDPBUFSIZE) {
|
||||
unsigned char *newbuf = realloc(param->srvbuf, UDPBUFSIZE);
|
||||
if (!newbuf) return 21;
|
||||
param->srvbuf = newbuf;
|
||||
param->srvbufsize = UDPBUFSIZE;
|
||||
}
|
||||
sin = param->sincr;
|
||||
|
||||
/* Build poll array once — sockets don't change across iterations */
|
||||
nfds = 0;
|
||||
fds[nfds].fd = param->remsock; /* always index 0 */
|
||||
fds[nfds].events = POLLIN;
|
||||
nfds++;
|
||||
|
||||
if (!param->waitserver64) {
|
||||
fds[nfds].fd = param->clisock;
|
||||
fds[nfds].events = POLLIN;
|
||||
clisock_idx = nfds++;
|
||||
cliaddr = param->sincr;
|
||||
if(param->ctrlsock != INVALID_SOCKET){
|
||||
sasize = sizeof(cliaddr);
|
||||
param->srv->so._getpeername(param->sostate, param->ctrlsock, (struct sockaddr *)&cliaddr, &sasize);
|
||||
}
|
||||
sin = cliaddr;
|
||||
|
||||
if (param->ctrlsock != INVALID_SOCKET) {
|
||||
fds[nfds].fd = param->ctrlsock;
|
||||
fds[nfds].events = POLLIN;
|
||||
ctrlsock_idx = nfds++;
|
||||
}
|
||||
|
||||
if (param->ctrlsocksrv != INVALID_SOCKET) {
|
||||
fds[nfds].fd = param->ctrlsocksrv;
|
||||
fds[nfds].events = POLLIN;
|
||||
ctrlsocksrv_idx = nfds++;
|
||||
}
|
||||
nhops = param->udp_nhops;
|
||||
if(param->srv->service == S_UDPPM) nhops++;
|
||||
udpfds(param, &p);
|
||||
|
||||
for (;;) {
|
||||
res = param->srv->so._poll(param->sostate, fds, nfds, timeo * 1000);
|
||||
res = param->srv->so._poll(param->sostate, p.fds, p.nfds, timeo * 1000);
|
||||
if (res < 0) return 481;
|
||||
if (res == 0) return 92;
|
||||
|
||||
/* datagram from client */
|
||||
if (clisock_idx >= 0 && fds[clisock_idx].revents) {
|
||||
int recvoff = 0, k;
|
||||
if (p.cli >= 0 && p.fds[p.cli].revents) {
|
||||
unsigned char *base = param->srvbuf + UDPHDROFF;
|
||||
PROXYSOCKADDRTYPE dst;
|
||||
char dstnamebuf[256];
|
||||
char *dstname = NULL;
|
||||
int i, k, w, off;
|
||||
|
||||
sasize = sizeof(sin);
|
||||
for (k = 1; k < nhops; k++)
|
||||
recvoff += 4 + (int)SAADDRLEN(¶m->udp_relay[k]) + 2;
|
||||
len = param->srv->so._recvfrom(param->sostate, param->clisock,
|
||||
(char *)param->srvbuf + recvoff, UDPBUFSIZE - recvoff,
|
||||
(char *)base, UDPBUFSIZE - UDPHDROFF,
|
||||
0, (struct sockaddr *)&sin, &sasize);
|
||||
if (len < 0 && (errno == EAGAIN || errno == EINTR)) continue;
|
||||
if (len <= 0) return 482;
|
||||
|
||||
if (SAADDRLEN(&sin) != SAADDRLEN(¶m->sincr) ||
|
||||
memcmp(SAADDR(&sin), SAADDR(¶m->sincr), SAADDRLEN(&sin)))
|
||||
if (SAADDRLEN(&sin) != SAADDRLEN(&cliaddr) ||
|
||||
memcmp(SAADDR(&sin), SAADDR(&cliaddr), SAADDRLEN(&sin)))
|
||||
continue;
|
||||
if (firstpacket) {
|
||||
if (!SAISNULL(¶m->req) && *SAPORT(¶m->req) &&
|
||||
@ -118,70 +245,129 @@ int udpsockmap(struct clientparam *param, int timeo)
|
||||
!memcmp(SAADDR(¶m->req), SAADDR(&sin), SAADDRLEN(¶m->req)) &&
|
||||
memcmp(SAPORT(¶m->req), SAPORT(&sin), 2))
|
||||
continue;
|
||||
param->sincr = sin;
|
||||
cliaddr = sin;
|
||||
firstpacket = 0;
|
||||
} else if (memcmp(SAPORT(&sin), SAPORT(¶m->sincr), 2)) {
|
||||
} else if (memcmp(SAPORT(&sin), SAPORT(&cliaddr), 2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(param->bandlimfunc && (*param->bandlimfunc)(param, 0, len)) continue;
|
||||
|
||||
if (nhops == 0) {
|
||||
int i;
|
||||
if (len < 10 || param->srvbuf[0] || param->srvbuf[1] || param->srvbuf[2])
|
||||
return 483;
|
||||
switch (param->srvbuf[3]) {
|
||||
case 1:
|
||||
*SAFAMILY(¶m->sinsr) = AF_INET;
|
||||
memcpy(SAADDR(¶m->sinsr), param->srvbuf + 4, 4);
|
||||
i = 8;
|
||||
break;
|
||||
case 4:
|
||||
if (len < 22) return 484;
|
||||
*SAFAMILY(¶m->sinsr) = AF_INET6;
|
||||
memcpy(SAADDR(¶m->sinsr), param->srvbuf + 4, 16);
|
||||
i = 20;
|
||||
break;
|
||||
case 3: {
|
||||
int sz = param->srvbuf[4], j;
|
||||
if (len < 7 + sz) return 485;
|
||||
for (j = 4; j < 4 + sz; j++) param->srvbuf[j] = param->srvbuf[j + 1];
|
||||
param->srvbuf[4 + sz] = 0;
|
||||
i = 5 + sz;
|
||||
if (!getip46(param->srv->family, param->srvbuf + 4,
|
||||
(struct sockaddr *)¶m->sinsr))
|
||||
return 100;
|
||||
break;
|
||||
if (len < 10 || base[0] || base[1] || base[2]) return 483;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
switch (base[3]) {
|
||||
case 1:
|
||||
*SAFAMILY(&dst) = AF_INET;
|
||||
memcpy(SAADDR(&dst), base + 4, 4);
|
||||
i = 8;
|
||||
break;
|
||||
case 4:
|
||||
if (len < 22) return 484;
|
||||
*SAFAMILY(&dst) = AF_INET6;
|
||||
memcpy(SAADDR(&dst), base + 4, 16);
|
||||
i = 20;
|
||||
break;
|
||||
case 3: {
|
||||
int sz = base[4];
|
||||
if (len < 7 + sz) return 485;
|
||||
memcpy(dstnamebuf, base + 5, sz);
|
||||
dstnamebuf[sz] = 0;
|
||||
dstname = dstnamebuf;
|
||||
i = 5 + sz;
|
||||
if (!getip46(param->srv->family, (unsigned char *)dstnamebuf,
|
||||
(struct sockaddr *)&dst)) {
|
||||
if (!nhops) return 100;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
*SAFAMILY(&dst) = AF_INET;
|
||||
}
|
||||
default: return 997;
|
||||
break;
|
||||
}
|
||||
default: return 997;
|
||||
}
|
||||
memcpy(SAPORT(&dst), base + i, 2);
|
||||
i += 2;
|
||||
|
||||
if (!havedst
|
||||
|| SAADDRLEN(&lastdst) != SAADDRLEN(&dst)
|
||||
|| memcmp(SAADDR(&lastdst), SAADDR(&dst), SAADDRLEN(&dst))
|
||||
|| memcmp(SAPORT(&lastdst), SAPORT(&dst), 2)
|
||||
|| strncmp(lastname, dstname? dstname : "", sizeof(lastname) - 1)) {
|
||||
int ares = 0, reconnect = 0;
|
||||
|
||||
if ((param->srv->udpauth & 1) && havedst && !lastres) {
|
||||
PROXYSOCKADDRTYPE newdst = dst;
|
||||
|
||||
param->req = lastdst;
|
||||
if (param->hostname) free(param->hostname);
|
||||
param->hostname = *lastname? (unsigned char *)strdup(lastname) : NULL;
|
||||
udplog(param);
|
||||
dst = newdst;
|
||||
}
|
||||
memcpy(SAPORT(¶m->sinsr), param->srvbuf + i, 2);
|
||||
i += 2;
|
||||
if (param->hostname) free(param->hostname);
|
||||
param->hostname = dstname? (unsigned char *)strdup(dstname) : NULL;
|
||||
param->req = dst;
|
||||
if (!nhops) param->sinsr = dst;
|
||||
|
||||
if (!havedst) reconnect = 1;
|
||||
else if ((param->srv->udpauth & 2) && !param->dstindep) {
|
||||
param->preauth = 2;
|
||||
ares = (*param->srv->authfunc)(param);
|
||||
param->preauth = 0;
|
||||
if (ares == 2) {
|
||||
if (param->lastace != lastace) reconnect = 1;
|
||||
ares = 0;
|
||||
}
|
||||
else if (!ares && (param->redirected ||
|
||||
(lastace && lastace->chains))) reconnect = 1;
|
||||
}
|
||||
if (reconnect) {
|
||||
ares = udpreconnect(param);
|
||||
if (param->remsock == INVALID_SOCKET) return ares;
|
||||
nhops = param->udp_nhops;
|
||||
if(param->srv->service == S_UDPPM) nhops++;
|
||||
udpfds(param, &p);
|
||||
}
|
||||
lastres = ares;
|
||||
lastdst = dst;
|
||||
strncpy(lastname, dstname? dstname : "", sizeof(lastname) - 1);
|
||||
lastname[sizeof(lastname) - 1] = 0;
|
||||
havedst = 1;
|
||||
if (!ares) lastace = param->lastace;
|
||||
else if (param->srv->udpauth & 1) {
|
||||
param->res = ares;
|
||||
udplog(param);
|
||||
param->res = 0;
|
||||
}
|
||||
}
|
||||
if (lastres) continue;
|
||||
|
||||
if (!nhops) {
|
||||
if (len > i) {
|
||||
param->srv->so._sendto(param->sostate, param->remsock,
|
||||
(char *)param->srvbuf + i, len - i, 0,
|
||||
(struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr));
|
||||
(char *)base + i, len - i, 0,
|
||||
(struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr));
|
||||
param->statscli64 += (len - i);
|
||||
param->nwrites++;
|
||||
}
|
||||
} else {
|
||||
int off = 0;
|
||||
off = 0;
|
||||
for (k = 1; k < nhops; k++)
|
||||
off += socks5_udp_build_hdr(param->srvbuf + off, ¶m->udp_relay[k]);
|
||||
off += 4 + (int)SAADDRLEN(¶m->udp_relay[k]) + 2;
|
||||
for (k = 1, w = UDPHDROFF - off; k < nhops; k++)
|
||||
w += socks5_udp_build_hdr(param->srvbuf + w, ¶m->udp_relay[k]);
|
||||
param->srv->so._sendto(param->sostate, param->remsock,
|
||||
(char *)param->srvbuf, off + len, 0,
|
||||
(struct sockaddr *)¶m->udp_relay[0], SASIZE(¶m->udp_relay[0]));
|
||||
(char *)param->srvbuf + (UDPHDROFF - off), off + len, 0,
|
||||
(struct sockaddr *)¶m->udp_relay[0], SASIZE(¶m->udp_relay[0]));
|
||||
param->statscli64 += len;
|
||||
param->nwrites++;
|
||||
}
|
||||
}
|
||||
|
||||
/* datagram from server / parent relay */
|
||||
if (fds[0].revents) {
|
||||
int hdrsize = (nhops == 0) ? 4 + (int)SAADDRLEN(¶m->sinsr) + 2 : 0;
|
||||
if (p.fds[0].revents) {
|
||||
int hdrsize = (nhops == 0) ? 4 + (int)SAADDRLEN(¶m->sinsl) + 2 : 0;
|
||||
int sendoff = 0, sendlen;
|
||||
sasize = sizeof(from);
|
||||
if (hdrsize > UDPBUFSIZE) return 468;
|
||||
len = param->srv->so._recvfrom(param->sostate, param->remsock,
|
||||
(char *)param->srvbuf + hdrsize, UDPBUFSIZE - hdrsize, 0,
|
||||
(struct sockaddr *)&from, &sasize);
|
||||
@ -201,9 +387,9 @@ int udpsockmap(struct clientparam *param, int timeo)
|
||||
sendlen = len;
|
||||
if (nhops == 0) {
|
||||
param->srvbuf[0] = param->srvbuf[1] = param->srvbuf[2] = 0;
|
||||
param->srvbuf[3] = (*SAFAMILY(¶m->sinsr) == AF_INET) ? 1 : 4;
|
||||
memcpy(param->srvbuf + 4, SAADDR(¶m->sinsr), SAADDRLEN(¶m->sinsr));
|
||||
memcpy(param->srvbuf + 4 + SAADDRLEN(¶m->sinsr), SAPORT(¶m->sinsr), 2);
|
||||
param->srvbuf[3] = (*SAFAMILY(&from) == AF_INET) ? 1 : 4;
|
||||
memcpy(param->srvbuf + 4, SAADDR(&from), SAADDRLEN(¶m->sinsl));
|
||||
memcpy(param->srvbuf + 4 + SAADDRLEN(¶m->sinsl), SAPORT(&from), 2);
|
||||
sendlen = len + hdrsize;
|
||||
} else if (nhops >= 2) {
|
||||
int off = 0, k;
|
||||
@ -219,13 +405,13 @@ int udpsockmap(struct clientparam *param, int timeo)
|
||||
}
|
||||
if (sendlen > 0)
|
||||
param->srv->so._sendto(param->sostate, param->clisock,
|
||||
(char *)param->srvbuf + sendoff, sendlen, 0,
|
||||
(struct sockaddr *)&sin, SASIZE(&sin));
|
||||
(char *)param->srvbuf + sendoff, sendlen, 0,
|
||||
(struct sockaddr *)&sin, SASIZE(&sin));
|
||||
if (param->srv->s_option && param->srv->service == S_UDPPM) return 0;
|
||||
}
|
||||
|
||||
if ((ctrlsock_idx >= 0 && fds[ctrlsock_idx].revents) ||
|
||||
(ctrlsocksrv_idx >= 0 && fds[ctrlsocksrv_idx].revents)) return 0;
|
||||
if ((p.ctrl >= 0 && p.fds[p.ctrl].revents) ||
|
||||
(p.ctrlsrv >= 0 && p.fds[p.ctrlsrv].revents)) return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user