mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-10 19:20:04 +08:00

不再使用 UDP_Putter 等机制去转发udp,而是用一个 netLayer.MsgConn 结构 proxy.Server 和 proxy.Client 接口改动, Client在握手udp时不再使用handshake方法, 而是用新的 EstablishUDPChannel 方法 Server 在 Handshake时会选择性返回两种接口,io.ReadWriteCloser 用于tcp, netLayer.MsgConn 用于 udp 此时vless、socks5、direct 的udp转发都已经成功经过了 go test 验证, 但是 main.go 还未修改。
432 lines
9.0 KiB
Go
432 lines
9.0 KiB
Go
package netLayer
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"errors"
|
||
"math/rand"
|
||
"net"
|
||
"net/netip"
|
||
"net/url"
|
||
"reflect"
|
||
"strconv"
|
||
"strings"
|
||
"unsafe"
|
||
|
||
"github.com/hahahrfool/v2ray_simple/utils"
|
||
)
|
||
|
||
// Atyp, for vless and vmess; 注意与 trojan和socks5的区别,trojan和socks5的相同含义的值是1,3,4
|
||
const (
|
||
AtypIP4 byte = 1
|
||
AtypDomain byte = 2
|
||
AtypIP6 byte = 3
|
||
)
|
||
|
||
// Addr represents a address that you want to access by proxy. Either Name or IP is used exclusively.
|
||
// Addr完整地表示了一个 传输层的目标,同时用 Network 字段 来记录网络层协议名
|
||
// Addr 还可以用Dial 方法直接进行拨号
|
||
type Addr struct {
|
||
Network string
|
||
Name string // domain name, 或者 unix domain socket 的 文件路径
|
||
IP net.IP
|
||
Port int
|
||
}
|
||
|
||
type HashableAddr struct {
|
||
Network, Name string
|
||
netip.AddrPort
|
||
}
|
||
|
||
func RandPort() int {
|
||
return rand.Intn(60000) + 4096
|
||
}
|
||
|
||
func RandPortStr() string {
|
||
return strconv.Itoa(RandPort())
|
||
}
|
||
|
||
func GetRandLocalAddr() string {
|
||
return "0.0.0.0:" + RandPortStr()
|
||
}
|
||
|
||
func GetRandLocalPrivateAddr() string {
|
||
return "127.0.0.1:" + RandPortStr()
|
||
}
|
||
|
||
func NewAddrFromUDPAddr(addr *net.UDPAddr) Addr {
|
||
return Addr{
|
||
IP: addr.IP,
|
||
Port: addr.Port,
|
||
Network: "udp",
|
||
}
|
||
}
|
||
|
||
//addrStr格式一般为 host:port ;如果不含冒号,将直接认为该字符串是域名或文件名
|
||
func NewAddr(addrStr string) (Addr, error) {
|
||
if !strings.Contains(addrStr, ":") {
|
||
//unix domain socket, 或者域名默认端口的情况
|
||
return Addr{Name: addrStr}, nil
|
||
}
|
||
|
||
return NewAddrByHostPort(addrStr)
|
||
}
|
||
|
||
//hostPortStr格式 必须为 host:port,本函数不对此检查
|
||
func NewAddrByHostPort(hostPortStr string) (Addr, error) {
|
||
host, portStr, err := net.SplitHostPort(hostPortStr)
|
||
if err != nil {
|
||
return Addr{}, err
|
||
}
|
||
if host == "" {
|
||
host = "127.0.0.1"
|
||
}
|
||
port, err := strconv.Atoi(portStr)
|
||
|
||
a := Addr{Port: port}
|
||
if ip := net.ParseIP(host); ip != nil {
|
||
|
||
a.IP = ip
|
||
} else {
|
||
a.Name = host
|
||
}
|
||
return a, nil
|
||
}
|
||
|
||
// 如 tcp://127.0.0.1:443 , tcp://google.com:443 ;
|
||
// 不支持unix domain socket, 因为它是文件路径, / 还需要转义,太麻烦;不是我们代码麻烦, 而是怕用户嫌麻烦
|
||
func NewAddrByURL(addrStr string) (Addr, error) {
|
||
|
||
u, err := url.Parse(addrStr)
|
||
if err != nil {
|
||
return Addr{}, err
|
||
}
|
||
if u.Scheme == "unix" {
|
||
return Addr{}, errors.New("parse unix domain socket by url is not supported")
|
||
}
|
||
addrStr = u.Host
|
||
|
||
host, portStr, err := net.SplitHostPort(addrStr)
|
||
if err != nil {
|
||
return Addr{}, err
|
||
}
|
||
if host == "" {
|
||
host = "127.0.0.1"
|
||
}
|
||
port, err := strconv.Atoi(portStr)
|
||
|
||
a := Addr{Port: port}
|
||
if ip := net.ParseIP(host); ip != nil {
|
||
a.IP = ip
|
||
} else {
|
||
a.Name = host
|
||
}
|
||
|
||
a.Network = u.Scheme
|
||
|
||
return a, nil
|
||
}
|
||
|
||
//会根据thing的类型 生成实际addr; 可以为数字端口,或者带冒号的字符串,或者一个 文件路径(unix domain socket)
|
||
func NewAddrFromAny(thing any) (addr Addr, err error) {
|
||
var integer int
|
||
var dest_type byte = 0 //0: port, 1: ip:port, 2: unix domain socket
|
||
var dest_string string
|
||
|
||
switch value := thing.(type) {
|
||
case float64: //json 默认把数字转换成float64,就算是整数也一样
|
||
|
||
if value > 65535 || value < 1 {
|
||
err = utils.ErrInErr{ErrDesc: "int port not valid", Data: value}
|
||
return
|
||
}
|
||
|
||
integer = int(value)
|
||
|
||
case int64:
|
||
integer = int(value)
|
||
case string:
|
||
//两种情况, 带冒号的 ip:port, 或者 unix domain socket 的文件路径
|
||
|
||
if strings.Contains(value, ":") {
|
||
dest_type = 1
|
||
dest_string = value
|
||
} else {
|
||
//不带冒号这里就直接认为是 unix domain socket
|
||
|
||
dest_type = 2
|
||
dest_string = value
|
||
}
|
||
|
||
default:
|
||
err = utils.ErrInErr{ErrDesc: "Fallback dest config type err", Data: reflect.TypeOf(thing)}
|
||
return
|
||
}
|
||
|
||
switch dest_type {
|
||
case 0:
|
||
addr = Addr{
|
||
IP: net.IPv4(127, 0, 0, 1),
|
||
Port: integer,
|
||
}
|
||
case 1:
|
||
addr, err = NewAddrByHostPort(dest_string)
|
||
if err != nil {
|
||
err = utils.ErrInErr{ErrDesc: "addr create with given string failed", ErrDetail: err, Data: dest_string}
|
||
return
|
||
}
|
||
case 2:
|
||
addr = Addr{
|
||
Network: "unix",
|
||
Name: dest_string,
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func (a *Addr) GetHashable() (ha HashableAddr) {
|
||
theip := a.IP
|
||
if i4 := a.IP.To4(); i4 != nil {
|
||
theip = i4 //能转成ipv4则必须转,否则虽然是同一个ip,但是如果被表示成了ipv6的形式,相等比较还是会失败
|
||
}
|
||
ip, _ := netip.AddrFromSlice(theip)
|
||
|
||
ha.AddrPort = netip.AddrPortFrom(ip, uint16(a.Port))
|
||
ha.Network = a.Network
|
||
ha.Name = a.Name
|
||
return
|
||
}
|
||
|
||
// Return host:port string
|
||
func (a *Addr) String() string {
|
||
if a.Network == "unix" {
|
||
return a.Name
|
||
} else {
|
||
port := strconv.Itoa(a.Port)
|
||
if a.IP == nil {
|
||
return net.JoinHostPort(a.Name, port)
|
||
}
|
||
return net.JoinHostPort(a.IP.String(), port)
|
||
}
|
||
|
||
}
|
||
|
||
//返回以url表示的 地址. unix的话文件名若带斜杠则会被转义
|
||
func (a *Addr) UrlString() string {
|
||
if a.Network != "" {
|
||
return a.Network + "://" + url.PathEscape(a.String())
|
||
|
||
} else {
|
||
return "tcp://" + a.String()
|
||
|
||
}
|
||
|
||
}
|
||
|
||
func (a *Addr) IsEmpty() bool {
|
||
return a.Name == "" && len(a.IP) == 0 && a.Network == "" && a.Port == 0
|
||
}
|
||
|
||
func (a *Addr) GetNetIPAddr() (na netip.Addr) {
|
||
if len(a.IP) < 1 {
|
||
return
|
||
}
|
||
|
||
na, _ = netip.AddrFromSlice(a.IP)
|
||
|
||
return
|
||
}
|
||
|
||
//a.Network == "udp", "udp4", "udp6"
|
||
func (a *Addr) IsUDP() bool {
|
||
return IsStrUDP_network(a.Network)
|
||
}
|
||
|
||
//如果a里只含有域名,则会自动解析域名为IP。
|
||
func (a *Addr) ToUDPAddr() *net.UDPAddr {
|
||
ua, err := net.ResolveUDPAddr("udp", a.String())
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
return ua
|
||
}
|
||
|
||
// Returned host string
|
||
func (a *Addr) HostStr() string {
|
||
if a.IP == nil {
|
||
return a.Name
|
||
}
|
||
return a.IP.String()
|
||
}
|
||
|
||
func (addr *Addr) Dial() (net.Conn, error) {
|
||
//log.Println("Dial called", addr, addr.Network)
|
||
var istls bool
|
||
var resultConn net.Conn
|
||
var err error
|
||
|
||
switch addr.Network {
|
||
case "":
|
||
addr.Network = "tcp"
|
||
goto tcp
|
||
case "tcp", "tcp4", "tcp6":
|
||
goto tcp
|
||
case "tls": //此形式目前被用于dns配置中 的 dns over tls 的 url中
|
||
istls = true
|
||
goto tcp
|
||
case "udp", "udp4", "udp6":
|
||
ua := addr.ToUDPAddr()
|
||
|
||
if !machineCanConnectToIpv6 && addr.IP.To4() == nil {
|
||
return nil, ErrMachineCantConnectToIpv6
|
||
}
|
||
|
||
return DialUDP(ua)
|
||
default:
|
||
|
||
goto defaultPart
|
||
|
||
}
|
||
|
||
tcp:
|
||
|
||
if addr.IP != nil {
|
||
if addr.IP.To4() == nil {
|
||
if !machineCanConnectToIpv6 {
|
||
return nil, ErrMachineCantConnectToIpv6
|
||
} else {
|
||
|
||
resultConn, err = net.DialTCP("tcp6", nil, &net.TCPAddr{
|
||
IP: addr.IP,
|
||
Port: addr.Port,
|
||
})
|
||
goto dialedPart
|
||
}
|
||
} else {
|
||
|
||
resultConn, err = net.DialTCP("tcp4", nil, &net.TCPAddr{
|
||
IP: addr.IP,
|
||
Port: addr.Port,
|
||
})
|
||
goto dialedPart
|
||
}
|
||
|
||
}
|
||
|
||
defaultPart:
|
||
resultConn, err = net.Dial(addr.Network, addr.String())
|
||
|
||
dialedPart:
|
||
if istls && err == nil {
|
||
|
||
conf := &tls.Config{}
|
||
|
||
if addr.Name != "" {
|
||
conf.ServerName = addr.Name
|
||
} else {
|
||
conf.InsecureSkipVerify = true
|
||
}
|
||
|
||
tlsconn := tls.Client(resultConn, conf)
|
||
err = tlsconn.Handshake()
|
||
return tlsconn, err
|
||
}
|
||
return resultConn, err
|
||
|
||
}
|
||
|
||
// 如果a的ip不为空,则会返回 AtypIP4 或 AtypIP6,否则会返回 AtypDomain
|
||
// Return address bytes and type
|
||
// 如果atyp类型是 域名,则 第一字节为该域名的总长度, 其余字节为域名内容。
|
||
func (a *Addr) AddressBytes() ([]byte, byte) {
|
||
var addr []byte
|
||
var atyp byte
|
||
|
||
if a.IP != nil {
|
||
if ip4 := a.IP.To4(); ip4 != nil {
|
||
addr = make([]byte, net.IPv4len)
|
||
atyp = AtypIP4
|
||
copy(addr[:], ip4)
|
||
} else {
|
||
addr = make([]byte, net.IPv6len)
|
||
atyp = AtypIP6
|
||
copy(addr[:], a.IP)
|
||
}
|
||
} else {
|
||
if len(a.Name) > 255 {
|
||
return nil, 0
|
||
}
|
||
addr = make([]byte, 1+len(a.Name))
|
||
atyp = AtypDomain
|
||
addr[0] = byte(len(a.Name))
|
||
copy(addr[1:], a.Name)
|
||
}
|
||
|
||
return addr, atyp
|
||
}
|
||
|
||
// ParseAddr 分析字符串,并按照特定方式返回 地址类型 atyp,地址数据 addr []byte,以及端口号,
|
||
// 如果解析出的地址是ip,则 addr返回 net.IP;
|
||
// 如果解析出的地址是 域名,则第一字节为域名总长度, 剩余字节为域名内容
|
||
func ParseStrToAddr(s string) (atyp byte, addr []byte, port_uint16 uint16, err error) {
|
||
|
||
var host string
|
||
var portStr string
|
||
|
||
host, portStr, err = net.SplitHostPort(s)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
if ip := net.ParseIP(host); ip != nil {
|
||
if ip4 := ip.To4(); ip4 != nil {
|
||
addr = make([]byte, net.IPv4len)
|
||
atyp = AtypIP4
|
||
copy(addr[:], ip4)
|
||
} else {
|
||
addr = make([]byte, net.IPv6len)
|
||
atyp = AtypIP6
|
||
copy(addr[:], ip)
|
||
}
|
||
} else {
|
||
if len(host) > 255 {
|
||
return
|
||
}
|
||
addr = make([]byte, 1+len(host))
|
||
atyp = AtypDomain
|
||
addr[0] = byte(len(host))
|
||
copy(addr[1:], host)
|
||
}
|
||
|
||
var portUint64 uint64
|
||
portUint64, err = strconv.ParseUint(portStr, 10, 16)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
port_uint16 = uint16(portUint64)
|
||
|
||
return
|
||
}
|
||
|
||
func UDPAddr_v4_to_Bytes(addr *net.UDPAddr) [6]byte {
|
||
ip := addr.IP.To4()
|
||
|
||
port := uint16(addr.Port)
|
||
|
||
var allByte [6]byte
|
||
abs := allByte[:]
|
||
copy(abs, ip)
|
||
copy(abs[4:], (*(*[2]byte)(unsafe.Pointer(&port)))[:])
|
||
|
||
return allByte
|
||
}
|
||
|
||
func UDPAddr2AddrPort(ua *net.UDPAddr) netip.AddrPort {
|
||
if ua == nil {
|
||
return netip.AddrPort{}
|
||
}
|
||
a, _ := netip.AddrFromSlice(ua.IP)
|
||
return netip.AddrPortFrom(a, uint16(ua.Port))
|
||
}
|