2023-01-23 18:54:21 +07:00
|
|
|
package dialer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"golang.org/x/net/proxy"
|
|
|
|
"net"
|
2023-01-30 14:50:55 +07:00
|
|
|
"runtime"
|
|
|
|
"syscall"
|
2023-01-23 18:54:21 +07:00
|
|
|
)
|
|
|
|
|
2023-01-27 01:10:27 +07:00
|
|
|
var SymmetricDirect = newDirect(false)
|
|
|
|
var FullconeDirect = newDirect(true)
|
2023-01-23 18:54:21 +07:00
|
|
|
|
2023-01-28 00:50:21 +07:00
|
|
|
func NewDirectDialer(option *GlobalOption, fullcone bool) *Dialer {
|
2023-01-27 01:10:27 +07:00
|
|
|
if fullcone {
|
2023-01-28 14:47:43 +07:00
|
|
|
return NewDialer(FullconeDirect, option, InstanceOption{Check: false}, true, "direct", "direct", "")
|
2023-01-27 01:10:27 +07:00
|
|
|
} else {
|
2023-01-28 14:47:43 +07:00
|
|
|
return NewDialer(SymmetricDirect, option, InstanceOption{Check: false}, true, "direct", "direct", "")
|
2023-01-27 01:10:27 +07:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 18:54:21 +07:00
|
|
|
|
|
|
|
type direct struct {
|
|
|
|
proxy.Dialer
|
2023-01-28 00:50:21 +07:00
|
|
|
netDialer *net.Dialer
|
2023-01-23 18:54:21 +07:00
|
|
|
fullCone bool
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:10:27 +07:00
|
|
|
func newDirect(fullCone bool) proxy.Dialer {
|
2023-01-23 18:54:21 +07:00
|
|
|
return &direct{
|
2023-01-30 14:50:55 +07:00
|
|
|
netDialer: &net.Dialer{Control: func(network, address string, c syscall.RawConn) error {
|
|
|
|
return SoMarkControl(c)
|
|
|
|
}},
|
|
|
|
fullCone: fullCone,
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *direct) Dial(network, addr string) (c net.Conn, err error) {
|
|
|
|
switch network {
|
|
|
|
case "tcp":
|
2023-02-02 20:22:18 +07:00
|
|
|
conn, err := d.netDialer.Dial(network, addr)
|
|
|
|
return conn, err
|
2023-01-23 18:54:21 +07:00
|
|
|
case "udp":
|
|
|
|
if d.fullCone {
|
|
|
|
conn, err := net.ListenUDP(network, nil)
|
2023-01-30 14:50:55 +07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-02-05 10:00:29 +07:00
|
|
|
raw, e := conn.SyscallConn()
|
|
|
|
if e == nil {
|
|
|
|
_ = SoMarkControl(raw)
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
return &directUDPConn{UDPConn: conn, FullCone: true}, nil
|
|
|
|
} else {
|
|
|
|
conn, err := d.netDialer.Dial(network, addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &directUDPConn{UDPConn: conn.(*net.UDPConn), FullCone: false}, nil
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, net.UnknownNetworkError(network)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type directUDPConn struct {
|
|
|
|
*net.UDPConn
|
|
|
|
FullCone bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *directUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
|
|
if !c.FullCone {
|
|
|
|
// FIXME: check the addr
|
|
|
|
return c.Write(b)
|
|
|
|
}
|
|
|
|
return c.UDPConn.WriteTo(b, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *directUDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
|
|
|
|
if !c.FullCone {
|
|
|
|
n, err = c.Write(b)
|
|
|
|
return n, 0, err
|
|
|
|
}
|
|
|
|
return c.UDPConn.WriteMsgUDP(b, oob, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *directUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
|
|
|
if !c.FullCone {
|
|
|
|
return c.Write(b)
|
|
|
|
}
|
|
|
|
return c.UDPConn.WriteToUDP(b, addr)
|
|
|
|
}
|
2023-01-30 14:50:55 +07:00
|
|
|
|
|
|
|
var fwmarkIoctl int
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "linux", "android":
|
|
|
|
fwmarkIoctl = 36 /* unix.SO_MARK */
|
|
|
|
case "freebsd":
|
|
|
|
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
|
|
|
|
case "openbsd":
|
|
|
|
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func SoMarkControl(c syscall.RawConn) error {
|
|
|
|
return c.Control(func(fd uintptr) {
|
|
|
|
//TODO: force to set 0xff. any chances to customize this value?
|
2023-02-04 10:24:03 +07:00
|
|
|
err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, fwmarkIoctl, 0x100)
|
2023-01-30 14:50:55 +07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|