diff --git a/core/src/main/golang/main.go b/core/src/main/golang/main.go index 2e94b15..6046ac5 100644 --- a/core/src/main/golang/main.go +++ b/core/src/main/golang/main.go @@ -9,25 +9,30 @@ import ( "github.com/Dreamacro/clash/constant" "github.com/kr328/clash/profile" + "github.com/kr328/clash/server" ) -func main() int { - if len(os.Args) != 1 { +func main() { + if len(os.Args) != 2 { fmt.Println("Invalid argument") - return -1 + return } + fmt.Println("[CLASH] PID =", int(os.Getpid())) + if cwd, err := os.Getwd(); err == nil { constant.SetHomeDir(cwd) } else { - return -1 + return } profile.LoadDefault() + server.Start(os.Args[1]) + sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh - return 0 + return } diff --git a/core/src/main/golang/server/profile.go b/core/src/main/golang/server/profile.go new file mode 100644 index 0000000..9ec1173 --- /dev/null +++ b/core/src/main/golang/server/profile.go @@ -0,0 +1,50 @@ +package server + +import ( + "encoding/binary" + "encoding/json" + "net" + + "github.com/Dreamacro/clash/log" + "github.com/kr328/clash/profile" +) + +func handleProfileDefault(client *net.UnixConn) { + profile.LoadDefault() + + client.Close() +} + +func handleProfileReload(client *net.UnixConn) { + packet, err := readCommandPacket(client) + if err != nil { + log.Warnln("Read profile payload failure, %s", err.Error()) + } + + var payload struct { + Path string `json:"path"` + } + + err = json.Unmarshal(packet, &payload) + if err != nil { + log.Warnln("Parse profile payload failure, %s", err.Error()) + } + + err = profile.LoadFromFile(payload.Path) + + if err != nil { + var reply struct { + Err string `json:"error"` + } + + reply.Err = err.Error() + + buffer, _ := json.Marshal(&reply) + + writeCommandPacket(client, buffer) + } else { + binary.Write(client, binary.BigEndian, uint32(0)) + } + + client.Close() +} diff --git a/core/src/main/golang/server/tun.go b/core/src/main/golang/server/tun.go index 9bbd46f..b5203dc 100644 --- a/core/src/main/golang/server/tun.go +++ b/core/src/main/golang/server/tun.go @@ -1,36 +1,56 @@ package server import ( + "encoding/binary" "net" - "golang.org/x/sys/unix" - "github.com/Dreamacro/clash/log" - "github.com/kr328/clash/tun" + "golang.org/x/sys/unix" +) + +const ( + tunCommandEnd = 0x243 ) func handleTunStart(client *net.UnixConn) { - buffer, err := readCommandPackage(client) + buffer := make([]byte, unix.CmsgLen(4*1)) + data := make([]byte, 8) + + _, noob, _, _, err := client.ReadMsgUnix(data, buffer) if err != nil { - log.Warnln("Read packet from unix socket failure %s", err.Error()) + log.Warnln("Read tun socket failure, %s", err.Error()) return } - msg, err := unix.ParseSocketControlMessage(buffer) + msg, err := unix.ParseSocketControlMessage(buffer[:noob]) if err != nil || len(msg) != 1 { - log.Warnln("Parse control package failure %s", err.Error()) + log.Warnln("Parse tun socket failure, %s", err.Error()) return } fds, err := unix.ParseUnixRights(&msg[0]) if err != nil { - log.Warnln("Parse control package failure %s", err.Error()) + log.Warnln("Parse tun socket failure, %s", err.Error()) + return } - tun.NewTunProxy(fds[0]) + mtu := binary.BigEndian.Uint32(data) + + if binary.BigEndian.Uint32(data[4:]) != tunCommandEnd { + log.Warnln("Invalid tun command end") + return + } + + tun.StartTunProxy(fds[0], int(mtu)) } func handleTunStop(client *net.UnixConn) { + buf, _ := readCommandPacket(client) + if tunCommandEnd != binary.BigEndian.Uint32(buf) { + log.Warnln("Invalid tun command end") + } + + tun.StopTunProxy() } diff --git a/core/src/main/golang/server/utils.go b/core/src/main/golang/server/utils.go index 62a48a8..8c81462 100644 --- a/core/src/main/golang/server/utils.go +++ b/core/src/main/golang/server/utils.go @@ -2,12 +2,13 @@ package server import ( "encoding/binary" + "errors" "io" "net" ) -func readCommandPackage(client *net.UnixConn) ([]byte, error) { - var packageSize int32 +func readCommandPacket(client *net.UnixConn) ([]byte, error) { + var packageSize uint32 if err := binary.Read(client, binary.BigEndian, &packageSize); err != nil { return nil, err @@ -15,9 +16,26 @@ func readCommandPackage(client *net.UnixConn) ([]byte, error) { buffer := make([]byte, packageSize) - if readSize, err := io.ReadFull(client, buffer); err != nil || int32(readSize) != packageSize { + if readSize, err := io.ReadFull(client, buffer); err != nil || uint32(readSize) != packageSize { return nil, err } return buffer, nil } + +func writeCommandPacket(client *net.UnixConn, buffer []byte) error { + if err := binary.Write(client, binary.BigEndian, uint32(len(buffer))); err != nil { + return err + } + + n, err := client.Write(buffer) + if err != nil { + return err + } + + if n != len(buffer) { + return errors.New("Write invalid data size") + } + + return nil +} diff --git a/core/src/main/golang/tun/tun.go b/core/src/main/golang/tun/tun.go index 34dc605..cd2ed2f 100644 --- a/core/src/main/golang/tun/tun.go +++ b/core/src/main/golang/tun/tun.go @@ -10,7 +10,7 @@ import ( "github.com/Dreamacro/clash/component/socks5" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "github.com/Dreamacro/clash/tunnel" + T "github.com/Dreamacro/clash/tunnel" "encoding/binary" @@ -24,25 +24,44 @@ import ( "github.com/google/netstack/waiter" ) -var ( - tun = tunnel.Instance() - ipstack *stack.Stack -) - -// TunAdapter tun device handler -type TunAdapter struct { +type tun struct { device *TunDevice ipstack *stack.Stack } -// NewTunProxy create TunProxy under Linux OS. -func NewTunProxy(fd int, mtu int) (*TunAdapter, error) { +var ( + tunnel = T.Instance() + instance *tun +) + +// StartTunProxy - start +func StartTunProxy(fd, mtu int) error { + if instance != nil { + instance.close() + } + + var err error + instance, err = newTunProxy(fd, mtu) + if err != nil { + return err + } + + return nil +} + +// StopTunProxy - stop +func StopTunProxy() { + instance.close() + instance = nil +} + +func newTunProxy(fd int, mtu int) (*tun, error) { tundev, err := OpenTunDevice(fd, mtu) if err != nil { return nil, fmt.Errorf("Can't open tun: %v", err) } - ipstack = stack.New(stack.Options{ + ipstack := stack.New(stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), ipv6.NewProtocol()}, TransportProtocols: []stack.TransportProtocol{tcp.NewProtocol(), udp.NewProtocol()}, }) @@ -76,7 +95,7 @@ func NewTunProxy(fd int, mtu int) (*TunAdapter, error) { conn := gonet.NewConn(&wq, ep) target := getAddr(ep.Info().(*tcp.EndpointInfo).ID) - tun.Add(adapters.NewSocket(target, conn, C.REDIR, C.TCP)) + tunnel.Add(adapters.NewSocket(target, conn, C.REDIR, C.TCP)) }) ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpFwd.HandlePacket) @@ -91,26 +110,25 @@ func NewTunProxy(fd int, mtu int) (*TunAdapter, error) { conn := gonet.NewConn(&wq, ep) target := getAddr(ep.Info().(*stack.TransportEndpointInfo).ID) - tun.Add(adapters.NewSocket(target, conn, C.REDIR, C.UDP)) + tunnel.Add(adapters.NewSocket(target, conn, C.REDIR, C.UDP)) }) ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, udpFwd.HandlePacket) - tl := &TunAdapter{ + tl := &tun{ device: tundev, ipstack: ipstack, } - android.Info("Tun device opened") log.Infoln("Tun device opened") return tl, nil } // Close close the TunAdapter -func (t *TunAdapter) Close() { +func (t *tun) close() { t.device.Close() - ipstack.Close() + t.ipstack.Close() } func getAddr(id stack.TransportEndpointID) socks5.Addr { @@ -126,11 +144,11 @@ func getAddr(id stack.TransportEndpointID) socks5.Addr { copy(addr[1:1+net.IPv4len], []byte(ipv4)) addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] return addr - } else { - addr := make([]byte, 1+net.IPv6len+2) - addr[0] = socks5.AtypIPv6 - copy(addr[1:1+net.IPv6len], []byte(id.LocalAddress)) - addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] - return addr } + + addr := make([]byte, 1+net.IPv6len+2) + addr[0] = socks5.AtypIPv6 + copy(addr[1:1+net.IPv6len], []byte(id.LocalAddress)) + addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] + return addr }