mirror of https://github.com/XTLS/Xray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
5.8 KiB
315 lines
5.8 KiB
package wireguard
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"net/netip"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"golang.zx2c4.com/wireguard/conn"
|
|
|
|
xnet "github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/features/dns"
|
|
"github.com/xtls/xray-core/transport/internet"
|
|
)
|
|
|
|
type netReadInfo struct {
|
|
// status
|
|
waiter sync.WaitGroup
|
|
// param
|
|
buff []byte
|
|
// result
|
|
bytes int
|
|
endpoint conn.Endpoint
|
|
err error
|
|
}
|
|
|
|
type netBindClient struct {
|
|
workers int
|
|
dialer internet.Dialer
|
|
dns dns.Client
|
|
dnsOption dns.IPOption
|
|
reserved []byte
|
|
|
|
readQueue chan *netReadInfo
|
|
}
|
|
|
|
func (bind *netBindClient) ParseEndpoint(s string) (conn.Endpoint, error) {
|
|
ipStr, port, _, err := splitAddrPort(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var addr net.IP
|
|
if IsDomainName(ipStr) {
|
|
ips, err := bind.dns.LookupIP(ipStr, bind.dnsOption)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(ips) == 0 {
|
|
return nil, dns.ErrEmptyResponse
|
|
}
|
|
addr = ips[0]
|
|
} else {
|
|
addr = net.ParseIP(ipStr)
|
|
}
|
|
if addr == nil {
|
|
return nil, errors.New("failed to parse ip: " + ipStr)
|
|
}
|
|
|
|
var ip xnet.Address
|
|
if p4 := addr.To4(); len(p4) == net.IPv4len {
|
|
ip = xnet.IPAddress(p4[:])
|
|
} else {
|
|
ip = xnet.IPAddress(addr[:])
|
|
}
|
|
|
|
dst := xnet.Destination{
|
|
Address: ip,
|
|
Port: xnet.Port(port),
|
|
Network: xnet.Network_UDP,
|
|
}
|
|
|
|
return &netEndpoint{
|
|
dst: dst,
|
|
}, nil
|
|
}
|
|
|
|
func (bind *netBindClient) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
|
|
bind.readQueue = make(chan *netReadInfo)
|
|
|
|
fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
n = 0
|
|
err = errors.New("channel closed")
|
|
}
|
|
}()
|
|
|
|
r := &netReadInfo{
|
|
buff: bufs[0],
|
|
}
|
|
r.waiter.Add(1)
|
|
bind.readQueue <- r
|
|
r.waiter.Wait() // wait read goroutine done, or we will miss the result
|
|
sizes[0], eps[0] = r.bytes, r.endpoint
|
|
return 1, r.err
|
|
}
|
|
workers := bind.workers
|
|
if workers <= 0 {
|
|
workers = 1
|
|
}
|
|
arr := make([]conn.ReceiveFunc, workers)
|
|
for i := 0; i < workers; i++ {
|
|
arr[i] = fun
|
|
}
|
|
|
|
return arr, uint16(uport), nil
|
|
}
|
|
|
|
func (bind *netBindClient) Close() error {
|
|
if bind.readQueue != nil {
|
|
close(bind.readQueue)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
|
|
c, err := bind.dialer.Dial(context.Background(), endpoint.dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
endpoint.conn = c
|
|
|
|
go func(readQueue <-chan *netReadInfo, endpoint *netEndpoint) {
|
|
for {
|
|
v, ok := <-readQueue
|
|
if !ok {
|
|
return
|
|
}
|
|
i, err := c.Read(v.buff)
|
|
|
|
if i > 3 {
|
|
v.buff[1] = 0
|
|
v.buff[2] = 0
|
|
v.buff[3] = 0
|
|
}
|
|
|
|
v.bytes = i
|
|
v.endpoint = endpoint
|
|
v.err = err
|
|
v.waiter.Done()
|
|
if err != nil && errors.Is(err, io.EOF) {
|
|
endpoint.conn = nil
|
|
return
|
|
}
|
|
}
|
|
}(bind.readQueue, endpoint)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (bind *netBindClient) Send(buff [][]byte, endpoint conn.Endpoint) error {
|
|
var err error
|
|
|
|
nend, ok := endpoint.(*netEndpoint)
|
|
if !ok {
|
|
return conn.ErrWrongEndpointType
|
|
}
|
|
|
|
if nend.conn == nil {
|
|
err = bind.connectTo(nend)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, buff := range buff {
|
|
if len(buff) > 3 && len(bind.reserved) == 3 {
|
|
copy(buff[1:], bind.reserved)
|
|
}
|
|
if _, err = nend.conn.Write(buff); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (bind *netBindClient) SetMark(mark uint32) error {
|
|
return nil
|
|
}
|
|
|
|
func (bind *netBindClient) BatchSize() int {
|
|
return 1
|
|
}
|
|
|
|
type netEndpoint struct {
|
|
dst xnet.Destination
|
|
conn net.Conn
|
|
}
|
|
|
|
func (netEndpoint) ClearSrc() {}
|
|
|
|
func (e netEndpoint) DstIP() netip.Addr {
|
|
return toNetIpAddr(e.dst.Address)
|
|
}
|
|
|
|
func (e netEndpoint) SrcIP() netip.Addr {
|
|
return netip.Addr{}
|
|
}
|
|
|
|
func (e netEndpoint) DstToBytes() []byte {
|
|
var dat []byte
|
|
if e.dst.Address.Family().IsIPv4() {
|
|
dat = e.dst.Address.IP().To4()[:]
|
|
} else {
|
|
dat = e.dst.Address.IP().To16()[:]
|
|
}
|
|
dat = append(dat, byte(e.dst.Port), byte(e.dst.Port>>8))
|
|
return dat
|
|
}
|
|
|
|
func (e netEndpoint) DstToString() string {
|
|
return e.dst.NetAddr()
|
|
}
|
|
|
|
func (e netEndpoint) SrcToString() string {
|
|
return ""
|
|
}
|
|
|
|
func toNetIpAddr(addr xnet.Address) netip.Addr {
|
|
if addr.Family().IsIPv4() {
|
|
ip := addr.IP()
|
|
return netip.AddrFrom4([4]byte{ip[0], ip[1], ip[2], ip[3]})
|
|
} else {
|
|
ip := addr.IP()
|
|
arr := [16]byte{}
|
|
for i := 0; i < 16; i++ {
|
|
arr[i] = ip[i]
|
|
}
|
|
return netip.AddrFrom16(arr)
|
|
}
|
|
}
|
|
|
|
func stringsLastIndexByte(s string, b byte) int {
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|
if s[i] == b {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func splitAddrPort(s string) (ip string, port uint16, v6 bool, err error) {
|
|
i := stringsLastIndexByte(s, ':')
|
|
if i == -1 {
|
|
return "", 0, false, errors.New("not an ip:port")
|
|
}
|
|
|
|
ip = s[:i]
|
|
portStr := s[i+1:]
|
|
if len(ip) == 0 {
|
|
return "", 0, false, errors.New("no IP")
|
|
}
|
|
if len(portStr) == 0 {
|
|
return "", 0, false, errors.New("no port")
|
|
}
|
|
port64, err := strconv.ParseUint(portStr, 10, 16)
|
|
if err != nil {
|
|
return "", 0, false, errors.New("invalid port " + strconv.Quote(portStr) + " parsing " + strconv.Quote(s))
|
|
}
|
|
port = uint16(port64)
|
|
if ip[0] == '[' {
|
|
if len(ip) < 2 || ip[len(ip)-1] != ']' {
|
|
return "", 0, false, errors.New("missing ]")
|
|
}
|
|
ip = ip[1 : len(ip)-1]
|
|
v6 = true
|
|
}
|
|
|
|
return ip, port, v6, nil
|
|
}
|
|
|
|
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
|
|
}
|