ambrop72 / badvpn

NCD scripting language, tun2socks proxifier, P2P VPN
Other
1.86k stars 603 forks source link

How does the UdpGw daemon work? #121

Open iMrDJAi opened 3 years ago

iMrDJAi commented 3 years ago

Hello, I'm trying to simulate the UdpGw layer with JavaScript, I'm stuck trying to figure out how to parse the received buffer and how to structure a response to the client, I need to do that in order to resolve the DNS requests.
I'm not sure how does it work. Is it forwarding all the UDP traffic? or just the DNS requests? Is it forwarding the DNS requests as it is? or just a raw DNS queries meant to be sent to a DNS server? Is the UdpGw daemon a UDP requests forwarder or a local DNS resolver?
I already tried creating a simple UDP server in order to listen to the UDP requests, and that's what I got:

var udp = require('dgram')
var server = udp.createSocket('udp6')

server.on('error', err => {
  error('Error: ' + err)
  server.close()
})
server.on('message', (msg, info) => {
  log('UDP: ' + msg.toString('hex') + '\n(' + msg.toString() + ')')
})
server.bind(1080, () => {
  log(`UDP server started on port 1080!`)
})

/** output:
UDP: 0000000108080808003517b8010000010000000000000377777706676f6f676c6503636f6d0000010001
(5�wwwgooglecom)
**/

We can clearly see the domain "www.google.com" there, but I still not sure how to handle that correctly.

iMrDJAi commented 3 years ago

I found out that the received buffer actually contains a real DNS packet, but I noticed that the first 10 bytes are not a part of it:

console.log(Buffer.from('00000001080808080035', 'hex').toString())
// output => "\u0000\u0000\u0000\u0001\b\b\b\b\u00005"

It seems to be kind of a header, so I just cut it off, then I decoded it with a library called dns-packet:

console.log(dnsPacket.decode(Buffer.from("17b8010000010000000000000377777706676f6f676c6503636f6d0000010001", "hex")))
// output => {"id":6072,"type":"query","flags":256,"flag_qr":false,"opcode":"QUERY","flag_aa":false,"flag_tc":false,"flag_rd":true,"flag_ra":false,"flag_z":false,"flag_ad":false,"flag_cd":false,"rcode":"NOERROR","questions":[{"name":"www.google.com","type":"A","class":"IN"}],"answers":[],"authorities":[],"additionals":[]}

So I used the public Google DNS to resolve the DNS request, using the DNS-over-HTTPS API, then I forwarded the response buffer back to the client:

server.on('message', async (msg, info) => {
    var dnsMessage = msg.slice(10)
    fetch('https://8.8.8.8/dns-query', {
        method: 'POST',
        headers: {
            'content-type': 'application/dns-message',
        },
        body: dnsMessage
    }).then(res => res.buffer().then(buf => {
        server.send(buf, 0, buf.length, info.port, info.address)
    })).catch(err => {
        console.error(err)
    })
})

Now I'm getting an error on the client side:

udp_fd_handler: no address for DNS request id 0

And I'm stuck at this point, not sure what changes should I make to the response buffer before sending it back, @ambrop72 I need you to provide me more info on how to structure the response please.

iMrDJAi commented 3 years ago

I ended up using the actual UDP associate feature of SOCKS v5 (https://github.com/ambrop72/badvpn/pull/71) to forward the DNS traffic. While this has worked fine, I couldn't find out how to re-implement the UdpGw daemon in JavaScript.

I'll keep this issue open for now, maybe someone in the future will provide an answer, in my opinion this remains an interesting topic for the community.