darkk / redsocks

transparent TCP-to-proxy redirector
http://darkk.net.ru/redsocks
3.28k stars 860 forks source link

Feature Request: fake DNS resolver #23

Open ghost opened 12 years ago

ghost commented 12 years ago

Like discussed on the mailing list: http://librelist.com/browser//redsocks/2012/5/15/transparent-proxy-dns-without-public-dns-server/#e8cb0f54932856f1c0cc9259e24cb089

  1. implement a fake DNS resolver
  2. catch DNS request
  3. query the the proxy over TCP (option, because UDP is sometimes blocked) or UDP (option) to do the hostname resolution
  4. give the result back to the fake DNS resolver
johnwchrome commented 12 years ago

This one (tor-dns-proxy.py) can act as dns transport proxy http://monkey.org/~dugsong/dsocks/

maybe someone can port it(python) to c code(redsocks)

if redsocks can act as dns transport proxy, it can be filter/switch to different socks5 server base on *.domain.com name. like the browser's proxy addon/plugin does

darkk commented 12 years ago

There is no point in poring tor-dns-proxy.py to C because it's tor-specific (grep for SOCKS_COMMAND_RESOLVE) and tor can be configured as transparent proxy on its own, so tor does not need redsocks to work.

darkk commented 12 years ago

Another reason to solve the ticket: there are HTTP proxies rejecting CONNECT IP:port request and allowing CONNECT FQDN:port. See http://librelist.com/browser//redsocks/2012/8/6/https-and-modes

RussianNeuroMancer commented 9 years ago

Also this probably could be useful for forwarding connections to i2p proxy.

RoganDawes commented 7 years ago

I realise this is a very old issue, and likely am necromancing this thread. However, I wanted to point out one more use case where this makes sense, in the hope of getting a usable solution (also, while I added this same comment to a different issue, I see now that this one remains open, while the other is closed. Also, this issue is referenced from others on the same topic).

In the course of assessing network applications, one task is often to intercept TCP traffic, analyse/modify it, and send it on. It's easy enough for unencrypted traffic, and redsocks makes it even easier, thank you!

However, intercepting SSL connections is a different beast, as this usually entails acting as either an HTTP CONNECT intercept, or possibly a SOCKS intercept, determining the hostname from the CONNECT request, then generating a matching certificate, and signing it with a private CA. If the private CA cert is installed on the client, then validation is successful, and interception can be achieved.

I recognise that SNI (Server Name Indicator) does make this somewhat easier in the cases where redsocks is purely passing on the IP address of the desired end point, however, not all clients implement SNI (and many intercepting proxies don't either!)

So, being able to configure a DNS implementation with a range of IP addresses (e.g. RFC1918 addresses unused on the client network, or even a random netblock that the clients are unlikely to connect to during legitimate traffic), that redsocks will then use to transform intercepted TCP connections back into "CONNECT hostname:port" requests would be really valuable.

One approach that springs to mind is a custom DNS server implemented as part of the redsocks server, that accepts DNS requests for A and AAAA records, and resolves them from an upstream server, storing the results in an in-memory LRU list of configurable size, that is queried on receiving a TCP connection on port 12345, and translated back into a suitable "CONNECT hostname:port" request. Non-A or AAAA requests can simply be passed through, perhaps, in a first implementation.

Problems with different hostnames resolving to the same IP address can then be addressed at the upstream DNS server (e.g. a dnsmasq instance) by hardcoding name/address mappings where necessary. (Logging a warning might help folks to identify this edge case, and take appropriate action). (Ninja edit: Problems with resolv.conf search orders can also be addressed at the upstream DNS instance, and need not be considered within redsocks itself)

darkk commented 7 years ago

Yep, there are use-cases for fake DNS server to CONNECT to FQDN instead of IP -- to issue fake cert, to conform to proxy server policy, that's true. Maybe I'll o

Non-A or AAAA requests can simply be passed through

Or tunneled through keep-alive connection to some TCP-enabled DNS recursor like 77.8.8.8, 8.8.8.8, 80.80.80.80 or 208.67.222.222. Keeping the connection alive will reduce DNS resolution latency.

RoganDawes commented 7 years ago

To my mind, the DNS tunnelling is orthogonal/unrelated to the matter of redsocks being able to CONNECT FQDN, apart from redsocks extracting A/AAAA answers from the tunnelled responses. Unfortunately, my C coding abilities are inadequate for the task of merging a simple DNS forwarder into the redsocks codebase, and handling the locking required. Otherwise, I'd certainly take a stab at a patch.

My alternative at the moment involves tailing the dnsmasq log output in verbose/extra mode, parsing the results to get the A/AAAA records, then passing those via a TCP connection to the SOCKS server that redsocks connects to, so that the SOCKS server can do the necessary translation. The SOCKS implementation is in Java, which is a language that I can get by in ;-)

RoganDawes commented 7 years ago

Would something like the following be acceptable, as an intermediate solution:

Redsocks opens a named pipe, from which it reads entries in the form: ip.add.re.ss hostname.domain.com

Any subsequent connections to the IP address get requested as if they were to hostname.domain.com?

A blank line may be interpreted as an instruction to drop any cached names, to prevent unbounded growth of the cache. That is probably the simplest solution that would achieve the end goal, within the existing framework of the application.

RoganDawes commented 7 years ago

Looking at how to implement this, it seems that I will have to define a new subsystem, with parser and init/fini etc.

For the moment, I am only looking at implementing it for http-connect, as it seems relatively simple to interpose a lookup in the 'printf("CONNECT %s")', than to implement hostname support for socksX protocols. If it works (and looks acceptable!), I guess it should not be too difficult to add socks support too.

RoganDawes commented 7 years ago

I have started hacking something together, but am struggling with some (probably very basic) issues with reading from a named pipe, using libevent :-( I based my code on https://github.com/libevent/libevent/blob/master/sample/event-read-fifo.c, even though I was unable to get that actual code to compile/link successfully.

If you take a look at:

https://github.com/RoganDawes/redsocks/commit/34f745b1d8d364c155a9d55f3543fce459fd8a98

And add a

rdns { 
  fifo = "/path/to/fifo"; 
}

stanza to your config file, and then write to that fifo, redsocks should simply print out the data received (at this stage, eventually, it will be parsed and added to the hashmap). However, libevent then keeps calling the handler, as though there is more data to read, even when there is not. Any ideas?

RoganDawes commented 7 years ago

Aha! http://libevent-users.monkey.narkive.com/geYbLuFT/callback-firing-many-infinite-times-for-stdin had the explanation, echo was closing the other end of the pipe, and thus libevent was legitimately returning 0, indicating that the pipe was closed. Sigh! The more one learns! ;-)

RoganDawes commented 11 months ago

Think my PR above is still relevant, it could perhaps be implemented as a DNS "resolver" that simply allocates an IP address sequentially from a configured range (e.g. 10.0.0.0/8, or 172.31.0.0/16), and stores that allocation in a local hashmap (size and expiry policy should probably be configurable too). Any connection to an IP address that has an entry in the hashmap gets replaced with the mapped hostname. I guess the hostname->IP mapping would need to be stored as well, otherwise one would allocate a new IP address on every lookup!

Not sure what the best approach is if the IP address is in the configured range, but there is no corresponding entry in the hashmap. This can happen if the entry has been evicted from the hashmap due to policy, or simply the client making a direct connection to the IP. Probably log a warning/error, and either drop the connection, or connect using the IP address in that case.