Files
Xray-core/proxy/wireguard/netstack.go
T

775 lines
20 KiB
Go

/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
*/
package wireguard
import (
"context"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"net"
"net/netip"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/xtls/xray-core/common/buf"
xnet "github.com/xtls/xray-core/common/net"
xtun "github.com/xtls/xray-core/proxy/tun"
"golang.zx2c4.com/wireguard/tun"
"golang.org/x/net/dns/dnsmessage"
)
// netTunQueue is how many packets of the stack can wait to be read from its device
const netTunQueue = 1024
// netTun is the device of a TCP/IP stack of Xray: what is written to it goes to the stack,
// and what the stack sends is read from it
type netTun struct {
stack xtun.Stack
events chan tun.Event
// packets are what the stack has sent and Read has not taken yet
packets chan *buf.Buffer
closed chan struct{}
closeOnce sync.Once
mtu int
}
// CreateNetTUN returns a device with a TCP/IP stack on it, and what opens connections through that from
// localAddresses. With a handler the stack takes every connection that comes from the device, for any address.
// Without one it takes none
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int, handler xtun.ConnectionHandler) (tun.Device, *Net, error) {
dev := &netTun{
events: make(chan tun.Event, 10),
packets: make(chan *buf.Buffer, netTunQueue),
closed: make(chan struct{}),
mtu: mtu,
}
stack, err := xtun.NewStack(context.Background(), xtun.StackOptions{Tun: dev, MTU: uint32(mtu), Addresses: localAddresses, Remote: true}, handler)
if err != nil {
return nil, nil, err
}
dev.stack = stack
tnet := &Net{
DialContextTCPAddrPort: stack.DialTCP,
DialUDPAddrPort: dev.DialUDPAddrPort,
dnsServers: dnsServers,
}
for _, ip := range localAddresses {
if ip.Is4() {
tnet.hasV4 = true
} else if ip.Is6() {
tnet.hasV6 = true
}
}
dev.events <- tun.EventUp
return dev, tnet, nil
}
func (tun *netTun) Name() (string, error) {
return "go", nil
}
func (tun *netTun) File() *os.File {
return nil
}
func (tun *netTun) Events() <-chan tun.Event {
return tun.events
}
func (tun *netTun) Read(buf [][]byte, sizes []int, offset int) (int, error) {
select {
case packet := <-tun.packets:
sizes[0] = copy(buf[0][offset:], packet.Bytes())
packet.Release()
return 1, nil
case <-tun.closed:
return 0, os.ErrClosed
}
}
func (tun *netTun) Write(buf [][]byte, offset int) (int, error) {
for _, buf := range buf {
tun.stack.Input(buf[offset:])
}
return len(buf), nil
}
// WritePacket implements xtun.Tun. The packet is in the hands of the stack only until this returns, so it is copied
// for Read. The stack does not wait for the one who reads the device, unless that one is far behind: it answers
// what it is written inside Write, and whoever writes would wait with it and lose what comes meanwhile
func (tun *netTun) WritePacket(header, payload []byte) error {
packet := buf.NewWithSize(int32(len(header) + len(payload)))
packet.Write(header)
packet.Write(payload)
select {
case tun.packets <- packet:
return nil
case <-tun.closed:
packet.Release()
return os.ErrClosed
}
}
// ReadPacket implements xtun.Tun. It is not called: the packets are handed to the stack by Write
func (tun *netTun) ReadPacket([]byte) ([]byte, error) {
<-tun.closed
return nil, os.ErrClosed
}
// Start implements xtun.Tun
func (tun *netTun) Start() error {
return nil
}
// Index implements xtun.Tun
func (tun *netTun) Index() (int, error) {
return 0, nil
}
func (tun *netTun) Close() error {
tun.closeOnce.Do(func() {
// before the stack ends its connections: nobody may be reading anymore what it writes for that
close(tun.closed)
tun.stack.Close()
close(tun.events)
})
return nil
}
func (tun *netTun) MTU() (int, error) {
return tun.mtu, nil
}
func (tun *netTun) BatchSize() int {
return 1
}
func (tun *netTun) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) {
conn, err := tun.stack.DialUDP()
if err != nil {
return nil, err
}
return &xnet.PacketConnWrapper{
PacketConn: conn,
Dest: net.UDPAddrFromAddrPort(raddr),
}, nil
}
type Net struct {
DialContextTCPAddrPort func(ctx context.Context, addr netip.AddrPort) (net.Conn, error)
DialUDPAddrPort func(laddr, raddr netip.AddrPort) (net.Conn, error)
dnsServers []netip.Addr
hasV4, hasV6 bool
cache cache
}
var (
errNoSuchHost = errors.New("no such host")
errLameReferral = errors.New("lame referral")
errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
errServerMisbehaving = errors.New("server misbehaving")
errInvalidDNSResponse = errors.New("invalid DNS response")
errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
errServerTemporarilyMisbehaving = errors.New("server misbehaving")
errCanceled = errors.New("operation was canceled")
errTimeout = errors.New("i/o timeout")
errNumericPort = errors.New("port must be numeric")
errNoSuitableAddress = errors.New("no suitable address found")
errMissingAddress = errors.New("missing address")
)
func (net *Net) LookupHost(host string) (addrs []string, err error) {
return net.LookupContextHost(context.Background(), host)
}
func isDomainName(s string) bool {
l := len(s)
if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
return false
}
last := byte('.')
nonNumeric := false
partlen := 0
for i := 0; i < len(s); i++ {
c := s[i]
switch {
default:
return false
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
nonNumeric = true
partlen++
case '0' <= c && c <= '9':
partlen++
case c == '-':
if last == '.' {
return false
}
partlen++
nonNumeric = true
case c == '.':
if last == '.' || last == '-' {
return false
}
if partlen > 63 || partlen == 0 {
return false
}
partlen = 0
}
last = c
}
if last == '-' || partlen > 63 {
return false
}
return nonNumeric
}
func randU16() uint16 {
var b [2]byte
_, err := rand.Read(b[:])
if err != nil {
panic(err)
}
return binary.LittleEndian.Uint16(b[:])
}
func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) {
id = randU16()
b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true})
b.EnableCompression()
if err := b.StartQuestions(); err != nil {
return 0, nil, nil, err
}
if err := b.Question(q); err != nil {
return 0, nil, nil, err
}
tcpReq, err = b.Finish()
udpReq = tcpReq[2:]
l := len(tcpReq) - 2
tcpReq[0] = byte(l >> 8)
tcpReq[1] = byte(l)
return id, udpReq, tcpReq, err
}
func equalASCIIName(x, y dnsmessage.Name) bool {
if x.Length != y.Length {
return false
}
for i := 0; i < int(x.Length); i++ {
a := x.Data[i]
b := y.Data[i]
if 'A' <= a && a <= 'Z' {
a += 0x20
}
if 'A' <= b && b <= 'Z' {
b += 0x20
}
if a != b {
return false
}
}
return true
}
func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
if !respHdr.Response {
return false
}
if reqID != respHdr.ID {
return false
}
if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
return false
}
return true
}
func dnsPacketRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
if _, err := c.Write(b); err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
b = make([]byte, 512)
for {
n, err := c.Read(b)
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
var p dnsmessage.Parser
h, err := p.Start(b[:n])
if err != nil {
continue
}
q, err := p.Question()
if err != nil || !checkResponse(id, query, h, q) {
continue
}
return p, h, nil
}
}
func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
if _, err := c.Write(b); err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
b = make([]byte, 1280)
if _, err := io.ReadFull(c, b[:2]); err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
l := int(b[0])<<8 | int(b[1])
if l > len(b) {
b = make([]byte, l)
}
n, err := io.ReadFull(c, b[:l])
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
var p dnsmessage.Parser
h, err := p.Start(b[:n])
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
}
q, err := p.Question()
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
}
if !checkResponse(id, query, h, q) {
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
}
return p, h, nil
}
func (tnet *Net) exchange(ctx context.Context, server netip.Addr, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
q.Class = dnsmessage.ClassINET
id, udpReq, tcpReq, err := newRequest(q)
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
}
for _, useUDP := range []bool{true, false} {
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
defer cancel()
var c net.Conn
var err error
if useUDP {
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, netip.AddrPortFrom(server, 53))
} else {
c, err = tnet.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(server, 53))
}
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
if d, ok := ctx.Deadline(); ok && !d.IsZero() {
err := c.SetDeadline(d)
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
}
// what is opened through the stack of Xray takes no deadline, it ends when it is closed
stop := context.AfterFunc(ctx, func() { c.Close() })
var p dnsmessage.Parser
var h dnsmessage.Header
if useUDP {
p, h, err = dnsPacketRoundTrip(c, id, q, udpReq)
} else {
p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq)
}
stop()
c.Close()
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
// as from a connection that takes a deadline
err = &net.OpError{Op: "read", Net: c.RemoteAddr().Network(), Err: os.ErrDeadlineExceeded}
} else if ctx.Err() != nil {
err = ctx.Err()
}
if err == context.Canceled {
err = errCanceled
} else if err == context.DeadlineExceeded {
err = errTimeout
}
return dnsmessage.Parser{}, dnsmessage.Header{}, err
}
if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
}
if h.Truncated {
continue
}
return p, h, nil
}
return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
}
func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
if h.RCode == dnsmessage.RCodeNameError {
return errNoSuchHost
}
_, err := p.AnswerHeader()
if err != nil && err != dnsmessage.ErrSectionDone {
return errCannotUnmarshalDNSMessage
}
if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
return errLameReferral
}
if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
if h.RCode == dnsmessage.RCodeServerFailure {
return errServerTemporarilyMisbehaving
}
return errServerMisbehaving
}
return nil
}
func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
for {
h, err := p.AnswerHeader()
if err == dnsmessage.ErrSectionDone {
return errNoSuchHost
}
if err != nil {
return errCannotUnmarshalDNSMessage
}
if h.Type == qtype {
return nil
}
if err := p.SkipAnswer(); err != nil {
return errCannotUnmarshalDNSMessage
}
}
}
func (tnet *Net) tryOneName(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
var lastErr error
n, err := dnsmessage.NewName(name)
if err != nil {
return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
}
q := dnsmessage.Question{
Name: n,
Type: qtype,
Class: dnsmessage.ClassINET,
}
for i := 0; i < 2; i++ {
for _, server := range tnet.dnsServers {
p, h, err := tnet.exchange(ctx, server, q, time.Second*5)
if err != nil {
dnsErr := &net.DNSError{
Err: err.Error(),
Name: name,
Server: server.String(),
}
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
dnsErr.IsTimeout = true
}
if _, ok := err.(*net.OpError); ok {
dnsErr.IsTemporary = true
}
lastErr = dnsErr
continue
}
if err := checkHeader(&p, h); err != nil {
dnsErr := &net.DNSError{
Err: err.Error(),
Name: name,
Server: server.String(),
}
if err == errServerTemporarilyMisbehaving {
dnsErr.IsTemporary = true
}
if err == errNoSuchHost {
dnsErr.IsNotFound = true
return p, server.String(), dnsErr
}
lastErr = dnsErr
continue
}
err = skipToAnswer(&p, qtype)
if err == nil {
return p, server.String(), nil
}
lastErr = &net.DNSError{
Err: err.Error(),
Name: name,
Server: server.String(),
}
if err == errNoSuchHost {
lastErr.(*net.DNSError).IsNotFound = true
return p, server.String(), lastErr
}
}
}
return dnsmessage.Parser{}, "", lastErr
}
func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string, error) {
if saddr := tnet.cache.LookupHost(host); saddr != nil {
return saddr, nil
}
if host == "" || (!tnet.hasV6 && !tnet.hasV4) {
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
}
zlen := len(host)
if strings.IndexByte(host, ':') != -1 {
if zidx := strings.LastIndexByte(host, '%'); zidx != -1 {
zlen = zidx
}
}
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
return []string{ip.String()}, nil
}
if !isDomainName(host) {
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
}
type result struct {
p dnsmessage.Parser
server string
error
}
var addrsV4, addrsV6 []netip.Addr
lanes := 0
if tnet.hasV4 {
lanes++
}
if tnet.hasV6 {
lanes++
}
lane := make(chan result, lanes)
var lastErr error
if tnet.hasV4 {
go func() {
p, server, err := tnet.tryOneName(ctx, host+".", dnsmessage.TypeA)
lane <- result{p, server, err}
}()
}
if tnet.hasV6 {
go func() {
p, server, err := tnet.tryOneName(ctx, host+".", dnsmessage.TypeAAAA)
lane <- result{p, server, err}
}()
}
ttl := uint32(300)
for l := 0; l < lanes; l++ {
result := <-lane
if result.error != nil {
if lastErr == nil {
lastErr = result.error
}
continue
}
loop:
for {
h, err := result.p.AnswerHeader()
if err != nil && err != dnsmessage.ErrSectionDone {
lastErr = &net.DNSError{
Err: errCannotMarshalDNSMessage.Error(),
Name: host,
Server: result.server,
}
}
if err != nil {
break
}
switch h.Type {
case dnsmessage.TypeA:
a, err := result.p.AResource()
if err != nil {
lastErr = &net.DNSError{
Err: errCannotMarshalDNSMessage.Error(),
Name: host,
Server: result.server,
}
break loop
}
ttl = min(ttl, h.TTL)
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
case dnsmessage.TypeAAAA:
aaaa, err := result.p.AAAAResource()
if err != nil {
lastErr = &net.DNSError{
Err: errCannotMarshalDNSMessage.Error(),
Name: host,
Server: result.server,
}
break loop
}
ttl = min(ttl, h.TTL)
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
default:
if err := result.p.SkipAnswer(); err != nil {
lastErr = &net.DNSError{
Err: errCannotMarshalDNSMessage.Error(),
Name: host,
Server: result.server,
}
break loop
}
continue
}
}
}
// We don't do RFC6724. Instead just put V6 addresses first if an IPv6 address is enabled
var addrs []netip.Addr
if tnet.hasV6 {
addrs = append(addrsV6, addrsV4...)
} else {
addrs = append(addrsV4, addrsV6...)
}
if len(addrs) == 0 && lastErr != nil {
return nil, lastErr
}
saddrs := make([]string, 0, len(addrs))
for _, ip := range addrs {
saddrs = append(saddrs, ip.String())
}
tnet.cache.Cache(host, saddrs, ttl)
return saddrs, nil
}
func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
if deadline.IsZero() {
return deadline, nil
}
timeRemaining := deadline.Sub(now)
if timeRemaining <= 0 {
return time.Time{}, errTimeout
}
timeout := timeRemaining / time.Duration(addrsRemaining)
const saneMinimum = 2 * time.Second
if timeout < saneMinimum {
if timeRemaining < saneMinimum {
timeout = timeRemaining
} else {
timeout = saneMinimum
}
}
return now.Add(timeout), nil
}
var protoSplitter = regexp.MustCompile(`^(tcp|udp|ping)(4|6)?$`)
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
if ctx == nil {
panic("nil context")
}
var acceptV4, acceptV6 bool
matches := protoSplitter.FindStringSubmatch(network)
if matches == nil {
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
} else if len(matches[2]) == 0 {
acceptV4 = true
acceptV6 = true
} else {
acceptV4 = matches[2][0] == '4'
acceptV6 = !acceptV4
}
var host string
var port int
if matches[1] == "ping" {
host = address
} else {
var sport string
var err error
host, sport, err = net.SplitHostPort(address)
if err != nil {
return nil, &net.OpError{Op: "dial", Err: err}
}
port, err = strconv.Atoi(sport)
if err != nil || port < 0 || port > 65535 {
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
}
}
allAddr, err := tnet.LookupContextHost(ctx, host)
if err != nil {
return nil, &net.OpError{Op: "dial", Err: err}
}
var addrs []netip.AddrPort
for _, addr := range allAddr {
ip, err := netip.ParseAddr(addr)
if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
}
}
if len(addrs) == 0 && len(allAddr) != 0 {
return nil, &net.OpError{Op: "dial", Err: errNoSuitableAddress}
}
var firstErr error
for i, addr := range addrs {
select {
case <-ctx.Done():
err := ctx.Err()
if err == context.Canceled {
err = errCanceled
} else if err == context.DeadlineExceeded {
err = errTimeout
}
return nil, &net.OpError{Op: "dial", Err: err}
default:
}
dialCtx := ctx
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
partialDeadline, err := partialDeadline(time.Now(), deadline, len(addrs)-i)
if err != nil {
if firstErr == nil {
firstErr = &net.OpError{Op: "dial", Err: err}
}
break
}
if partialDeadline.Before(deadline) {
var cancel context.CancelFunc
dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
defer cancel()
}
}
var c net.Conn
switch matches[1] {
case "tcp":
c, err = tnet.DialContextTCPAddrPort(dialCtx, addr)
case "udp":
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, addr)
case "ping":
err = errors.New("not support")
// c, err = tnet.DialPingAddr(netip.Addr{}, addr.Addr())
}
if err == nil {
return c, nil
}
if firstErr == nil {
firstErr = err
}
}
if firstErr == nil {
firstErr = &net.OpError{Op: "dial", Err: errMissingAddress}
}
return nil, firstErr
}
func (tnet *Net) Dial(network, address string) (net.Conn, error) {
return tnet.DialContext(context.Background(), network, address)
}