Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding DNS header #587

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Adding DNS header #587

wants to merge 2 commits into from

Conversation

Cl-He-O
Copy link

@Cl-He-O Cl-He-O commented Mar 17, 2023

Mostly copied from Xray-core's DNS header, useful for bypassing school network's login.

@tobyxdd tobyxdd requested review from tobyxdd and haruue March 17, 2023 23:21
@tobyxdd tobyxdd added the enhancement New feature or request label Mar 17, 2023
@tobyxdd
Copy link
Collaborator

tobyxdd commented Mar 22, 2023

I love the idea of adding a DNS mode but I have some concerns about this implementation - using obfs as the argument for the domain name & removing the actual obfuscation process means that packets will always have a plain text DNS header + QUIC header, which obviously does not seem legitimate...

Maybe we should consider adding a new field in config to provide arguments to fake protocols like this?

@Cl-He-O
Copy link
Author

Cl-He-O commented Mar 23, 2023

It would be better for sure. But I met difficulty in implementing it, the type of function won't match after adding a new parameter...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package dns

import (
	"encoding/binary"
	"math/rand"
	"net"
	"os"
	"sync"
	"syscall"
	"time"

	"github.com/miekg/dns"
)

const udpBufferSize = 4096

type DnsUDPPacketConn struct {
	orig *net.UDPConn

	readBuf   []byte
	writeBuf  []byte
	header    []byte
	readMutex sync.RWMutex
}

func NewDnsUDPConn(orig *net.UDPConn, domain string) *DnsUDPPacketConn {
	header := make([]byte, 0, 512)

	header = append(header, 0, 0)                         // Transaction ID (placeholder)
	header = append(header, 0x01, 0x00)                   // Flags: Standard query
	header = append(header, 0x00, 0x01)                   // Questions
	header = append(header, 0x00, 0x00, 0x00, 0x00)       // Answer RRs, Authority RRs, Additional RRs

	buf := make([]byte, 256)
	off1, err := dns.PackDomainName(dns.Fqdn(domain), buf, 0, nil, false)
	if err != nil {
		return nil
	}

	header = append(header, buf[:off1]...)
	header = append(header, 0x00, 0x01, 0x00, 0x01)       // Type: A, Class: IN

	return &DnsUDPPacketConn{
		orig:     orig,
		readBuf:  make([]byte, udpBufferSize),
		writeBuf: make([]byte, udpBufferSize),
		header:   header,
	}
}

func (c *DnsUDPPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
	for {
		c.readMutex.RLock()
		n, addr, err := c.orig.ReadFrom(c.readBuf)
		if n <= len(c.header) {
			c.readMutex.RUnlock()
			return 0, addr, err
		}

		newN := copy(p, c.readBuf[len(c.header):n])
		c.readMutex.RUnlock()

		if newN > 0 {
			// Valid packet
			return newN, addr, err
		} else if err != nil {
			// Not valid and orig.ReadFrom had some error
			return 0, addr, err
		}
	}
}

func (c *DnsUDPPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
	c.readMutex.Lock()
	defer c.readMutex.Unlock()

	copy(c.writeBuf, c.header)
	binary.BigEndian.PutUint16(c.writeBuf, uint16(rand.Uint32()))

	bn := copy(c.writeBuf[len(c.header):], p)
	_, err = c.orig.WriteTo(c.writeBuf[:len(c.header)+bn], addr)
	if err != nil {
		return 0, err
	}
	return len(p), nil
}

func (c *DnsUDPPacketConn) Close() error {
	return c.orig.Close()
}

func (c *DnsUDPPacketConn) LocalAddr() net.Addr {
	return c.orig.LocalAddr()
}

func (c *DnsUDPPacketConn) SetDeadline(t time.Time) error {
	return c.orig.SetDeadline(t)
}

func (c *DnsUDPPacketConn) SetReadDeadline(t time.Time) error {
	return c.orig.SetReadDeadline(t)
}

func (c *DnsUDPPacketConn) SetWriteDeadline(t time.Time) error {
	return c.orig.SetWriteDeadline(t)
}

func (c *DnsUDPPacketConn) SetReadBuffer(bytes int) error {
	return c.orig.SetReadBuffer(bytes)
}

func (c *DnsUDPPacketConn) SetWriteBuffer(bytes int) error {
	return c.orig.SetWriteBuffer(bytes)
}

func (c *DnsUDPPacketConn) SyscallConn() (syscall.RawConn, error) {
	return c.orig.SyscallConn()
}

func (c *DnsUDPPacketConn) File() (f *os.File, err error) {
	return c.orig.File()
}

The main changes made to the code are:

  1. The readBuf and writeBuf slices are now reused across multiple calls to NewDnsUDPConn, reducing memory allocations.

  2. The sync.Mutex has been replaced with a sync.RWMutex to allow concurrent read operations while protecting write operations.

  3. In the ReadFrom method, the code now directly returns a slice pointing to the data in c.readBuf, avoiding unnecessary copying.

  4. In the WriteTo method, the transaction ID is now directly assigned to the c.writeBuf slice, avoiding the use of binary.BigEndian.PutUint16.

  5. The writeMutex has been removed, and the readMutex is used to protect both read and write operations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants