diff --git a/main.go b/main.go index 860e116..ad98e4d 100644 --- a/main.go +++ b/main.go @@ -41,7 +41,7 @@ import ( var cmdRoot = &cobra.Command{ Use: "b612", - Version: "2.1.0.beta.4", + Version: "2.1.0.beta.6", } func init() { diff --git a/net/cmd.go b/net/cmd.go index 53a24d8..d24f349 100644 --- a/net/cmd.go +++ b/net/cmd.go @@ -52,7 +52,7 @@ func init() { CmdNetTrace.Flags().BoolVarP(&disableIpInfo, "disable-ipinfo", "D", false, "禁用ip信息查询") CmdNetTrace.Flags().StringVarP(&bindAddr, "bind", "b", "0.0.0.0", "绑定地址") CmdNetTrace.Flags().BoolVarP(&hideIncorrect, "hide-incorrect", "H", false, "隐藏错误节点") - Cmd.AddCommand(CmdNetTrace) + Cmd.AddCommand(CmdNetTrace, cmdSSHJar) } diff --git a/net/nat_test.go b/net/nat_test.go index 66601c4..9202607 100644 --- a/net/nat_test.go +++ b/net/nat_test.go @@ -25,5 +25,5 @@ func TestNat(t *testing.T) { } func TestTrace(t *testing.T) { - Traceroute("b612.me", "", 32, time.Millisecond*800, "https://ip.b612.me/{ip}/detail") + //Traceroute("b612.me", "", 32, time.Millisecond*800, "https://ip.b612.me/{ip}/detail") } diff --git a/net/setcpinfo_darwin.go b/net/setcpinfo_darwin.go new file mode 100644 index 0000000..b815255 --- /dev/null +++ b/net/setcpinfo_darwin.go @@ -0,0 +1,35 @@ +//go:build darwin + +package net + +import ( + "net" + "syscall" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + if usingKeepAlive { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, keepAliveIdel) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x101, keepAlivePeriod) + if err != nil { + return + } + }) + } else { + err = conn.SetKeepAlive(false) + } + if userTimeout > 0 { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x12, userTimeout) + }) + } + return err +} diff --git a/net/setcpinfo_linux.go b/net/setcpinfo_linux.go new file mode 100644 index 0000000..e6a19e3 --- /dev/null +++ b/net/setcpinfo_linux.go @@ -0,0 +1,39 @@ +//go:build !(windows && darwin) + +package net + +import ( + "net" + "syscall" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + if usingKeepAlive { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, keepAliveIdel) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, keepAlivePeriod) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, keepAliveCount) + if err != nil { + return + } + }) + } else { + err = conn.SetKeepAlive(false) + } + if userTimeout > 0 { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x12, userTimeout) + }) + } + return err +} diff --git a/net/setcpinfo_windows.go b/net/setcpinfo_windows.go new file mode 100644 index 0000000..79c0a07 --- /dev/null +++ b/net/setcpinfo_windows.go @@ -0,0 +1,33 @@ +//go:build windows + +package net + +import ( + "net" + "os" + "runtime" + "syscall" + "unsafe" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + if usingKeepAlive { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + err = rawConn.Control(func(fd uintptr) { + ka := syscall.TCPKeepalive{ + OnOff: 1, + Time: uint32(keepAliveIdel), + Interval: uint32(keepAlivePeriod), + } + ret := uint32(0) + size := uint32(unsafe.Sizeof(ka)) + err = syscall.WSAIoctl(syscall.Handle(fd), syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0) + runtime.KeepAlive(fd) + }) + return os.NewSyscallError("wsaioctl", err) + } + return conn.SetKeepAlive(false) +} diff --git a/net/sshjar.go b/net/sshjar.go new file mode 100644 index 0000000..5afd872 --- /dev/null +++ b/net/sshjar.go @@ -0,0 +1,145 @@ +package net + +import ( + "b612.me/starcrypto" + "b612.me/starlog" + "b612.me/starnet" + "crypto/elliptic" + "encoding/json" + "fmt" + "github.com/spf13/cobra" + "golang.org/x/crypto/ssh" + "net" + "os" + "os/signal" + "strings" +) + +var ( + listenAddr string + keyFile string + KeyPasswd string + outpath string + curlUrl string + curlArg []string +) + +func init() { + cmdSSHJar.Flags().StringVarP(&listenAddr, "listen", "l", "0.0.0.0:22", "监听地址") + cmdSSHJar.Flags().StringVarP(&keyFile, "key", "k", "", "私钥文件") + cmdSSHJar.Flags().StringVarP(&KeyPasswd, "passwd", "p", "", "私钥密码") + cmdSSHJar.Flags().StringVarP(&outpath, "output", "o", "", "输出文件") +} + +var cmdSSHJar = &cobra.Command{ + Use: "sshjar", + Short: "SSH蜜罐", + Long: "SSH蜜罐", + Run: func(cmd *cobra.Command, args []string) { + runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath) + }, +} + +func runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath string) { + var f *os.File + var err error + if outpath != "" { + f, err = os.OpenFile(outpath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + starlog.Errorf("Failed to open file %s (%s)", outpath, err) + return + } + } + defer f.Close() + config := &ssh.ServerConfig{ + // 密码验证回调函数 + PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { + starlog.Infof("Login attempt from %s with %s %s\n", c.RemoteAddr(), c.User(), string(pass)) + data := []string{c.RemoteAddr().String(), c.User(), string(pass)} + bts, _ := json.Marshal(data) + if f != nil { + f.Write(bts) + f.Write([]byte("\n")) + } + if curlUrl != "" { + go func() { + data := map[string]string{ + "ip": c.RemoteAddr().String(), + "user": c.User(), + "passwd": string(pass), + } + if curlArg != nil && len(curlArg) > 0 { + for _, v := range curlArg { + args := strings.SplitN(v, ":", 2) + if len(args) == 2 { + data[args[0]] = args[1] + } + } + starnet.Curl(starnet.NewRequests(curlUrl, []byte(starnet.BuildQuery(data)), "POST")) + } + }() + } + return nil, fmt.Errorf("password rejected for %q", c.User()) + }, + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + return nil, fmt.Errorf("public key rejected for %q", conn.User()) + }, + } + if keyFile == "" { + secKey, _, err := starcrypto.GenerateEcdsaKey(elliptic.P256()) + if err != nil { + starlog.Errorf("Failed to generate ECDSA key (%s)", err) + return + } + key, err := ssh.NewSignerFromKey(secKey) + if err != nil { + starlog.Errorf("Failed to generate signer from key (%s)", err) + return + } + config.AddHostKey(key) + } else { + keyByte, err := os.ReadFile(keyFile) + if err != nil { + starlog.Errorf("Failed to read private key from %s (%s)", keyFile, err) + return + } + var key ssh.Signer + if KeyPasswd != "" { + key, err = ssh.ParsePrivateKeyWithPassphrase(keyByte, []byte(KeyPasswd)) + } else { + key, err = ssh.ParsePrivateKey(keyByte) + } + if err != nil { + starlog.Errorf("Failed to load private key from %s (%s)", keyFile, err) + return + } + config.AddHostKey(key) + } + listener, err := net.Listen("tcp", listenAddr) + if err != nil { + starlog.Errorf("Failed to listen on %s (%s)", listenAddr, err) + return + } + starlog.Noticeln("SSH HoneyJar is listening on", listenAddr) + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, os.Kill) + for { + select { + case <-sig: + starlog.Noticef("SSH HoneyJar is shutting down") + listener.Close() + return + default: + } + conn, err := listener.Accept() + if err != nil { + continue + } + starlog.Infof("New connection from %s\n", conn.RemoteAddr()) + go func(conn net.Conn) { + ssh.NewServerConn(conn, config) + conn.Close() + }(conn) + } + +} diff --git a/net/sshjar_test.go b/net/sshjar_test.go new file mode 100644 index 0000000..fd91dbb --- /dev/null +++ b/net/sshjar_test.go @@ -0,0 +1,7 @@ +package net + +import "testing" + +func TestSSHJar(t *testing.T) { + //runSSHHoneyJar("0.0.0.0:22") +} diff --git a/net/tcpclient.go b/net/tcpclient.go new file mode 100644 index 0000000..876233a --- /dev/null +++ b/net/tcpclient.go @@ -0,0 +1,246 @@ +package net + +import ( + "b612.me/stario" + "b612.me/starlog" + "context" + "encoding/hex" + "fmt" + "net" + "os" + "path/filepath" + "runtime" + "strings" + "time" +) + +type TcpClient struct { + LocalAddr string + RemoteAddr string + UsingKeepAlive bool + KeepAlivePeriod int + KeepAliveIdel int + KeepAliveCount int + Interactive bool + UserTimeout int + ShowRecv bool + ShowAsHex bool + SaveToFolder string + Rmt *TcpConn + LogPath string + stopCtx context.Context + stopFn context.CancelFunc +} + +func (s *TcpClient) Close() error { + return s.Rmt.Close() +} + +func (s *TcpClient) handleInteractive() { + var currentCmd string + notifyMap := make(map[string]chan struct{}) + if !s.Interactive { + return + } + starlog.Infoln("Interactive mode enabled") + for { + select { + case <-s.stopCtx.Done(): + starlog.Infoln("Interactive mode stopped due to context done") + return + default: + } + cmd := stario.MessageBox("", "").MustString() + if cmd == "" { + continue + } + cmdf := strings.Fields(cmd) + switch cmdf[0] { + case "hex": + currentCmd = "hex" + starlog.Infoln("Switch to hex mode,send hex to remote client") + case "text": + currentCmd = "text" + starlog.Infoln("Switch to text mode,send text to remote client") + case "close": + if s.Rmt.TCPConn == nil { + starlog.Errorln("No client selected") + continue + } + s.Rmt.TCPConn.Close() + starlog.Infof("Client %s closed\n", s.Rmt.RemoteAddr().String()) + s.Rmt = nil + currentCmd = "" + case "startauto": + if s.Rmt == nil { + starlog.Errorln("No client selected") + continue + } + notifyMap[s.Rmt.RemoteAddr().String()] = make(chan struct{}) + go func(conn *TcpConn) { + for { + select { + case <-notifyMap[conn.RemoteAddr().String()]: + starlog.Infoln("Auto send stopped") + return + default: + } + _, err := conn.Write([]byte(strings.Repeat("B612", 256))) + if err != nil { + starlog.Errorln("Write error:", err) + return + } + } + }(s.Rmt) + starlog.Infoln("Auto send started") + case "closeauto": + if s.Rmt == nil { + starlog.Errorln("No client selected") + continue + } + close(notifyMap[s.Rmt.RemoteAddr().String()]) + case "send": + if s.Rmt == nil { + starlog.Errorln("No client selected") + continue + } + if currentCmd == "hex" { + data, err := hex.DecodeString(strings.TrimSpace(strings.TrimPrefix(cmd, "send"))) + if err != nil { + starlog.Errorln("Hex decode error:", err) + continue + } + _, err = s.Rmt.Write(data) + if err != nil { + starlog.Errorln("Write error:", err) + } else { + if s.Rmt.f != nil { + s.Rmt.f.Write([]byte(time.Now().String() + " send\n")) + s.Rmt.f.Write(data) + s.Rmt.f.Write([]byte("\n")) + } + } + } else { + _, err := s.Rmt.Write([]byte(strings.TrimSpace(strings.TrimPrefix(cmd, "send")))) + if err != nil { + starlog.Errorln("Write error:", err) + } else { + if s.Rmt.f != nil { + s.Rmt.f.Write([]byte(time.Now().String() + " send\n")) + s.Rmt.f.Write([]byte(cmdf[1])) + s.Rmt.f.Write([]byte("\n")) + } + } + } + starlog.Infof("Send to %s success\n", s.Rmt.RemoteAddr().String()) + } + } +} + +func (s *TcpClient) Run() error { + var err error + s.stopCtx, s.stopFn = context.WithCancel(context.Background()) + if s.LogPath != "" { + err := starlog.SetLogFile(s.LogPath, starlog.Std, true) + if err != nil { + starlog.Errorln("SetLogFile error:", err) + return fmt.Errorf("SetLogFile error: %w", err) + } + } + var localAddr *net.TCPAddr + if s.LocalAddr != "" { + localAddr, err = net.ResolveTCPAddr("tcp", s.LocalAddr) + if err != nil { + starlog.Errorln("ResolveTCPAddr error:", err) + return fmt.Errorf("ResolveTCPAddr error: %w", err) + } + } + remoteAddr, err := net.ResolveTCPAddr("tcp", s.RemoteAddr) + if err != nil { + starlog.Errorln("ResolveTCPAddr error:", err) + return fmt.Errorf("ResolveTCPAddr error: %w", err) + } + + conn, err := net.DialTCP("tcp", localAddr, remoteAddr) + if err != nil { + starlog.Errorln("Dial TCP error:", err) + return fmt.Errorf("Dial TCP error: %w", err) + } + starlog.Infof("Connected to %s LocalAddr: %s\n", conn.RemoteAddr().String(), conn.LocalAddr().String()) + if s.Interactive { + go s.handleInteractive() + } + s.Rmt = s.getTcpConn(conn) + + s.handleConn(s.Rmt) + return nil +} + +func (s *TcpClient) getTcpConn(conn *net.TCPConn) *TcpConn { + var err error + var f *os.File + if s.SaveToFolder != "" { + f, err = os.Create(filepath.Join(s.SaveToFolder, strings.ReplaceAll(conn.RemoteAddr().String(), ":", "_"))) + if err != nil { + starlog.Errorf("Create file error for %s: %v\n", conn.RemoteAddr().String(), err) + } + } + return &TcpConn{ + TCPConn: conn, + f: f, + } +} + +func (s *TcpClient) handleConn(conn *TcpConn) { + var err error + log := starlog.Std.NewFlag() + err = SetTcpInfo(conn.TCPConn, s.UsingKeepAlive, s.KeepAliveIdel, s.KeepAlivePeriod, s.KeepAliveCount, s.UserTimeout) + if err != nil { + log.Errorf("SetTcpInfo error for %s: %v\n", conn.RemoteAddr().String(), err) + conn.Close() + return + } + log.Infof("SetKeepAlive success for %s\n", conn.RemoteAddr().String()) + log.Infof("KeepAlivePeriod: %d, KeepAliveIdel: %d, KeepAliveCount: %d, UserTimeout: %d\n", s.KeepAlivePeriod, s.KeepAliveIdel, s.KeepAliveCount, s.UserTimeout) + if runtime.GOOS != "linux" { + log.Warningln("keepAliveCount and userTimeout only work on linux") + } + + for { + select { + case <-s.stopCtx.Done(): + log.Infof("Connection from %s closed due to context done\n", conn.RemoteAddr().String()) + conn.Close() + return + default: + } + buf := make([]byte, 8192) + n, err := conn.Read(buf) + if err != nil { + log.Errorf("Read error for %s: %v\n", conn.RemoteAddr().String(), err) + conn.Close() + return + } + if n > 0 { + if s.ShowRecv { + if s.ShowAsHex { + log.Printf("Recv from %s: %x\n", conn.RemoteAddr().String(), buf[:n]) + } else { + log.Printf("Recv from %s: %s\n", conn.RemoteAddr().String(), string(buf[:n])) + } + } + if conn.f != nil { + conn.f.Write([]byte(time.Now().String() + " recv\n")) + conn.f.Write(buf[:n]) + conn.f.Write([]byte("\n")) + } + } + } +} + +func (s *TcpClient) Stop() { + s.stopFn() + if s.Rmt != nil { + s.Rmt.Close() + } +} diff --git a/net/tcpcmd.go b/net/tcpcmd.go new file mode 100644 index 0000000..e8eb08a --- /dev/null +++ b/net/tcpcmd.go @@ -0,0 +1,80 @@ +package net + +import ( + "b612.me/starlog" + "github.com/spf13/cobra" + "os" + "os/signal" + "time" +) + +var ( + tcps TcpServer + tcpc TcpClient +) + +func init() { + CmdTcps.Flags().StringVarP(&tcps.LocalAddr, "local", "l", "0.0.0.0:29127", "本地地址") + CmdTcps.Flags().BoolVarP(&tcps.UsingKeepAlive, "keepalive", "k", true, "启用KeepAlive") + CmdTcps.Flags().IntVarP(&tcps.KeepAlivePeriod, "keepalive-period", "p", 10, "KeepAlive重试周期(秒)") + CmdTcps.Flags().IntVarP(&tcps.KeepAliveIdel, "keepalive-idel", "i", 15, "KeepAlive空闲时间(秒)") + CmdTcps.Flags().IntVarP(&tcps.KeepAliveCount, "keepalive-count", "c", 3, "KeepAlive次数)") + CmdTcps.Flags().BoolVarP(&tcps.Interactive, "interactive", "I", false, "交互模式") + CmdTcps.Flags().IntVarP(&tcps.UserTimeout, "user-timeout", "u", 0, "用户超时时间(毫秒)") + CmdTcps.Flags().BoolVarP(&tcps.ShowRecv, "show-recv", "r", true, "显示接收数据") + CmdTcps.Flags().BoolVarP(&tcps.ShowAsHex, "show-hex", "H", false, "显示十六进制") + CmdTcps.Flags().StringVarP(&tcps.SaveToFolder, "save", "s", "", "保存到文件夹") + CmdTcps.Flags().StringVarP(&tcps.LogPath, "log", "L", "", "日志文件路径") + Cmd.AddCommand(CmdTcps) + + CmdTcpc.Flags().StringVarP(&tcpc.LocalAddr, "local", "l", "", "本地地址") + CmdTcpc.Flags().BoolVarP(&tcpc.UsingKeepAlive, "keepalive", "k", true, "启用KeepAlive") + CmdTcpc.Flags().IntVarP(&tcpc.KeepAlivePeriod, "keepalive-period", "p", 1, "KeepAlive重试周期(秒)") + CmdTcpc.Flags().IntVarP(&tcpc.KeepAliveIdel, "keepalive-idel", "i", 15, "KeepAlive空闲时间(秒)") + CmdTcpc.Flags().IntVarP(&tcpc.KeepAliveCount, "keepalive-count", "c", 3, "KeepAlive次数") + CmdTcpc.Flags().BoolVarP(&tcpc.Interactive, "interactive", "I", false, "交互模式") + CmdTcpc.Flags().IntVarP(&tcpc.UserTimeout, "user-timeout", "u", 0, "用户超时时间(毫秒)") + CmdTcpc.Flags().BoolVarP(&tcpc.ShowRecv, "show-recv", "r", true, "显示接收数据") + CmdTcpc.Flags().BoolVarP(&tcpc.ShowAsHex, "show-hex", "H", false, "显示十六进制") + CmdTcpc.Flags().StringVarP(&tcpc.SaveToFolder, "save", "s", "", "保存到文件夹") + CmdTcpc.Flags().StringVarP(&tcpc.LogPath, "log", "L", "", "日志文件路径") + + Cmd.AddCommand(CmdTcpc) +} + +var CmdTcps = &cobra.Command{ + Use: "tcps", + Short: "TCP服务端", + Run: func(cmd *cobra.Command, args []string) { + go func() { + s := make(chan os.Signal, 1) + signal.Notify(s, os.Interrupt, os.Kill) + <-s + tcps.Stop() + time.Sleep(5 * time.Second) + os.Exit(0) + }() + tcps.Run() + }, +} + +var CmdTcpc = &cobra.Command{ + Use: "tcpc", + Short: "TCP客户端", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + starlog.Errorln("请指定目标地址") + return + } + tcpc.RemoteAddr = args[0] + go func() { + s := make(chan os.Signal, 1) + signal.Notify(s, os.Interrupt, os.Kill) + <-s + tcpc.Stop() + time.Sleep(5 * time.Second) + os.Exit(0) + }() + tcpc.Run() + }, +} diff --git a/net/tcpserver.go b/net/tcpserver.go new file mode 100644 index 0000000..77af3d1 --- /dev/null +++ b/net/tcpserver.go @@ -0,0 +1,285 @@ +package net + +import ( + "b612.me/stario" + "b612.me/starlog" + "context" + "encoding/hex" + "fmt" + "net" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "time" +) + +type TcpConn struct { + *net.TCPConn + f *os.File +} + +type TcpServer struct { + LocalAddr string + UsingKeepAlive bool + KeepAlivePeriod int + KeepAliveIdel int + KeepAliveCount int + sync.Mutex + Clients map[string]*TcpConn + Interactive bool + UserTimeout int + ShowRecv bool + ShowAsHex bool + SaveToFolder string + Listen *net.TCPListener + LogPath string + stopCtx context.Context + stopFn context.CancelFunc +} + +func (s *TcpServer) Close() error { + return s.Listen.Close() +} + +func (s *TcpServer) handleInteractive() { + var conn *TcpConn + var currentCmd string + notifyMap := make(map[string]chan struct{}) + if !s.Interactive { + return + } + starlog.Infoln("Interactive mode enabled") + for { + select { + case <-s.stopCtx.Done(): + starlog.Infoln("Interactive mode stopped due to context done") + return + default: + } + cmd := stario.MessageBox("", "").MustString() + if cmd == "" { + continue + } + cmdf := strings.Fields(cmd) + switch cmdf[0] { + case "list": + s.Lock() + for k, v := range s.Clients { + starlog.Green("Client %s: %s\n", k, v.RemoteAddr().String()) + } + s.Unlock() + case "use": + if len(cmdf) < 2 { + starlog.Errorln("use command need a client address") + continue + } + conn = s.Clients[cmdf[1]] + if conn == nil { + starlog.Errorln("Client not found") + continue + } + starlog.Infof("Using client %s\n", conn.RemoteAddr().String()) + case "hex": + currentCmd = "hex" + starlog.Infoln("Switch to hex mode,send hex to remote client") + case "text": + currentCmd = "text" + starlog.Infoln("Switch to text mode,send text to remote client") + case "close": + if conn.TCPConn == nil { + starlog.Errorln("No client selected") + continue + } + conn.TCPConn.Close() + starlog.Infof("Client %s closed\n", conn.RemoteAddr().String()) + conn = nil + currentCmd = "" + case "startauto": + if conn == nil { + starlog.Errorln("No client selected") + continue + } + notifyMap[conn.RemoteAddr().String()] = make(chan struct{}) + go func(conn *TcpConn) { + for { + select { + case <-notifyMap[conn.RemoteAddr().String()]: + starlog.Infoln("Auto send stopped") + return + default: + } + _, err := conn.Write([]byte(strings.Repeat("B612", 256))) + if err != nil { + starlog.Errorln("Write error:", err) + return + } + } + }(conn) + starlog.Infoln("Auto send started") + case "closeauto": + if conn == nil { + starlog.Errorln("No client selected") + continue + } + close(notifyMap[conn.RemoteAddr().String()]) + case "send": + if conn == nil { + starlog.Errorln("No client selected") + continue + } + if currentCmd == "hex" { + data, err := hex.DecodeString(strings.TrimSpace(strings.TrimPrefix(cmd, "send"))) + if err != nil { + starlog.Errorln("Hex decode error:", err) + continue + } + _, err = conn.Write(data) + if err != nil { + starlog.Errorln("Write error:", err) + } else { + if conn.f != nil { + conn.f.Write([]byte(time.Now().String() + " send\n")) + conn.f.Write(data) + conn.f.Write([]byte("\n")) + } + } + } else { + _, err := conn.Write([]byte(strings.TrimSpace(strings.TrimPrefix(cmd, "send")))) + if err != nil { + starlog.Errorln("Write error:", err) + } else { + if conn.f != nil { + conn.f.Write([]byte(time.Now().String() + " send\n")) + conn.f.Write([]byte(cmdf[1])) + conn.f.Write([]byte("\n")) + } + } + } + starlog.Infof("Send to %s success\n", conn.RemoteAddr().String()) + } + } +} + +func (s *TcpServer) Run() error { + s.stopCtx, s.stopFn = context.WithCancel(context.Background()) + if s.LogPath != "" { + err := starlog.SetLogFile(s.LogPath, starlog.Std, true) + if err != nil { + starlog.Errorln("SetLogFile error:", err) + return fmt.Errorf("SetLogFile error: %w", err) + } + } + s.Clients = make(map[string]*TcpConn) + tcpAddr, err := net.ResolveTCPAddr("tcp", s.LocalAddr) + if err != nil { + starlog.Errorln("ResolveTCPAddr error:", err) + return fmt.Errorf("ResolveTCPAddr error: %w", err) + } + s.Listen, err = net.ListenTCP("tcp", tcpAddr) + if err != nil { + starlog.Errorln("ListenTCP error:", err) + return fmt.Errorf("ListenTCP error: %w", err) + } + starlog.Infof("TcpServer listen on %s\n", s.LocalAddr) + if s.Interactive { + go s.handleInteractive() + } + for { + select { + case <-s.stopCtx.Done(): + starlog.Infoln("TcpServer stopped due to context done") + return s.Listen.Close() + default: + } + conn, err := s.Listen.AcceptTCP() + if err != nil { + starlog.Errorln("AcceptTCP error:", err) + continue + } + starlog.Infof("Accept new connection from %s", conn.RemoteAddr().String()) + s.Lock() + s.Clients[conn.RemoteAddr().String()] = s.getTcpConn(conn) + s.Unlock() + go s.handleConn(s.Clients[conn.RemoteAddr().String()]) + } +} + +func (s *TcpServer) getTcpConn(conn *net.TCPConn) *TcpConn { + var err error + var f *os.File + if s.SaveToFolder != "" { + f, err = os.Create(filepath.Join(s.SaveToFolder, strings.ReplaceAll(conn.RemoteAddr().String(), ":", "_"))) + if err != nil { + starlog.Errorf("Create file error for %s: %v\n", conn.RemoteAddr().String(), err) + } + } + return &TcpConn{ + TCPConn: conn, + f: f, + } +} + +func (s *TcpServer) handleConn(conn *TcpConn) { + var err error + log := starlog.Std.NewFlag() + err = SetTcpInfo(conn.TCPConn, s.UsingKeepAlive, s.KeepAliveIdel, s.KeepAlivePeriod, s.KeepAliveCount, s.UserTimeout) + if err != nil { + log.Errorf("SetTcpInfo error for %s: %v\n", conn.RemoteAddr().String(), err) + s.Lock() + delete(s.Clients, conn.RemoteAddr().String()) + s.Unlock() + conn.Close() + return + } + log.Infof("SetKeepAlive success for %s\n", conn.RemoteAddr().String()) + log.Infof("KeepAlivePeriod: %d, KeepAliveIdel: %d, KeepAliveCount: %d, UserTimeout: %d\n", s.KeepAlivePeriod, s.KeepAliveIdel, s.KeepAliveCount, s.UserTimeout) + if runtime.GOOS != "linux" { + log.Warningln("keepAliveCount and userTimeout only work on linux") + } + + for { + select { + case <-s.stopCtx.Done(): + log.Infof("Connection from %s closed due to context done\n", conn.RemoteAddr().String()) + s.Lock() + delete(s.Clients, conn.RemoteAddr().String()) + s.Unlock() + conn.Close() + return + default: + } + buf := make([]byte, 8192) + n, err := conn.Read(buf) + if err != nil { + log.Errorf("Read error for %s: %v\n", conn.RemoteAddr().String(), err) + s.Lock() + delete(s.Clients, conn.RemoteAddr().String()) + s.Unlock() + conn.Close() + return + } + if n > 0 { + if s.ShowRecv { + if s.ShowAsHex { + log.Printf("Recv from %s: %x\n", conn.RemoteAddr().String(), buf[:n]) + } else { + log.Printf("Recv from %s: %s\n", conn.RemoteAddr().String(), string(buf[:n])) + } + } + if conn.f != nil { + conn.f.Write([]byte(time.Now().String() + " recv\n")) + conn.f.Write(buf[:n]) + conn.f.Write([]byte("\n")) + } + } + } +} + +func (s *TcpServer) Stop() { + s.stopFn() + if s.Listen != nil { + s.Close() + } +} diff --git a/netforward/cmd.go b/netforward/cmd.go index 81f4785..0203ce5 100644 --- a/netforward/cmd.go +++ b/netforward/cmd.go @@ -23,6 +23,12 @@ func init() { CmdNetforward.Flags().BoolVarP(&f.EnableUDP, "enable-udp-forward", "u", true, "enable udp forward mode") CmdNetforward.Flags().Int64VarP(&dialTimeout, "dial-timeout", "d", 10000, "dial timeout milliseconds") CmdNetforward.Flags().Int64VarP(&udpTimeout, "udp-timeout", "D", 60000, "udp connection timeout milliseconds") + CmdNetforward.Flags().BoolVarP(&f.UsingKeepAlive, "keepalive", "k", true, "enable keepalive") + CmdNetforward.Flags().IntVarP(&f.KeepAlivePeriod, "keepalive-period", "P", 10, "keepalive retry period (seconds)") + CmdNetforward.Flags().IntVarP(&f.KeepAliveIdel, "keepalive-idel", "I", 15, "keepalive idel time (seconds)") + CmdNetforward.Flags().IntVarP(&f.KeepAliveCount, "keepalive-count", "C", 3, "keepalive count") + CmdNetforward.Flags().IntVarP(&f.UserTimeout, "user-timeout", "U", 0, "user timeout (milliseconds)") + CmdNetforward.Flags().BoolVarP(&f.IgnoreEof, "ignore-eof", "E", false, "ignore eof") } var CmdNetforward = &cobra.Command{ diff --git a/netforward/forward.go b/netforward/forward.go index d94d27d..0b41135 100644 --- a/netforward/forward.go +++ b/netforward/forward.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "io" "net" "strconv" "strings" @@ -23,11 +24,18 @@ type NetForward struct { DelayMilSec int DelayToward int StdinMode bool + IgnoreEof bool DialTimeout time.Duration UDPTimeout time.Duration stopCtx context.Context stopFn context.CancelFunc running int32 + + KeepAlivePeriod int + KeepAliveIdel int + KeepAliveCount int + UserTimeout int + UsingKeepAlive bool } func (n *NetForward) Close() { @@ -145,6 +153,12 @@ func (n *NetForward) runTCP() error { log.Infof("Delay %d ms\n", n.DelayMilSec) time.Sleep(time.Millisecond * time.Duration(n.DelayMilSec)) } + err = SetTcpInfo(conn.(*net.TCPConn), n.UsingKeepAlive, n.KeepAliveIdel, n.KeepAlivePeriod, n.KeepAliveCount, n.UserTimeout) + if err != nil { + log.Errorf("SetTcpInfo error for %s: %v\n", conn.RemoteAddr().String(), err) + conn.Close() + continue + } go func(conn net.Conn) { rmt, err := net.DialTimeout("tcp", n.RemoteURI, n.DialTimeout) if err != nil { @@ -152,9 +166,17 @@ func (n *NetForward) runTCP() error { conn.Close() return } + err = SetTcpInfo(rmt.(*net.TCPConn), n.UsingKeepAlive, n.KeepAliveIdel, n.KeepAlivePeriod, n.KeepAliveCount, n.UserTimeout) + if err != nil { + log.Errorf("SetTcpInfo error for %s: %v\n", conn.RemoteAddr().String(), err) + rmt.Close() + return + } log.Infof("TCP Connect %s <==> %s\n", conn.RemoteAddr().String(), rmt.RemoteAddr().String()) n.copy(rmt, conn) log.Noticef("TCP Connection Closed %s <==> %s\n", conn.RemoteAddr().String(), n.RemoteURI) + conn.Close() + rmt.Close() }(conn) } } @@ -289,6 +311,9 @@ func (n *NetForward) copy(dst, src net.Conn) { for { count, err := src.Read(bufsize) if err != nil { + if n.IgnoreEof && err == io.EOF { + continue + } dst.Close() src.Close() return @@ -310,6 +335,9 @@ func (n *NetForward) copy(dst, src net.Conn) { for { count, err := dst.Read(bufsize) if err != nil { + if n.IgnoreEof && err == io.EOF { + continue + } src.Close() dst.Close() return diff --git a/netforward/setcpinfo_darwin.go b/netforward/setcpinfo_darwin.go new file mode 100644 index 0000000..08ee890 --- /dev/null +++ b/netforward/setcpinfo_darwin.go @@ -0,0 +1,35 @@ +//go:build darwin + +package netforward + +import ( + "net" + "syscall" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + if usingKeepAlive { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, keepAliveIdel) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x101, keepAlivePeriod) + if err != nil { + return + } + }) + } else { + err = conn.SetKeepAlive(false) + } + if userTimeout > 0 { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x12, userTimeout) + }) + } + return err +} diff --git a/netforward/setcpinfo_linux.go b/netforward/setcpinfo_linux.go new file mode 100644 index 0000000..0d2f68f --- /dev/null +++ b/netforward/setcpinfo_linux.go @@ -0,0 +1,39 @@ +//go:build !(windows && darwin) + +package netforward + +import ( + "net" + "syscall" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + if usingKeepAlive { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, keepAliveIdel) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, keepAlivePeriod) + if err != nil { + return + } + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, keepAliveCount) + if err != nil { + return + } + }) + } else { + err = conn.SetKeepAlive(false) + } + if userTimeout > 0 { + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, 0x12, userTimeout) + }) + } + return err +} diff --git a/netforward/setcpinfo_windows.go b/netforward/setcpinfo_windows.go new file mode 100644 index 0000000..eee8098 --- /dev/null +++ b/netforward/setcpinfo_windows.go @@ -0,0 +1,33 @@ +//go:build windows + +package netforward + +import ( + "net" + "os" + "runtime" + "syscall" + "unsafe" +) + +func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + if usingKeepAlive { + rawConn, err := conn.SyscallConn() + if err != nil { + return err + } + err = rawConn.Control(func(fd uintptr) { + ka := syscall.TCPKeepalive{ + OnOff: 1, + Time: uint32(keepAliveIdel), + Interval: uint32(keepAlivePeriod), + } + ret := uint32(0) + size := uint32(unsafe.Sizeof(ka)) + err = syscall.WSAIoctl(syscall.Handle(fd), syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0) + runtime.KeepAlive(fd) + }) + return os.NewSyscallError("wsaioctl", err) + } + return conn.SetKeepAlive(false) +} diff --git a/smtpclient/smtp.go b/smtpclient/smtp.go index 4f9697e..23b0968 100644 --- a/smtpclient/smtp.go +++ b/smtpclient/smtp.go @@ -28,6 +28,8 @@ var useHTML bool var user, pwd, server string var skipInsecure, usingFile bool var useTLS int +var hostname string +var autoHostname bool func init() { Cmd.Flags().BoolVarP(&usingFile, "file", "F", false, "using file") @@ -45,6 +47,8 @@ func init() { Cmd.Flags().StringVarP(&server, "server", "S", "127.0.0.1:25", "server") Cmd.Flags().IntVarP(&useTLS, "tls", "l", 0, "use tls,1 means use tls,2 means use starttls,other means not use tls") Cmd.Flags().BoolVarP(&skipInsecure, "skip-insecure", "i", false, "skip insecure") + Cmd.Flags().StringVarP(&hostname, "hostname", "n", "", "hostname") + Cmd.Flags().BoolVarP(&autoHostname, "auto-hostname", "N", false, "auto hostname") } func run() { @@ -112,16 +116,28 @@ func run() { } } } + + if autoHostname && hostname == "" { + hostname = strings.Split(server, ":")[0] + } var auth smtp.Auth if user != "" && pwd != "" { - auth = smtp.PlainAuth("", user, pwd, server) + auth = smtp.PlainAuth("", user, pwd, hostname) } + switch useTLS { case 1: - err = mail.SendWithTLS(server, auth, &tls.Config{InsecureSkipVerify: skipInsecure}) + starlog.Noticef("Mail send method:TLS InsecureSkipVerify:%v ServerName:%v\n", skipInsecure, hostname) + err = mail.SendWithTLS(server, auth, &tls.Config{ + InsecureSkipVerify: skipInsecure, + ServerName: hostname, + }) case 2: - err = mail.SendWithStartTLS(server, auth, &tls.Config{InsecureSkipVerify: skipInsecure}) + starlog.Noticef("Mail send method:StartTLS InsecureSkipVerify:%v ServerName:%v\n", skipInsecure, hostname) + err = mail.SendWithStartTLS(server, auth, &tls.Config{InsecureSkipVerify: skipInsecure, + ServerName: hostname}) default: + starlog.Noticef("Mail send method:Normal\n") err = mail.Send(server, auth) } if err != nil { diff --git a/tls/cert.go b/tls/cert.go index 999d0ae..107a71e 100644 --- a/tls/cert.go +++ b/tls/cert.go @@ -9,18 +9,33 @@ import ( "encoding/pem" "fmt" "github.com/spf13/cobra" + "golang.org/x/net/idna" + "golang.org/x/net/proxy" + "net" "os" "path/filepath" + "strconv" "strings" + "sync" "time" ) var hideDetail bool var dump string +var reqRawIP int +var timeoutMillSec int +var socks5 string +var socks5Auth string +var showCA bool func init() { Cmd.Flags().BoolVarP(&hideDetail, "hide-detail", "H", false, "隐藏证书详细信息") Cmd.Flags().StringVarP(&dump, "dump", "d", "", "将证书保存到文件") + Cmd.Flags().IntVarP(&reqRawIP, "resolve-ip", "r", 0, "使用解析到的IP地址进行连接,输入数字表示使用解析到的第几个IP地址") + Cmd.Flags().IntVarP(&timeoutMillSec, "timeout", "t", 5000, "连接超时时间(毫秒)") + Cmd.Flags().StringVarP(&socks5, "socks5", "p", "", "socks5代理,示例:127.0.0.1:1080") + Cmd.Flags().StringVarP(&socks5Auth, "socks5-auth", "A", "", "socks5代理认证,示例:username:password") + Cmd.Flags().BoolVarP(&showCA, "show-ca", "c", false, "显示CA证书") } var Cmd = &cobra.Command{ @@ -29,25 +44,155 @@ var Cmd = &cobra.Command{ Long: "查看TLS证书信息", Run: func(cmd *cobra.Command, args []string) { for _, target := range args { - showTls(target, !hideDetail, dump) + showTls(target, !hideDetail, showCA, reqRawIP, dump, time.Duration(timeoutMillSec)*time.Millisecond) } }, } -func showTls(target string, showDetail bool, dumpPath string) { - if !strings.Contains(target, ":") { - target += ":443" +func showTls(target string, showDetail, showCA bool, reqRawIP int, dumpPath string, timeout time.Duration) { + var err error + { + sp := strings.Split(target, ":") + if len(sp) < 2 { + target = target + ":443" + } else { + if _, err := strconv.Atoi(sp[len(sp)-1]); err != nil { + target = target + ":443" + } + } + } + if timeout == 0 { + timeout = 5 * time.Second + } + hostname := strings.Split(target, ":")[0] + if strings.Count(target, ":") == 2 { + strs := strings.Split(target, ":") + if len(strs) != 3 { + starlog.Errorln("invalid target format") + return + } + target = strs[0] + ":" + strs[2] + hostname = strs[1] + } + if reqRawIP > 0 { + domain := strings.Split(target, ":")[0] + ips, err := net.LookupIP(domain) + if err != nil { + starlog.Errorln("failed to resolve domain: " + err.Error()) + return + } + if len(ips) == 0 { + starlog.Errorln("no ip found for domain") + return + } + for _, v := range ips { + starlog.Infof("解析到的IP地址为: %s\n", v.String()) + } + if reqRawIP > len(ips) { + reqRawIP = len(ips) + } + target = ips[reqRawIP-1].String() + ":443" + hostname = ips[reqRawIP-1].String() + starlog.Noticeln("使用解析到的IP地址进行连接:", target) + } + starlog.Noticef("将使用如下地址连接:%s ; ServerName: %s\n", target, hostname) + punyCode, err := idna.ToASCII(hostname) + if err == nil { + if punyCode != hostname { + starlog.Infoln("检测到域名中含有非ASCII字符,PunyCode转换后为:", punyCode) + hostname = punyCode + } } starlog.Infof("正在连接服务器: %s\n", target) - conn, err := tls.Dial("tcp", target, &tls.Config{ - InsecureSkipVerify: true, - }) - if err != nil { - starlog.Errorln("failed to connect: " + err.Error()) - return + var netDialer = &net.Dialer{ + Timeout: timeout, + } + var socksDialer *proxy.Dialer + if socks5 != "" { + var auth *proxy.Auth + if socks5Auth != "" { + up := strings.SplitN(socks5Auth, ":", 2) + if len(up) == 2 { + auth = &proxy.Auth{ + User: up[0], + Password: up[1], + } + } else { + starlog.Errorln("socks5认证格式错误") + return + } + } + s5Dial, err := proxy.SOCKS5("tcp", socks5, auth, proxy.Direct) + if err == nil { + socksDialer = &s5Dial + } else { + starlog.Errorln("socks5代理错误:", err) + return + } + } + var verifyErr error + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + var conn *tls.Conn + if socksDialer == nil { + conn, verifyErr = tls.DialWithDialer(netDialer, "tcp", target, &tls.Config{ + InsecureSkipVerify: false, + ServerName: hostname, + MinVersion: tls.VersionSSL30, + }) + if verifyErr == nil { + conn.Close() + } + } else { + con, err := (*socksDialer).Dial("tcp", target) + if err != nil { + verifyErr = err + return + } + conn = tls.Client(con, &tls.Config{ + InsecureSkipVerify: false, + ServerName: hostname, + MinVersion: tls.VersionSSL30, + }) + verifyErr = conn.Handshake() + con.Close() + } + }() + var conn *tls.Conn + + if socksDialer == nil { + conn, err = tls.DialWithDialer(netDialer, "tcp", target, &tls.Config{ + InsecureSkipVerify: true, + ServerName: hostname, + MinVersion: tls.VersionSSL30, + }) + if err != nil { + starlog.Errorln("failed to connect: " + err.Error()) + return + } + } else { + con, err := (*socksDialer).Dial("tcp", target) + if err != nil { + starlog.Errorln("failed to connect: " + err.Error()) + return + } + defer con.Close() + conn = tls.Client(con, &tls.Config{ + InsecureSkipVerify: true, + ServerName: hostname, + MinVersion: tls.VersionSSL30, + }) + err = conn.Handshake() + if err != nil { + starlog.Errorln("failed to handshake: " + err.Error()) + return + } + } defer conn.Close() - starlog.Infoln("连接成功,正在获取证书信息") + starlog.Infof("连接成功,对方IP:%s,正在获取证书信息\n", conn.RemoteAddr().String()) certs := conn.ConnectionState().PeerCertificates if len(certs) == 0 { starlog.Errorln("no certificate found") @@ -58,11 +203,11 @@ func showTls(target string, showDetail bool, dumpPath string) { switch state.Version { case tls.VersionSSL30: - starlog.Infoln("当前TLS版本: SSL 3.0") + starlog.Warningln("当前TLS版本: SSL 3.0") case tls.VersionTLS10: - starlog.Infoln("当前TLS版本: TLS 1.0") + starlog.Warningln("当前TLS版本: TLS 1.0") case tls.VersionTLS11: - starlog.Infoln("当前TLS版本: TLS 1.1") + starlog.Warningln("当前TLS版本: TLS 1.1") case tls.VersionTLS12: starlog.Infoln("当前TLS版本: TLS 1.2") case tls.VersionTLS13: @@ -123,13 +268,22 @@ func showTls(target string, showDetail bool, dumpPath string) { starlog.Infoln("当前加密套件:", state.CipherSuite) } starlog.Infoln("服务器名称:", state.ServerName) - + wg.Wait() + if verifyErr != nil { + starlog.Red("证书验证失败: " + verifyErr.Error()) + } else { + starlog.Green("证书验证成功") + } if showDetail { for _, c := range certs { - if c.IsCA { + if c.IsCA && !showCA { continue } - fmt.Printf("----------\n证书基础信息: %+v\n", c.Subject) + fmt.Printf("----------\n") + if c.IsCA { + fmt.Println("!这是一个CA证书!") + } + fmt.Printf("证书基础信息: %+v\n", c.Subject) fmt.Printf("证书颁发者: %+v\n", c.Issuer) fmt.Printf("证书生效时间: %+v 距今:%.1f天\n", c.NotBefore.In(time.Local), time.Since(c.NotBefore).Hours()/24) fmt.Printf("证书过期时间: %+v 剩余:%.1f天\n", c.NotAfter.In(time.Local), c.NotAfter.Sub(time.Now()).Hours()/24) diff --git a/whois/cmd.go b/whois/cmd.go index ddaf91b..7075660 100644 --- a/whois/cmd.go +++ b/whois/cmd.go @@ -1,19 +1,28 @@ package whois import ( + "b612.me/starlog" "b612.me/staros" "github.com/likexian/whois" "github.com/spf13/cobra" + "golang.org/x/net/proxy" "os" + "strings" "time" ) var timeout int var output string +var whoisServer []string +var socks5 string +var socks5Auth string func init() { Cmd.Flags().IntVarP(&timeout, "timeout", "t", 20, "超时时间") Cmd.Flags().StringVarP(&output, "output", "o", "", "输出文件夹") + Cmd.Flags().StringSliceVarP(&whoisServer, "server", "s", nil, "whois服务器") + Cmd.Flags().StringVarP(&socks5, "socks5", "p", "", "socks5代理,示例:127.0.0.1:1080") + Cmd.Flags().StringVarP(&socks5Auth, "socks5-auth", "A", "", "socks5代理认证,示例:username:password") } var Cmd = &cobra.Command{ @@ -30,9 +39,31 @@ var Cmd = &cobra.Command{ output = "" } c := whois.NewClient() + if socks5 != "" { + var auth *proxy.Auth + if socks5Auth != "" { + up := strings.SplitN(socks5Auth, ":", 2) + if len(up) == 2 { + auth = &proxy.Auth{ + User: up[0], + Password: up[1], + } + } else { + starlog.Errorln("socks5认证格式错误") + return + } + } + s5Dial, err := proxy.SOCKS5("tcp", socks5, auth, proxy.Direct) + if err == nil { + c.SetDialer(s5Dial) + } else { + starlog.Errorln("socks5代理错误:", err) + return + } + } c.SetTimeout(time.Second * time.Duration(timeout)) for _, v := range args { - data, err := c.Whois(v) + data, err := c.Whois(v, whoisServer...) cmd.Println("Query:", v) if err != nil { cmd.Println("查询失败:", err)