Closed n8fr8 closed 2 years ago
(also want to talk about long term firestack plans and/or integration of tor-android into firestack/rethink)
Currently thinking we need to integrate this code: https://github.com/celzero/firestack/blob/rdns/intra/dnsproxy/upstream.go
into the current UDP handler: https://github.com/n8fr8/IPtProxy/commit/76d5ece5ef80fe3a231e66122055138bc9871a28
Hi Nathan,
(also want to talk about long term firestack plans and/or integration of tor-android into firestack/rethink)
You've caught us at the wrong time: We're midway through an now-paused rewrite (to be resumed soon), and are leaning towards deprecating go-tun2socks
(as upstream has been archived in favour of leaf which a rust-lang wrapper on tun2socks
, but sure enough, there indeed are bugs in the golang impl that would now linger forever).
Most likely we move to netstack (#3) or drop user-space tcp/ip altogether and transition to being a stateless firewall that operates on individual packets (gopacket) than on protocols: That is, not transparent-proxy anything whatsoever. Or may be, operate firestack in two modes, stateful and stateless... I'd imagine that to be painful to maintain but worth a shot nevertheless (since the stateful bits are already in place).
Any tips, insights, help would be appreciated, and we do also have funding to support paid assistance. Contact me @n8fr8 on irc, matrix, twitter, etc, or nathan@guardianproject.info email.
Sure thing! I'll take a look at IPtProxy
, and get back in a day or so.
Thanks! We love Leaf to and are already using it on iOS with our new Orbot for iOS there. It is working well. However, on Android, it created a huge library dependency on top of our existing go IPtProxy library size, so we opted to try go-tun2socks.
Anyhow, thanks for whatever advice you can provide, and still want to talk about how we can support your important work in any way.
(also want to talk about long term firestack plans and/or integration of tor-android into firestack/rethink)
Hi Nathan, lets catch up over a Signal session or something? I've sent you an email.
Any tips, insights
So, from the handler, all you need to do is proxy the request as-is to whatever udp endpoint is capable of handling DNS (in this case, I assume its localhost:5400
?). Firestack's dnsproxy:upstream.go
and intra:udp.go
is unnecessary for Orbot because unlike Firestack, Orbot only needs to forward ALL udp packets to the loopback, which is relatively straight-forward.
I haven't tested this with Orbot's code to know whether it would work, but I'd wager that this generic 'proxying' handler would work:
// adapted from github.com/Jigsaw-Code/outline-go-tun2socks/blob/11e6e39/shadowsocks/udp.go
package proxy
import (
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/eycorsican/go-tun2socks/core"
)
type udpHandler struct {
sync.Mutex
timeout time.Duration
// maps redirected core.UDPConn to a local proxy-conn that pipes data to upstream
udpConns map[core.UDPConn]*net.UDPConn
// upstream is the target udp server
// app -> tun -> lwip -> this-handler -> upstream
// core.UDPConn <--> proxy-conn <--> upstream
upstream *net.UDPAddr
}
// constructor
func NewUDPHandler(target string, timeout time.Duration) core.UDPConnHandler {
return &udpHandler{
timeout: timeout,
udpConns: make(map[core.UDPConn]*net.UDPConn),
// target is localhost:5400
upstream: net.ResolveUDPAddr("udp", target),
}
}
// drain proxy-conn (connected to upstream) into core.UDPConn (connected to android/app/tun)
func (h *udpHandler) fetchUDPInput(conn core.UDPConn, pc *net.UDPConn) {
buf := core.NewBytes(core.BufSize)
defer func() {
h.Close(conn)
core.FreeBytes(buf)
}()
for {
// extend hole-punch deadline
pc.SetDeadline(time.Now().Add(h.timeout))
// reads data from pc into buf upto n bytes
n, who, err := pc.ReadFrom(buf)
if err != nil { // break
return
}
// write n bytes of buf to downstream conn (tun) with spoofed source, who
// who is usually upstream
_, err = conn.WriteFrom(buf[:n], who)
if err != nil { // break
return
}
}
}
// Connect spoofs core.UDPConn's connection to target via a proxy-conn to upstream
func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error {
// create an unconnected udp socket, this is our proxy-conn
bindAddr := &net.UDPAddr{IP: nil, Port: 0}
pc, err := net.ListenUDP("udp", bindAddr)
if err != nil {
return err
}
h.Lock()
// assign this proxy-conn to this incoming core.UDPConn from android/app (tun)
h.udpConns[conn] = pc
h.Unlock()
// drain proxy-conn (pc) and pipe it to core.UDPConn (tun)
go h.fetchUDPInput(conn, pc)
return nil
}
// ReceiveTo receives data from core.UDPConn and sends it to upstream via
// proxy-conn instead of sending it to addr
func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error {
h.Lock()
pc, ok := h.udpConns[conn]
h.Unlock()
if ok {
_, err := pc.WriteTo(data, h.upstream)
return err // err could be nil
} else {
return errors.New("proxy-conn missing")
}
}
// remove the udp hole-punch mapping between
// core.UDPConn (tun) <-> proxy-conn (this-handler) <-> upstream (localhost:5400)
func (h *udpHandler) Close(conn core.UDPConn) {
conn.Close()
h.Lock()
defer h.Unlock()
if pc, ok := h.udpConns[conn]; ok {
pc.Close()
delete(h.udpConns, conn)
}
}
I would have tested the above with Orbot, but then I came across this commit https://github.com/guardianproject/orbot/commit/416107699fe35b7371c51ac008f9ce598f8d6b1d and figured there's another way you've found to make it all work (: Sorry I slept on this task for a bit more than I should have. If not, let me know in case we should pair program integrate this thing over jitsi/signal session or something.
Just a note: In my reading of the code, it looks like Outline's shadowsocks implementation (1, 2) is more similar to Orbot's integration with eycorsican/go-tun2socks
than Intra's (or RethinkDNS' for that matter, both of which focus on proxying DNS over multiple protocols a bit too much, complicating otherwise simpler code).
(also want to talk about long term firestack plans and/or integration of tor-android into firestack/rethink)
Sent you an email from mz
at celzero
dot com
into the current UDP handler: https://github.com/n8fr8/IPtProxy/commit/76d5ece5ef80fe3a231e66122055138bc9871a28
(I am not sure if this is already known to the Orbot/Tor developers but, I will err on the side of caution and spell it out anyway): This UDP handler responds with a truncated DNS answer for ALL queries. Android netd
would then retry those same queries over TCP. This means, DNS can be handled just as Orbot (go-tun2socks) would handle any TCP request, and no special handling for UDP is required.
@n8fr8
Closing this issue since Orbot DNS is all up and working. Feel free to reach out over email or Signal, I am here ready to volunteer in any which way for Orbot.
Btw, RethinkDNS has moved to gvisor/netstack, and it has been working wonderfully well: https://github.com/celzero/firestack/tree/n2
We are moving Orbot to go-tun2socks (and perhaps eventually firestack), but are having trouble with proxying the DNS to the Tor localhost:5400 service.
Work is happening here: https://github.com/guardianproject/orbot/tree/dev_gotun2socks
and integration with go-tun2socks is here (due to the fact we can only have one golib in the app): https://github.com/n8fr8/IPtProxy/commits/master
Any tips, insights, help would be appreciated, and we do also have funding to support paid assistance. Contact me @n8fr8 on irc, matrix, twitter, etc, or nathan@guardianproject.info email.