├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cmd └── scanner.go ├── connect.go ├── connect_test.go ├── dialer.go ├── dialer_test.go ├── examples └── socks5 │ ├── cracker.go │ ├── main.go │ ├── password.txt │ └── username.txt ├── generator.go ├── generator_test.go ├── go.mod ├── go.sum ├── init.go ├── init_windows.go ├── install.sh ├── interface.go ├── interface_linux.go ├── interface_test.go ├── interface_windows.go ├── options.go ├── pfring.go ├── pprof_test.go ├── routing.go ├── routing_linux.go ├── routing_test.go ├── routing_windows.go ├── scanner.go ├── scanner_test.go ├── syn.go └── syn_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.13.x 4 | - 1.12.x 5 | - 1.11.x 6 | sudo: true 7 | env: 8 | - GO111MODULE=on 9 | before_install: 10 | - sudo apt-get -y install libpcap-dev 11 | script: 12 | - go build 13 | # gopacket routing_linux panic -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 For What 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast-scanner 2 | [![Build Status](https://travis-ci.org/For-ACGN/fast-scanner.svg?branch=master)](https://travis-ci.org/For-ACGN/fast-scanner) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/For-ACGN/fast-scanner)](https://goreportcard.com/report/github.com/For-ACGN/fast-scanner) 4 | [![GoDoc](https://godoc.org/github.com/For-ACGN/fast-scanner?status.svg)](http://godoc.org/github.com/For-ACGN/fast-scanner) 5 | [![license](https://img.shields.io/github/license/For-ACGN/fast-scanner.svg)](https://github.com/For-ACGN/fast-scanner/blob/master/LICENSE) 6 | \ 7 | fast-scanner can make it easy for you to develop scanners 8 | ## Features 9 | * Support CONNECT & SYN method 10 | * SYN scanning method is similar to masscan stateless scanning 11 | * Support IPv4 & IPv6 12 | * Support Windows & Linux 13 | * Scan result is a string channal 14 | ## Dependence 15 | `````` 16 | windows: 17 | Winpcap or Npcap 18 | linux: 19 | apt-get libpcap-dev 20 | yum install libpcap-devel 21 | `````` 22 | ## Install 23 | ``````sh 24 | wget https://github.com/For-ACGN/fast-scanner/install.sh 25 | chmod +x install.sh && ./install.sh 26 | `````` 27 | ## Parameter 28 | `````` 29 | targets: 30 | "1.1.1.1, 1.1.1.2-1.1.1.3, 1.1.1.1/24" 31 | "2606:4700:4700::1001, 2606:4700:4700::1002-2606:4700:4700::1003" 32 | ports: 33 | "80, 81-82" 34 | Options: 35 | see options.go 36 | `````` 37 | ## Example 38 | ``````go 39 | s, err := scanner.New("1.1.1.1-1.1.1.2, 2606:4700:4700::1001", "53-54", nil) 40 | if err != nil { 41 | log.Fatalln(err) 42 | } 43 | err = s.Start() 44 | if err != nil { 45 | log.Fatalln(err) 46 | } 47 | for address := range s.Result { 48 | log.Print(address + "\r\n") 49 | } 50 | `````` 51 | `````` 52 | 1.1.1.1:53 53 | [2606:4700:4700::1001]:53 54 | `````` 55 | ## TODO 56 | `````` 57 | 1. target support IPv6 CIDR 58 | 2. BPFFilter for IPv6 59 | _ = handle.SetBPFFilter("tcp[13] = 0x12") 60 | is not support IPv6 61 | 3. PF_RING 62 | `````` 63 | -------------------------------------------------------------------------------- /cmd/scanner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/For-ACGN/fast-scanner" 12 | ) 13 | 14 | type logger struct { 15 | file *os.File 16 | } 17 | 18 | func (l *logger) Write(p []byte) (n int, err error) { 19 | _, _ = os.Stderr.Write(p) 20 | return l.file.Write(p) 21 | } 22 | 23 | func main() { 24 | var ( 25 | targets string 26 | ports string 27 | method string 28 | device string 29 | rate int 30 | timeout time.Duration 31 | workers int 32 | senders int 33 | save string 34 | ) 35 | tu := bytes.Buffer{} 36 | tu.WriteString("host: 192.168.1.1, fe80::1\n") 37 | tu.WriteString("192.168.1.1-192.168.1.3, fe80::1-fe80::1\n") 38 | tu.WriteString("192.168.1.1/24") 39 | flag.StringVar(&targets, "h", "", tu.String()) 40 | flag.StringVar(&ports, "p", "", "ports: 80, 81-82") 41 | flag.StringVar(&method, "m", scanner.MethodSYN, "method connect or syn") 42 | flag.StringVar(&device, "d", "", "device: interface name Ethernet0, eth0") 43 | flag.IntVar(&rate, "r", 1000, "send packet rate") 44 | flag.DurationVar(&timeout, "t", 5*time.Second, "timeout") 45 | flag.IntVar(&workers, "worker", 0, "scanner number") 46 | flag.IntVar(&senders, "sender", 0, "packet sender number") 47 | flag.StringVar(&save, "save", "", "result file path") 48 | flag.Parse() 49 | opts := scanner.Options{ 50 | Method: method, 51 | Device: device, 52 | Rate: rate, 53 | Timeout: timeout, 54 | Workers: workers, 55 | Senders: senders, 56 | } 57 | if save != "" { 58 | file, err := os.OpenFile(save, os.O_CREATE|os.O_APPEND, 0644) 59 | if err != nil { 60 | log.Fatalln(err) 61 | } 62 | log.SetOutput(&logger{file: file}) 63 | } 64 | s, err := scanner.New(targets, ports, &opts) 65 | if err != nil { 66 | log.Fatalln(err) 67 | } 68 | start := time.Now() 69 | err = s.Start() 70 | if err != nil { 71 | log.Fatalln(err) 72 | } 73 | go func() { 74 | signalChan := make(chan os.Signal, 1) 75 | signal.Notify(signalChan, os.Kill, os.Interrupt) 76 | <-signalChan 77 | s.Stop() 78 | log.Print("stop scanner.\r\n") 79 | }() 80 | var scanned int 81 | for address := range s.Result { 82 | scanned += 1 83 | log.Print(address + "\r\n") 84 | } 85 | log.Printf("scan finished. total: %d time: %s\r\n", scanned, time.Since(start)) 86 | } 87 | -------------------------------------------------------------------------------- /connect.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | ) 7 | 8 | func (s *Scanner) connectScanner(wg *sync.WaitGroup) { 9 | defer wg.Done() 10 | var ( 11 | ip net.IP 12 | port string 13 | address string 14 | conn net.Conn 15 | err error 16 | ) 17 | for { 18 | ip = <-s.generator.IP 19 | if ip == nil { 20 | return 21 | } 22 | for port = range s.ports { 23 | // get token 24 | select { 25 | case <-s.tokenBucket: 26 | case <-s.stopSignal: 27 | return 28 | } 29 | // scan 30 | if len(ip) == net.IPv4len { 31 | address = ip.String() + ":" + port 32 | } else { 33 | address = "[" + ip.String() + "]:" + port 34 | } 35 | conn, err = s.dialer.Dial("tcp", address) 36 | s.addScanned() 37 | if err != nil { 38 | continue 39 | } 40 | ip := conn.RemoteAddr().(*net.TCPAddr).IP 41 | _ = conn.Close() 42 | if s.addResult(ip, port) { 43 | return 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /connect_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestConnectScanner(t *testing.T) { 12 | start := time.Now() 13 | targets := "8.8.8.8-8.8.8.10, 2606:4700:4700::1001-2606:4700:4700::1003" 14 | ports := "53,54,55-57" 15 | opt := Options{ 16 | Method: MethodConnect, 17 | Timeout: 3 * time.Second, 18 | } 19 | scanner, err := New(targets, ports, &opt) 20 | require.NoError(t, err) 21 | err = scanner.Start() 22 | require.NoError(t, err) 23 | result := make(map[string]struct{}) 24 | for address := range scanner.Result { 25 | result[address] = struct{}{} 26 | t.Log(address) 27 | } 28 | expected := []string{ 29 | "8.8.8.8:53", 30 | "[2606:4700:4700::1001]:53", 31 | } 32 | for i := 0; i < len(expected); i++ { 33 | if _, ok := result[expected[i]]; !ok { 34 | t.Fatal(expected[i], "is lost") 35 | } 36 | } 37 | t.Log("result:", len(result), "time:", time.Since(start)) 38 | require.Equal(t, scanner.HostNum().String(), "30") 39 | require.Equal(t, scanner.Scanned().String(), "30") 40 | } 41 | 42 | func TestConnectScanner_Stop(t *testing.T) { 43 | targets := "8.8.8.8/16" 44 | ports := "53,54,55-57" 45 | opt := Options{ 46 | Method: MethodConnect, 47 | Timeout: 3 * time.Second, 48 | } 49 | scanner, err := New(targets, ports, &opt) 50 | require.NoError(t, err) 51 | err = scanner.Start() 52 | require.NoError(t, err) 53 | go func() { 54 | err = scanner.Start() 55 | require.Error(t, err) 56 | }() 57 | time.Sleep(2 * time.Second) 58 | scanner.Stop() 59 | go func() { scanner.Stop() }() 60 | time.Sleep(250 * time.Millisecond) 61 | require.Equal(t, 2, runtime.NumGoroutine()) 62 | } 63 | -------------------------------------------------------------------------------- /dialer.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "os" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | const ( 15 | minPort = 1024 16 | maxPort = 65535 17 | ) 18 | 19 | type addr struct { 20 | ip string 21 | port int 22 | mu sync.Mutex 23 | } 24 | 25 | type addrPool struct { 26 | addrs []*addr 27 | addrsEnd int // len(laddrsv4) - 1 28 | addrsIndex int 29 | addrsMutex sync.Mutex 30 | } 31 | 32 | func newaddrPool() *addrPool { 33 | return &addrPool{addrsEnd: -1} 34 | } 35 | 36 | func (ap *addrPool) add(addr *addr) { 37 | ap.addrs = append(ap.addrs, addr) 38 | ap.addrsEnd = len(ap.addrs) - 1 39 | } 40 | 41 | func (ap *addrPool) get() *net.TCPAddr { 42 | // not set loacl ip 43 | if ap.addrsEnd == -1 { 44 | return nil 45 | } 46 | ap.addrsMutex.Lock() 47 | i := ap.addrsIndex 48 | // add d.laddrIndex 49 | if ap.addrsIndex == ap.addrsEnd { 50 | ap.addrsIndex = 0 51 | } else { 52 | ap.addrsIndex += 1 53 | } 54 | ap.addrsMutex.Unlock() 55 | laddr := ap.addrs[i] 56 | // add port 57 | laddr.mu.Lock() 58 | port := laddr.port 59 | if laddr.port == maxPort { 60 | laddr.port = minPort 61 | } else { 62 | laddr.port += 1 63 | } 64 | laddr.mu.Unlock() 65 | address := laddr.ip + ":" + strconv.Itoa(port) 66 | addr, _ := net.ResolveTCPAddr("tcp", address) 67 | return addr 68 | } 69 | 70 | type Dialer struct { 71 | timeout time.Duration 72 | ipv4GU *addrPool // GlobalUnicast "192.168.0.1", "1.1.1.1" 73 | ipv4LLU *addrPool // LinkLocalUnicast "169.254.0.1" 74 | ipv6GU *addrPool // GlobalUnicast "240c::1" 75 | ipv6LLU *addrPool // LinkLocalUnicast "fe80::1" 76 | } 77 | 78 | func NewDialer(localIPs []string, timeout time.Duration) (*Dialer, error) { 79 | d := &Dialer{ 80 | timeout: timeout, 81 | ipv4GU: newaddrPool(), 82 | ipv4LLU: newaddrPool(), 83 | ipv6GU: newaddrPool(), 84 | ipv6LLU: newaddrPool(), 85 | } 86 | if localIPs != nil { 87 | g, err := NewGenerator(localIPs) 88 | if err != nil { 89 | return nil, err 90 | } 91 | for ip := range g.IP { 92 | if ip.Equal(net.IPv4bcast) || 93 | ip.IsUnspecified() || 94 | ip.IsMulticast() || 95 | ip.IsLoopback() { 96 | continue 97 | } 98 | if len(ip) == net.IPv4len { 99 | addr := &addr{ 100 | ip: ip.String(), 101 | port: minPort, 102 | } 103 | if ip.IsGlobalUnicast() { 104 | d.ipv4GU.add(addr) 105 | } else { 106 | d.ipv4LLU.add(addr) 107 | } 108 | } else { 109 | addr := &addr{ 110 | ip: "[" + ip.String() + "]", 111 | port: minPort, 112 | } 113 | if ip.IsGlobalUnicast() { 114 | d.ipv6GU.add(addr) 115 | } else { 116 | d.ipv6LLU.add(addr) 117 | } 118 | } 119 | } 120 | } 121 | return d, nil 122 | } 123 | 124 | func (d *Dialer) Dial(network, address string) (net.Conn, error) { 125 | for { 126 | raddr, err := net.ResolveTCPAddr(network, address) 127 | if err != nil { 128 | return nil, err 129 | } 130 | ip := raddr.IP 131 | if ip.Equal(net.IPv4bcast) || 132 | ip.IsUnspecified() || 133 | ip.IsMulticast() { 134 | return nil, errors.New("invalid ip") 135 | } 136 | dialer := &net.Dialer{ 137 | Timeout: d.timeout, 138 | } 139 | if !ip.IsLoopback() { 140 | var laddr *net.TCPAddr 141 | if strings.Index(address, "[") == -1 { // ipv4 142 | if ip.IsGlobalUnicast() { 143 | laddr = d.ipv4GU.get() 144 | } else { 145 | laddr = d.ipv4LLU.get() 146 | } 147 | } else { // ipv6 148 | if ip.IsGlobalUnicast() { 149 | laddr = d.ipv6GU.get() 150 | } else { 151 | laddr = d.ipv6LLU.get() 152 | } 153 | } 154 | if laddr != nil { 155 | dialer.LocalAddr = laddr 156 | } 157 | } 158 | conn, err := dialer.Dial(network, address) 159 | if err != nil { 160 | if isLocalError(err) { 161 | time.Sleep(10 * time.Millisecond) 162 | continue 163 | } else { 164 | return nil, err 165 | } 166 | } 167 | return conn, nil 168 | } 169 | } 170 | 171 | func isLocalError(err error) bool { 172 | syscallErr, ok := err.(*net.OpError).Err.(*os.SyscallError) 173 | if !ok { 174 | return false 175 | } 176 | errno := syscallErr.Err.(syscall.Errno) 177 | switch int(errno) { 178 | case 10013: 179 | // An attempt was made to access a socket in a way 180 | // forbidden by its access permissions. 181 | case 10048: 182 | // Only one usage of each socket ip 183 | // (protocol/network ip/port) is normally permitted 184 | case 10055: 185 | // An operation on a socket could not be 186 | // performed because the system lacked sufficient 187 | // buffer space or because a queue was full. 188 | default: 189 | return false 190 | } 191 | return true 192 | } 193 | -------------------------------------------------------------------------------- /dialer_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | "strconv" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestAddrPool(t *testing.T) { 14 | // ipv4 GlobalUnicast 15 | dialer, err := NewDialer([]string{"192.168.1.1-192.168.1.2"}, time.Second) 16 | require.NoError(t, err) 17 | expected := bytes.Buffer{} 18 | for i := 1024; i < 65536; i++ { 19 | expected.WriteString("192.168.1.1:") 20 | expected.WriteString(strconv.Itoa(i)) 21 | expected.WriteString("\n") 22 | expected.WriteString("192.168.1.2:") 23 | expected.WriteString(strconv.Itoa(i)) 24 | expected.WriteString("\n") 25 | } 26 | // cycle 27 | expected.WriteString("192.168.1.1:1024\n") 28 | expected.WriteString("192.168.1.2:1024\n") 29 | actual := bytes.Buffer{} 30 | for i := 0; i < 2*(65536-1024+1); i++ { 31 | actual.WriteString(dialer.ipv4GU.get().String() + "\n") 32 | } 33 | require.True(t, expected.String() == actual.String()) 34 | // ipv4 LinkLocalUnicast 35 | dialer, err = NewDialer([]string{"169.254.1.1-169.254.1.2"}, time.Second) 36 | require.NoError(t, err) 37 | expected.Reset() 38 | for i := 1024; i < 65536; i++ { 39 | expected.WriteString("169.254.1.1:") 40 | expected.WriteString(strconv.Itoa(i)) 41 | expected.WriteString("\n") 42 | expected.WriteString("169.254.1.2:") 43 | expected.WriteString(strconv.Itoa(i)) 44 | expected.WriteString("\n") 45 | } 46 | // cycle 47 | expected.WriteString("169.254.1.1:1024\n") 48 | expected.WriteString("169.254.1.2:1024\n") 49 | actual.Reset() 50 | for i := 0; i < 2*(65536-1024+1); i++ { 51 | actual.WriteString(dialer.ipv4LLU.get().String() + "\n") 52 | } 53 | require.True(t, expected.String() == actual.String()) 54 | // ipv6 GlobalUnicast 55 | dialer, err = NewDialer([]string{"240c::1-240c::2"}, time.Second) 56 | require.NoError(t, err) 57 | expected.Reset() 58 | for i := 1024; i < 65536; i++ { 59 | expected.WriteString("[240c::1]:") 60 | expected.WriteString(strconv.Itoa(i)) 61 | expected.WriteString("\n") 62 | expected.WriteString("[240c::2]:") 63 | expected.WriteString(strconv.Itoa(i)) 64 | expected.WriteString("\n") 65 | } 66 | // cycle 67 | expected.WriteString("[240c::1]:1024\n") 68 | expected.WriteString("[240c::2]:1024\n") 69 | actual.Reset() 70 | for i := 0; i < 2*(65536-1024+1); i++ { 71 | actual.WriteString(dialer.ipv6GU.get().String() + "\n") 72 | } 73 | require.True(t, expected.String() == actual.String()) 74 | // ipv6 LinkLocalUnicast 75 | dialer, err = NewDialer([]string{"fe80::1-fe80::2"}, time.Second) 76 | require.NoError(t, err) 77 | expected.Reset() 78 | for i := 1024; i < 65536; i++ { 79 | expected.WriteString("[fe80::1]:") 80 | expected.WriteString(strconv.Itoa(i)) 81 | expected.WriteString("\n") 82 | expected.WriteString("[fe80::2]:") 83 | expected.WriteString(strconv.Itoa(i)) 84 | expected.WriteString("\n") 85 | } 86 | // cycle 87 | expected.WriteString("[fe80::1]:1024\n") 88 | expected.WriteString("[fe80::2]:1024\n") 89 | actual.Reset() 90 | for i := 0; i < 2*(65536-1024+1); i++ { 91 | actual.WriteString(dialer.ipv6LLU.get().String() + "\n") 92 | } 93 | require.True(t, expected.String() == actual.String()) 94 | } 95 | 96 | func TestDialer_Dial(t *testing.T) { 97 | iface, err := SelectInterface("") 98 | require.NoError(t, err) 99 | l := len(iface.IPNets) 100 | localIPs := make([]string, l) 101 | for i := 0; i < l; i++ { 102 | localIPs[i] = iface.IPNets[i].IP.String() 103 | } 104 | dialer, err := NewDialer(localIPs, 5*time.Second) 105 | require.NoError(t, err) 106 | listener, err := net.Listen("tcp", ":0") 107 | require.NoError(t, err) 108 | defer func() { _ = listener.Close() }() 109 | _, port, _ := net.SplitHostPort(listener.Addr().String()) 110 | // ipv4 111 | _, err = dialer.Dial("tcp", "8.8.8.8:53") 112 | require.NoError(t, err) 113 | _, err = dialer.Dial("tcp", "8.8.4.4:53") 114 | require.NoError(t, err) 115 | _, err = dialer.Dial("tcp", "127.0.0.1:"+port) 116 | require.NoError(t, err) 117 | _, err = dialer.Dial("tcp", "169.254.1.1:66666") 118 | require.Error(t, err) 119 | _, err = dialer.Dial("tcp", "169.254.1.1:53") 120 | require.Error(t, err) 121 | // ipv6 122 | _, err = dialer.Dial("tcp", "[2606:4700:4700::1111]:53") 123 | require.NoError(t, err) 124 | _, err = dialer.Dial("tcp", "[2606:4700:4700::1001]:53") 125 | require.NoError(t, err) 126 | _, err = dialer.Dial("tcp", "[::1]:"+port) 127 | require.NoError(t, err) 128 | _, err = dialer.Dial("tcp", "[fe80::1]:66666") 129 | require.Error(t, err) 130 | _, err = dialer.Dial("tcp", "[fe80::1]:53") 131 | require.Error(t, err) 132 | } 133 | -------------------------------------------------------------------------------- /examples/socks5/cracker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "sync" 11 | "time" 12 | 13 | "github.com/For-ACGN/fast-scanner" 14 | ) 15 | 16 | type cracker struct { 17 | address <-chan string 18 | dialer *scanner.Dialer 19 | timeout time.Duration 20 | auths []*auth 21 | authsLen int 22 | stopSignal chan struct{} 23 | wg *sync.WaitGroup 24 | } 25 | 26 | func (c *cracker) Do() { 27 | defer c.wg.Done() 28 | var address string 29 | for { 30 | select { 31 | case address = <-c.address: 32 | if address == "" { 33 | return 34 | } 35 | c.crack(address) 36 | case <-c.stopSignal: 37 | return 38 | } 39 | } 40 | } 41 | 42 | func (c *cracker) crack(address string) { 43 | // no password 44 | err := c.connect(address, "", "") 45 | if err == nil { 46 | log.Print(address + "\r\n") 47 | return 48 | } 49 | // with password 50 | for i := 0; i < c.authsLen; i++ { 51 | select { 52 | case <-c.stopSignal: 53 | return 54 | default: 55 | } 56 | user := c.auths[i].Username 57 | pass := c.auths[i].Password 58 | err = c.connect(address, user, pass) 59 | if err != nil { 60 | if err != errInvalidUserPass { 61 | return 62 | } 63 | } else { 64 | log.Printf("%s %s %s\r\n", address, user, pass) 65 | return 66 | } 67 | } 68 | } 69 | 70 | const ( 71 | version5 uint8 = 0x05 72 | // auth method 73 | notRequired uint8 = 0x00 74 | usernamePassword uint8 = 0x02 75 | noAcceptableMethods uint8 = 0xFF 76 | // auth 77 | usernamePasswordVersion uint8 = 0x01 78 | statusSucceeded uint8 = 0x00 79 | // connect 80 | reserve uint8 = 0x00 81 | connect uint8 = 0x01 82 | ipv4 uint8 = 0x01 83 | // reply 84 | succeeded uint8 = 0x00 85 | ) 86 | 87 | var ( 88 | errNoAcceptableMethods = errors.New("no acceptable authentication methods") 89 | errInvalidUserPass = errors.New("invalid username/password") 90 | ) 91 | 92 | type timeoutConn struct { 93 | timeout time.Duration 94 | net.Conn 95 | } 96 | 97 | func (c *timeoutConn) Read(b []byte) (n int, err error) { 98 | _ = c.SetReadDeadline(time.Now().Add(c.timeout)) 99 | return c.Conn.Read(b) 100 | } 101 | 102 | func (c *timeoutConn) Write(b []byte) (n int, err error) { 103 | _ = c.SetWriteDeadline(time.Now().Add(c.timeout)) 104 | return c.Conn.Write(b) 105 | } 106 | 107 | func (c *cracker) connect(address, username, password string) error { 108 | conn, err := c.dialer.Dial("tcp", address) 109 | if err != nil { 110 | return err 111 | } 112 | defer func() { _ = conn.Close() }() 113 | conn = &timeoutConn{timeout: c.timeout, Conn: conn} 114 | // request authentication 115 | buffer := bytes.Buffer{} 116 | buffer.WriteByte(version5) 117 | if username == "" { 118 | buffer.WriteByte(1) 119 | buffer.WriteByte(notRequired) 120 | } else { 121 | buffer.WriteByte(2) 122 | buffer.WriteByte(notRequired) 123 | buffer.WriteByte(usernamePassword) 124 | } 125 | _, err = conn.Write(buffer.Bytes()) 126 | if err != nil { 127 | return err 128 | } 129 | resp := make([]byte, 2) 130 | _, err = io.ReadFull(conn, resp) 131 | if err != nil { 132 | return err 133 | } 134 | if resp[0] != version5 { 135 | return fmt.Errorf("unexpected protocol version %d", resp[0]) 136 | } 137 | am := resp[1] 138 | if am == noAcceptableMethods { 139 | return errNoAcceptableMethods 140 | } 141 | // authenticate 142 | switch am { 143 | case notRequired: 144 | case usernamePassword: 145 | if len(username) == 0 || len(username) > 255 { 146 | return errors.New("invalid username length") 147 | } 148 | // https://www.ietf.org/rfc/rfc1929.txt 149 | buffer.Reset() 150 | buffer.WriteByte(usernamePasswordVersion) 151 | buffer.WriteByte(byte(len(username))) 152 | buffer.WriteString(username) 153 | buffer.WriteByte(byte(len(password))) 154 | buffer.WriteString(password) 155 | _, err = conn.Write(buffer.Bytes()) 156 | if err != nil { 157 | return err 158 | } 159 | resp = make([]byte, 2) 160 | _, err = io.ReadFull(conn, resp) 161 | if err != nil { 162 | return err 163 | } 164 | if resp[0] != usernamePasswordVersion { 165 | return errors.New("invalid username/password version") 166 | } 167 | if resp[1] != statusSucceeded { 168 | return errInvalidUserPass 169 | } 170 | default: 171 | return fmt.Errorf("unsupported authentication method %d", am) 172 | } 173 | // check & send test connect target 174 | // 8.8.8.8:53 175 | buffer.Reset() 176 | buffer.WriteByte(version5) 177 | buffer.WriteByte(connect) 178 | buffer.WriteByte(reserve) 179 | buffer.WriteByte(ipv4) 180 | buffer.Write([]byte{8, 8, 8, 8}) 181 | buffer.Write([]byte{0, 53}) 182 | _, err = conn.Write(buffer.Bytes()) 183 | if err != nil { 184 | return err 185 | } 186 | // receive reply 187 | resp = make([]byte, 4) 188 | _, err = io.ReadFull(conn, resp) 189 | if err != nil { 190 | return err 191 | } 192 | if resp[0] != version5 { 193 | return fmt.Errorf("unexpected protocol version %d", resp[0]) 194 | } 195 | if resp[1] != succeeded { 196 | return errors.New("connect failed") 197 | } 198 | if resp[2] != 0 { 199 | return errors.New("non-zero reserved field") 200 | } 201 | // receive ipv4 202 | resp = make([]byte, net.IPv4len+2) // ipv4 + ip + port 203 | _, err = io.ReadFull(conn, resp) 204 | return err 205 | } 206 | -------------------------------------------------------------------------------- /examples/socks5/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "flag" 7 | "log" 8 | "os" 9 | "os/signal" 10 | "runtime" 11 | "sync" 12 | "time" 13 | 14 | "github.com/For-ACGN/fast-scanner" 15 | ) 16 | 17 | type auth struct { 18 | Username string 19 | Password string 20 | } 21 | 22 | type logger struct { 23 | file *os.File 24 | } 25 | 26 | func (l *logger) Write(p []byte) (n int, err error) { 27 | _, _ = os.Stderr.Write(p) 28 | return l.file.Write(p) 29 | } 30 | 31 | func main() { 32 | var ( 33 | targets string 34 | ports string 35 | method string 36 | device string 37 | rate int 38 | timeout time.Duration 39 | workers int 40 | senders int 41 | save string 42 | crackers int 43 | username string 44 | password string 45 | ) 46 | tu := bytes.Buffer{} 47 | tu.WriteString("host: 192.168.1.1, fe80::1\n") 48 | tu.WriteString("192.168.1.1-192.168.1.3, fe80::1-fe80::1\n") 49 | tu.WriteString("192.168.1.1/24") 50 | flag.StringVar(&targets, "h", "", tu.String()) 51 | flag.StringVar(&ports, "p", "1080", "ports: 1080, 1081-1082") 52 | flag.StringVar(&method, "m", scanner.MethodSYN, "method connect or syn") 53 | flag.StringVar(&device, "d", "", "device: interface name Ethernet0, eth0") 54 | flag.IntVar(&rate, "r", 1000, "send packet rate") 55 | flag.DurationVar(&timeout, "t", 5*time.Second, "timeout") 56 | flag.IntVar(&workers, "workers", 0, "scanner number") 57 | flag.IntVar(&senders, "senders", 0, "packet sender number") 58 | flag.StringVar(&save, "save", "", "result file path") 59 | flag.IntVar(&crackers, "crackers", 16*runtime.NumCPU(), "crackers number") 60 | flag.StringVar(&username, "username", "username.txt", "username file path") 61 | flag.StringVar(&password, "password", "password.txt", "password file path") 62 | flag.Parse() 63 | opts := scanner.Options{ 64 | Method: method, 65 | Device: device, 66 | Rate: rate, 67 | Timeout: timeout, 68 | Workers: workers, 69 | Senders: senders, 70 | } 71 | if save != "" { 72 | file, err := os.OpenFile(save, os.O_CREATE|os.O_APPEND, 644) 73 | if err != nil { 74 | log.Println(err) 75 | return 76 | } 77 | log.SetOutput(&logger{file: file}) 78 | } 79 | // read username password 80 | var ( 81 | usernames []string 82 | passwords []string 83 | ) 84 | if username != "" { 85 | file, err := os.Open(username) 86 | if err != nil { 87 | log.Println(err) 88 | return 89 | } 90 | reader := bufio.NewReader(file) 91 | for { 92 | username, _, err := reader.ReadLine() 93 | if err != nil { 94 | break 95 | } 96 | if username != nil { 97 | usernames = append(usernames, string(username)) 98 | } 99 | } 100 | } 101 | if password != "" { 102 | file, err := os.Open(password) 103 | if err != nil { 104 | log.Println(err) 105 | return 106 | } 107 | reader := bufio.NewReader(file) 108 | for { 109 | password, _, err := reader.ReadLine() 110 | if err != nil { 111 | break 112 | } 113 | if password != nil { 114 | passwords = append(passwords, string(password)) 115 | } 116 | } 117 | } 118 | // make password directory 119 | usernamesLen := len(usernames) 120 | passwordsLen := len(passwords) 121 | authsLen := usernamesLen * (passwordsLen + 1) 122 | auths := make([]*auth, authsLen) // NULL 123 | index := 0 124 | for i := 0; i < usernamesLen; i++ { 125 | // add NULL 126 | auths[index] = &auth{Username: usernames[i]} 127 | index += 1 128 | for j := 0; j < len(passwords); j++ { 129 | auths[index] = &auth{ 130 | Username: usernames[i], 131 | Password: passwords[j], 132 | } 133 | index += 1 134 | } 135 | } 136 | // init scanner 137 | s, err := scanner.New(targets, ports, &opts) 138 | if err != nil { 139 | log.Fatalln(err) 140 | } 141 | start := time.Now() 142 | err = s.Start() 143 | if err != nil { 144 | log.Fatalln(err) 145 | } 146 | stopSignal := make(chan struct{}) 147 | wg := sync.WaitGroup{} 148 | go func() { 149 | signalChan := make(chan os.Signal, 1) 150 | signal.Notify(signalChan, os.Kill, os.Interrupt) 151 | <-signalChan 152 | s.Stop() 153 | close(stopSignal) 154 | wg.Wait() 155 | log.Print("stop scanner.\r\n") 156 | }() 157 | // init crackers 158 | // more localIPs can lift scan speed 159 | // > 65536 conns at the same time 160 | iface, err := scanner.SelectInterface(device) 161 | if err != nil { 162 | log.Fatalln(err) 163 | } 164 | l := len(iface.IPNets) 165 | localIPs := make([]string, l) 166 | for i := 0; i < l; i++ { 167 | localIPs[i] = iface.IPNets[i].IP.String() 168 | } 169 | dialer, err := scanner.NewDialer(localIPs, timeout) 170 | if err != nil { 171 | log.Fatalln(err) 172 | } 173 | for i := 0; i < crackers; i++ { 174 | c := cracker{ 175 | address: s.Result, 176 | dialer: dialer, 177 | timeout: timeout, 178 | auths: auths, 179 | authsLen: authsLen, 180 | stopSignal: stopSignal, 181 | wg: &wg, 182 | } 183 | wg.Add(1) 184 | go c.Do() 185 | } 186 | wg.Wait() 187 | log.Printf("scan finished. time: %s\r\n", time.Since(start)) 188 | } 189 | -------------------------------------------------------------------------------- /examples/socks5/password.txt: -------------------------------------------------------------------------------- 1 | 123456 2 | password 3 | qwerty 4 | 12345 5 | 1234567 6 | 12345678 7 | 123456789 8 | admin 9 | pass 10 | football 11 | iloveyou 12 | welcome 13 | letmein 14 | login 15 | hello 16 | -------------------------------------------------------------------------------- /examples/socks5/username.txt: -------------------------------------------------------------------------------- 1 | admin 2 | root -------------------------------------------------------------------------------- /generator.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math" 7 | "math/big" 8 | "net" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | 13 | "github.com/pkg/errors" 14 | ) 15 | 16 | type Generator struct { 17 | N *big.Int // host number will be generate 18 | IP chan net.IP 19 | wg sync.WaitGroup 20 | once sync.Once 21 | stop chan struct{} 22 | } 23 | 24 | func (g *Generator) Close() { 25 | g.once.Do(func() { 26 | close(g.stop) 27 | }) 28 | g.wg.Wait() 29 | } 30 | 31 | // Range 32 | func (g *Generator) genIPWithHyphen(target string) { 33 | ips := strings.Split(target, "-") 34 | startIP := net.ParseIP(ips[0]) 35 | stopIP := net.ParseIP(ips[1]) 36 | ipv4 := startIP.To4() 37 | if ipv4 != nil { // ipv4 38 | // bytes to uint32 39 | start := binary.BigEndian.Uint32(ipv4) 40 | stop := binary.BigEndian.Uint32(stopIP.To4()) 41 | // add host number 42 | g.N.Add(g.N, big.NewInt(int64(stop-start+1))) 43 | // generate 44 | g.wg.Add(1) 45 | go func() { 46 | defer g.wg.Done() 47 | for { 48 | select { 49 | case g.IP <- net.IP(uint32ToBytes(start)): 50 | case <-g.stop: 51 | return 52 | } 53 | if start == stop { 54 | return 55 | } 56 | start += 1 57 | } 58 | }() 59 | } else { // ipv6 60 | delta := big.NewInt(1) 61 | start := new(big.Int).SetBytes(startIP.To16()) 62 | stop := new(big.Int).SetBytes(stopIP.To16()) 63 | stopBytes := stop.Bytes() 64 | // add host number 65 | g.N.Add(g.N, stop.Sub(stop, start)) 66 | g.N.Add(g.N, delta) 67 | // generate 68 | g.wg.Add(1) 69 | go func() { 70 | defer g.wg.Done() 71 | for { 72 | b := start.Bytes() 73 | select { 74 | case g.IP <- net.IP(paddingSlice16(b)): 75 | case <-g.stop: 76 | return 77 | } 78 | if bytes.Equal(b, stopBytes) { 79 | return 80 | } 81 | start.Add(start, delta) 82 | } 83 | }() 84 | } 85 | } 86 | 87 | // CIDR 88 | func (g *Generator) genIPWithDash(target string) { 89 | ip, ipnet, _ := net.ParseCIDR(target) 90 | n, _ := strconv.Atoi(strings.Split(ipnet.String(), "/")[1]) 91 | if ip.To4() != nil { // ipv4 92 | i := uint32(0) 93 | hostNumber := uint32(math.Pow(2, float64(net.IPv4len*8-n))) 94 | // add host number 95 | g.N.Add(g.N, big.NewInt(int64(hostNumber))) 96 | // generate 97 | address := binary.BigEndian.Uint32(ipnet.IP) 98 | g.wg.Add(1) 99 | go func() { 100 | defer g.wg.Done() 101 | for { 102 | select { 103 | case g.IP <- net.IP(uint32ToBytes(address)): 104 | case <-g.stop: 105 | return 106 | } 107 | i += 1 108 | if i == hostNumber { 109 | return 110 | } 111 | address += 1 112 | } 113 | }() 114 | } else { // ipv6 115 | // TODO ipv6 CIDR 116 | /* 117 | delta := big.NewInt(1) 118 | i := new(big.Int) 119 | hostNumber := new(big.Int).Lsh(big.NewInt(1), uint(net.IPv6len*8-n)) 120 | hostNumber.Sub(hostNumber, delta) // for loop 121 | hostNumberBytes := hostNumber.Bytes() 122 | startIP := new(big.Int).SetBytes(ipnet.IP) 123 | for { 124 | select { 125 | case ipChan <- net.IP(paddingSlice16(startIP.Bytes())): 126 | case <-ctx.Done(): 127 | return 128 | } 129 | if bytes.Equal(i.Bytes(), hostNumberBytes) { 130 | return 131 | } 132 | i.Add(i, delta) 133 | startIP.Add(startIP, delta) 134 | } 135 | */ 136 | } 137 | } 138 | 139 | func NewGenerator(targets []string) (*Generator, error) { 140 | l := len(targets) 141 | g := Generator{ 142 | N: big.NewInt(0), 143 | IP: make(chan net.IP, l), 144 | stop: make(chan struct{}), 145 | } 146 | for _, target := range targets { 147 | hyphen := strings.Index(target, "-") 148 | dash := strings.Index(target, "/") 149 | switch { 150 | case hyphen+dash == -2: // single ip "192.168.1.1" 151 | ip := net.ParseIP(target) 152 | if ip == nil { 153 | g.Close() 154 | return nil, errors.New("invalid ip: " + target) 155 | } 156 | var dst net.IP 157 | if i := ip.To4(); i != nil { 158 | dst = i 159 | } else { 160 | dst = ip.To16() 161 | } 162 | // add host number 163 | g.N.Add(g.N, big.NewInt(1)) 164 | // generate 165 | g.wg.Add(1) 166 | go func() { 167 | defer g.wg.Done() 168 | select { 169 | case g.IP <- dst: 170 | case <-g.stop: 171 | return 172 | } 173 | }() 174 | case hyphen != -1 && dash == -1: // range "192.168.1.1-192.168.1.2" 175 | const ( 176 | ipv4 = 0 177 | ipv6 = 1 178 | ) 179 | ips := strings.Split(target, "-") 180 | if len(ips) != 2 { 181 | g.Close() 182 | return nil, errors.New("invalid target: " + target) 183 | } 184 | // check is same ip type 185 | startIP := net.ParseIP(ips[0]) 186 | stopIP := net.ParseIP(ips[1]) 187 | startIPType := ipv4 188 | stopIPType := ipv4 189 | if startIP.To4() == nil { 190 | if startIP.To16() != nil { 191 | startIPType = ipv6 192 | } else { 193 | g.Close() 194 | return nil, errors.New("invalid start ip: " + target) 195 | } 196 | } 197 | if stopIP.To4() == nil { 198 | if stopIP.To16() != nil { 199 | stopIPType = ipv6 200 | } else { 201 | g.Close() 202 | return nil, errors.New("invalid stop ip: " + target) 203 | } 204 | } 205 | if startIPType != stopIPType { 206 | g.Close() 207 | return nil, errors.New("different ip type: " + target) 208 | } 209 | // check start <= stop 210 | start := new(big.Int) 211 | stop := new(big.Int) 212 | switch startIPType { 213 | case ipv4: 214 | start.SetBytes(startIP.To4()) 215 | stop.SetBytes(stopIP.To4()) 216 | case ipv6: 217 | start.SetBytes(startIP.To16()) 218 | stop.SetBytes(stopIP.To16()) 219 | } 220 | if start.Cmp(stop) == 1 { 221 | g.Close() 222 | return nil, errors.New("start ip > stop ip: " + target) 223 | } 224 | g.genIPWithHyphen(target) 225 | case hyphen == -1 && dash != -1: // CIDR "192.168.1.1/24" 226 | _, _, err := net.ParseCIDR(target) 227 | if err != nil { 228 | g.Close() 229 | return nil, errors.New("invalid CIDR: " + target) 230 | } 231 | g.genIPWithDash(target) 232 | case hyphen != -1 && dash != -1: // "192.168.1.1-192.168.1.2/24" 233 | g.Close() 234 | return nil, errors.New("invalid target: " + target) 235 | } 236 | } 237 | // if all generated close ip chan 238 | go func() { 239 | g.wg.Wait() 240 | close(g.IP) 241 | }() 242 | return &g, nil 243 | } 244 | 245 | func uint32ToBytes(n uint32) []byte { 246 | buffer := make([]byte, 4) 247 | binary.BigEndian.PutUint32(buffer, n) 248 | return buffer 249 | } 250 | 251 | // []byte{1} -> []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} 252 | func paddingSlice16(s []byte) []byte { 253 | l := len(s) 254 | if l == 16 { 255 | return s 256 | } 257 | p := make([]byte, net.IPv6len) 258 | copy(p[net.IPv6len-l:], s) 259 | return p 260 | } 261 | -------------------------------------------------------------------------------- /generator_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func ExampleGenerator() { 12 | testdata := [][]string{ 13 | {"192.168.1.1"}, 14 | {"192.168.1.1-192.168.1.1"}, 15 | {"192.168.1.1-192.168.1.3"}, 16 | {"192.168.1.1/31"}, 17 | {"fe80::1"}, 18 | {"fe80::1-fe80::1"}, 19 | {"fe80::1-fe80::2"}, 20 | {"::1-::2"}, 21 | } 22 | for _, targets := range testdata { 23 | generator, err := NewGenerator(targets) 24 | if err != nil { 25 | log.Fatalln(err) 26 | } 27 | fmt.Println("number:", generator.N) 28 | for ip := range generator.IP { 29 | fmt.Println(ip, len(ip)) 30 | } 31 | } 32 | 33 | // check number 34 | l := len(testdata) 35 | targets := make([]string, l) 36 | for i := 0; i < l; i++ { 37 | targets[i] = testdata[i][0] 38 | } 39 | generator, err := NewGenerator(targets) 40 | if err != nil { 41 | log.Fatalln(err) 42 | } 43 | generator.Close() 44 | fmt.Println("number:", generator.N) 45 | 46 | // Output: 47 | // number: 1 48 | // 192.168.1.1 4 49 | // number: 1 50 | // 192.168.1.1 4 51 | // number: 3 52 | // 192.168.1.1 4 53 | // 192.168.1.2 4 54 | // 192.168.1.3 4 55 | // number: 2 56 | // 192.168.1.0 4 57 | // 192.168.1.1 4 58 | // number: 1 59 | // fe80::1 16 60 | // number: 1 61 | // fe80::1 16 62 | // number: 2 63 | // fe80::1 16 64 | // fe80::2 16 65 | // number: 2 66 | // ::1 16 67 | // ::2 16 68 | // number: 13 69 | } 70 | 71 | func TestNewGenerator(t *testing.T) { 72 | testdata := [][]string{ 73 | // single 74 | {"192.168.1.256"}, // invalid single ipv4 75 | {"fg::1"}, // invalid single ipv6 76 | // range 77 | {"192.168.1.1-192.168.1.1-"}, // 2 "-" 78 | {"192.168.1.256-192.168.1.1"}, // invalid start ipv4 79 | {"fg::1-fg::1"}, // invalid start ipv6 80 | {"192.168.1.1-192.168.1.256"}, // invalid stop ipv4 81 | {"fe80::1-fg80::1"}, // invalid stop ipv6 82 | {"192.168.1.1-fe80::1"}, // start ip type != stop ip type 83 | {"fe80::1-192.168.1.1"}, // start ip type != stop ip type 84 | {"192.168.1.2-192.168.1.1"}, // start ipv4 > stop ipv6 85 | {"fe80::2-fe80::1"}, // start ipv6 > stop ipv6 86 | // CIDR 87 | {"192.168.1.1/33"}, // ipv4 88 | {"fe80::1/129"}, // ipv6 89 | // range & CIDR 90 | {"192.168.1.1-192.168.1.2/24"}, 91 | // interrupt 92 | {"192.168.1.2-192.168.1.22", "192.168.1.256"}, 93 | {"192.168.1.2-192.168.1.255", "192.168.1.255", "192.168.1.256"}, 94 | } 95 | for _, targets := range testdata { 96 | _, err := NewGenerator(targets) 97 | require.NotNil(t, err) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/For-ACGN/fast-scanner 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d 7 | github.com/go-ole/go-ole v1.2.4 // indirect 8 | github.com/google/gopacket v1.1.18-0.20190912173203-2d7fab0d91d6 9 | github.com/pkg/errors v0.8.1 10 | github.com/stretchr/testify v1.4.0 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= 2 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= 6 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 7 | github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= 8 | github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 9 | github.com/google/gopacket v1.1.18-0.20190731192947-c340012d34ad h1:zqEPVLoTsTkMX0k5QeqwnoMSP+6AdTh7MAxCMxZE7rg= 10 | github.com/google/gopacket v1.1.18-0.20190731192947-c340012d34ad/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 11 | github.com/google/gopacket v1.1.18-0.20190912173203-2d7fab0d91d6 h1:31OUydzq5pD8r360HWCRVpjIhaS5P7Ar7gK6/vaUWtk= 12 | github.com/google/gopacket v1.1.18-0.20190912173203-2d7fab0d91d6/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 13 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 14 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 19 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 20 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 21 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 22 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 23 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 24 | golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998= 25 | golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 27 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 28 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 29 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | var initErr error 4 | -------------------------------------------------------------------------------- /init_windows.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "github.com/google/gopacket/pcap" 5 | ) 6 | 7 | func init() { 8 | initErr = pcap.LoadWinPCAP() 9 | } 10 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | yum install -y go 2 | yum install -y libpcap 3 | yum install -y libpcap-devel 4 | yum install -y net-tools 5 | yum install -y htop 6 | yum install -y sysstat 7 | cd /opt 8 | git clone https://github.com/For-ACGN/fast-scanner 9 | cd fast-scanner/cmd 10 | go build -v -i -ldflags "-s -w" -o scanner 11 | cd ../examples/socks5 12 | go build -v -i -ldflags "-s -w" -o socks5 -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | var ErrNoInterfaces = errors.New("no Interfaces") 11 | 12 | type Interface struct { 13 | Name string // windows: "Ethernet0" linux: "eth0" 14 | Device string // windows: "\Device\NPF_{GUID}" linux: "eth0" 15 | MAC net.HardwareAddr 16 | IPNets []*net.IPNet 17 | Gateways []net.IP 18 | } 19 | 20 | // if name is "" select the first interface 21 | func SelectInterface(name string) (*Interface, error) { 22 | ifaces, err := GetAllInterfaces() 23 | if err != nil { 24 | return nil, err 25 | } 26 | if name == "" { 27 | return ifaces[0], nil 28 | } 29 | for i := 0; i < len(ifaces); i++ { 30 | if ifaces[i].Name == name { 31 | return ifaces[i], nil 32 | } 33 | } 34 | return nil, fmt.Errorf("interface: %s doesn't exist", name) 35 | } 36 | -------------------------------------------------------------------------------- /interface_linux.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/google/gopacket/pcap" 7 | ) 8 | 9 | func GetAllInterfaces() ([]*Interface, error) { 10 | devs, err := pcap.FindAllDevs() 11 | if err != nil { 12 | return nil, err 13 | } 14 | devsLen := len(devs) 15 | if devsLen == 0 { 16 | return nil, ErrNoInterfaces 17 | } 18 | // to get MAC 19 | ifs, err := net.Interfaces() 20 | if err != nil { 21 | return nil, err 22 | } 23 | ifsLen := len(ifs) 24 | ifaces := make([]*Interface, devsLen) 25 | for i := 0; i < devsLen; i++ { 26 | iface := Interface{ 27 | Name: devs[i].Name, // same 28 | Device: devs[i].Name, // same 29 | // not need gateways 30 | } 31 | // set IPNets 32 | iface.IPNets = make([]*net.IPNet, len(devs[i].Addresses)) 33 | for i, address := range devs[i].Addresses { 34 | iface.IPNets[i] = &net.IPNet{ 35 | IP: address.IP, 36 | Mask: address.Netmask, 37 | } 38 | } 39 | // set MACS 40 | for i := 0; i < ifsLen; i++ { 41 | if ifs[i].Name == iface.Name { 42 | iface.MAC = ifs[i].HardwareAddr 43 | } 44 | } 45 | ifaces[i] = &iface 46 | } 47 | return ifaces, nil 48 | } 49 | -------------------------------------------------------------------------------- /interface_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestSelectInterfaces(t *testing.T) { 10 | iface, err := SelectInterface("") 11 | require.NoError(t, err) 12 | t.Log(iface) 13 | } 14 | -------------------------------------------------------------------------------- /interface_windows.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/StackExchange/wmi" 8 | ) 9 | 10 | func GetAllInterfaces() ([]*Interface, error) { 11 | type Win32NetworkAdapter struct { 12 | NetConnectionID string 13 | GUID string // GUID=SettingID 14 | } 15 | var adapters []Win32NetworkAdapter 16 | q := "SELECT * FROM Win32_NetworkAdapter WHERE NetEnabled = TRUE" 17 | err := wmi.Query(q, &adapters) 18 | if err != nil { 19 | return nil, err 20 | } 21 | l := len(adapters) 22 | if l == 0 { 23 | return nil, ErrNoInterfaces 24 | } 25 | type Win32NetworkAdapterConfiguration struct { 26 | SettingID string 27 | MACAddress string 28 | IPAddress []string 29 | IPSubnet []string 30 | DefaultIPGateway []string 31 | } 32 | var configs []Win32NetworkAdapterConfiguration 33 | q = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = TRUE" 34 | err = wmi.Query(q, &configs) 35 | if err != nil { 36 | return nil, err 37 | } 38 | ifaces := make([]*Interface, l) 39 | for i, adapter := range adapters { 40 | iface := Interface{ 41 | Name: adapter.NetConnectionID, 42 | Device: "\\Device\\NPF_" + adapter.GUID, 43 | } 44 | for _, config := range configs { 45 | if config.SettingID == adapter.GUID { 46 | // set MAC Address 47 | iface.MAC, _ = net.ParseMAC(config.MACAddress) 48 | // set IP Nets 49 | l = len(config.IPAddress) 50 | iface.IPNets = make([]*net.IPNet, l) 51 | for i := 0; i < l; i++ { 52 | ip := net.ParseIP(config.IPAddress[i]) 53 | var mask net.IPMask 54 | if ip.To4() != nil { // ipv4 55 | mask = net.IPMask(net.ParseIP(config.IPSubnet[i]).To4()) 56 | } else { // ipv6 57 | n, _ := strconv.Atoi(config.IPSubnet[i]) 58 | mask = net.CIDRMask(n, 128) 59 | } 60 | iface.IPNets[i] = &net.IPNet{ 61 | IP: ip, 62 | Mask: mask, 63 | } 64 | } 65 | l = len(config.DefaultIPGateway) 66 | iface.Gateways = make([]net.IP, l) 67 | for i := 0; i < l; i++ { 68 | iface.Gateways[i] = net.ParseIP(config.DefaultIPGateway[i]) 69 | } 70 | } 71 | } 72 | ifaces[i] = &iface 73 | } 74 | return ifaces, nil 75 | } 76 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "runtime" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | const ( 10 | MethodSYN = "syn" 11 | MethodConnect = "connect" 12 | ) 13 | 14 | type Options struct { 15 | // "syn", "connect" 16 | Method string 17 | Device string 18 | Rate int 19 | Timeout time.Duration 20 | // "connect": connectScanner() goroutine number 21 | // "syn": synScanner() and synParser() goroutine number 22 | Workers int 23 | // packetSender number useless for "connect" 24 | Senders int 25 | // if true scanner will not handle duplicate result, 26 | // it will use less memory 27 | // default will handle duplicate result 28 | Raw bool 29 | } 30 | 31 | func (opt *Options) apply() { 32 | if opt.Method == "" { 33 | opt.Method = MethodSYN 34 | } 35 | if opt.Rate < 1 { 36 | opt.Rate = 1000 37 | } 38 | if opt.Timeout < 1 { 39 | switch opt.Method { 40 | case MethodConnect: 41 | opt.Timeout = 5 * time.Second 42 | case MethodSYN: 43 | opt.Timeout = 3 * time.Second 44 | } 45 | } 46 | if opt.Workers < 1 { 47 | switch opt.Method { 48 | case MethodConnect: 49 | opt.Workers = 64 * runtime.NumCPU() 50 | case MethodSYN: 51 | opt.Workers = 32 * runtime.NumCPU() 52 | } 53 | } 54 | if opt.Senders < 1 { 55 | switch runtime.GOOS { 56 | case "windows": 57 | opt.Senders = 2 // "magic send packet speed" 58 | case "linux": 59 | opt.Senders = 1 // "one goroutine will get full speed" 60 | default: 61 | opt.Senders = 1 62 | } 63 | } 64 | } 65 | 66 | func split(str string) []string { 67 | str = strings.Replace(str, " ", "", -1) 68 | return strings.Split(str, ",") 69 | } 70 | -------------------------------------------------------------------------------- /pfring.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package scanner 4 | -------------------------------------------------------------------------------- /pprof_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net/http" 5 | _ "net/http/pprof" 6 | ) 7 | 8 | func pprof() { 9 | go func() { _ = http.ListenAndServe(":8080", nil) }() 10 | } 11 | -------------------------------------------------------------------------------- /routing.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | errRouteSelf = errors.New("self ip") 9 | ) 10 | -------------------------------------------------------------------------------- /routing_linux.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/google/gopacket/routing" 7 | ) 8 | 9 | type router struct { 10 | mac net.HardwareAddr 11 | router routing.Router 12 | } 13 | 14 | func (r *router) route(dst net.IP) (gateway, preferredSrc net.IP, err error) { 15 | _, g, src, err := r.router.RouteWithSrc(r.mac, nil, dst) 16 | return g, src, err 17 | } 18 | 19 | func newRouter(iface *Interface) (*router, error) { 20 | r, err := routing.New() 21 | if err != nil { 22 | return nil, err 23 | } 24 | return &router{router: r, mac: iface.MAC}, nil 25 | } 26 | -------------------------------------------------------------------------------- /routing_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestRoute(t *testing.T) { 11 | iface, err := SelectInterface("") 12 | require.NoError(t, err) 13 | route, err := newRouter(iface) 14 | require.NoError(t, err) 15 | // ipv4 16 | g, src, err := route.route(net.ParseIP("192.168.1.1")) 17 | require.NoError(t, err) 18 | require.Nil(t, g) 19 | t.Log("src:", src) 20 | g, src, err = route.route(net.ParseIP("1.1.1.1")) 21 | require.NoError(t, err) 22 | t.Log("gateway:", g, "src:", src) 23 | // ipv6 24 | g, src, err = route.route(net.ParseIP("fe80::1")) 25 | require.NoError(t, err) 26 | require.Nil(t, g) 27 | t.Log("src:", src) 28 | g, src, err = route.route(net.ParseIP("240c::1")) 29 | require.NoError(t, err) 30 | t.Log("gateway:", g, "src:", src) 31 | } 32 | -------------------------------------------------------------------------------- /routing_windows.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | type router struct { 10 | ipv4Nets []*net.IPNet 11 | ipv6Nets []*net.IPNet 12 | ipv4Gateway map[*net.IP]struct{} 13 | ipv6Gateway map[*net.IP]struct{} 14 | } 15 | 16 | func (r *router) route(dst net.IP) (gateway, preferredSrc net.IP, err error) { 17 | if !((len(dst) == net.IPv4len || len(dst) == net.IPv6len) && 18 | !dst.Equal(net.IPv4bcast) && 19 | !dst.IsUnspecified() && 20 | !dst.IsMulticast() && 21 | !dst.IsLoopback()) { 22 | err = errors.New("invalid ip") 23 | return 24 | } 25 | if dst.To4() != nil { 26 | if len(r.ipv4Nets) == 0 { 27 | err = errors.New("no ipv4 net") 28 | return 29 | } 30 | // check is LAN 31 | for _, ipnet := range r.ipv4Nets { 32 | if ipnet.IP.Equal(dst) { 33 | err = errRouteSelf 34 | return 35 | } 36 | if ipnet.Contains(dst) { 37 | preferredSrc = ipnet.IP 38 | return 39 | } 40 | } 41 | // intranet 42 | for g := range r.ipv4Gateway { 43 | preferredSrc = r.ipv4Nets[0].IP 44 | gateway = *g 45 | return 46 | } 47 | } else if dst.To16() != nil { 48 | if len(r.ipv6Nets) == 0 { 49 | err = errors.New("no ipv6 net") 50 | return 51 | } 52 | // check is LAN 53 | for _, ipnet := range r.ipv6Nets { 54 | if ipnet.IP.Equal(dst) { 55 | err = errRouteSelf 56 | return 57 | } 58 | if ipnet.Contains(dst) { 59 | preferredSrc = ipnet.IP 60 | return 61 | } 62 | } 63 | // intranet 64 | for g := range r.ipv6Gateway { 65 | for _, ipnet := range r.ipv6Nets { 66 | if ipnet.IP.IsGlobalUnicast() { 67 | preferredSrc = ipnet.IP 68 | gateway = *g 69 | return 70 | } 71 | } 72 | } 73 | } else { 74 | err = fmt.Errorf("invalid ip: %s", dst) 75 | return 76 | } 77 | return nil, nil, errors.New("no gateway") 78 | } 79 | 80 | func newRouter(iface *Interface) (*router, error) { 81 | if len(iface.IPNets) == 0 { 82 | return nil, errors.New("no ip") 83 | } 84 | r := &router{ 85 | ipv4Gateway: make(map[*net.IP]struct{}), 86 | ipv6Gateway: make(map[*net.IP]struct{}), 87 | } 88 | for _, ipnet := range iface.IPNets { 89 | switch len(ipnet.Mask) { 90 | case net.IPv4len: 91 | ipnet.IP = ipnet.IP.To4() 92 | r.ipv4Nets = append(r.ipv4Nets, ipnet) 93 | case net.IPv6len: 94 | ipnet.IP = ipnet.IP.To16() 95 | r.ipv6Nets = append(r.ipv6Nets, ipnet) 96 | } 97 | } 98 | for i := 0; i < len(iface.Gateways); i++ { 99 | g := iface.Gateways[i].To4() 100 | if g != nil { // ipv4 101 | r.ipv4Gateway[&g] = struct{}{} 102 | } else { // ipv6 103 | g := iface.Gateways[i].To16() 104 | r.ipv6Gateway[&g] = struct{}{} 105 | } 106 | } 107 | return r, nil 108 | } 109 | -------------------------------------------------------------------------------- /scanner.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "encoding/binary" 5 | "math/big" 6 | "math/rand" 7 | "net" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | 14 | "github.com/google/gopacket/pcap" 15 | "github.com/pkg/errors" 16 | ) 17 | 18 | type Scanner struct { 19 | method string 20 | targets []string 21 | ports map[string]struct{} 22 | opts *Options 23 | generator *Generator 24 | // about count 25 | hostNum *big.Int 26 | scannedNum *big.Int 27 | delta *big.Int 28 | numMu sync.Mutex 29 | // about handle duplicate result 30 | resultsv4 map[[net.IPv4len + 2]byte]struct{} // ip+port 31 | resultsv4Mu sync.Mutex 32 | resultsv6 map[[net.IPv6len + 2]byte]struct{} 33 | resultsv6Mu sync.Mutex 34 | // about scanning 35 | Result chan string 36 | dialer *Dialer // connect 37 | iface *Interface // syn 38 | route *router 39 | salt []byte 40 | sendQueue chan []byte // sendPacket 41 | recvQueue chan []byte // synCapturer -> synParser 42 | gatewayMACs map[string]net.HardwareAddr 43 | gatewayMu sync.Mutex 44 | tokenBucket chan struct{} // rate 45 | // control 46 | started int32 47 | stopped int32 48 | stopSignal chan struct{} 49 | closeOnce sync.Once 50 | wg sync.WaitGroup 51 | } 52 | 53 | func New(targets, ports string, opts *Options) (*Scanner, error) { 54 | if targets == "" { 55 | return nil, errors.New("no target") 56 | } 57 | if ports == "" { 58 | return nil, errors.New("no port") 59 | } 60 | if opts == nil { 61 | opts = new(Options) 62 | } 63 | opts.apply() 64 | s := Scanner{ 65 | method: opts.Method, 66 | targets: split(targets), 67 | ports: make(map[string]struct{}, 1), 68 | opts: opts, 69 | tokenBucket: make(chan struct{}, opts.Rate), 70 | scannedNum: big.NewInt(0), 71 | delta: big.NewInt(1), 72 | Result: make(chan string, 16*opts.Workers), 73 | stopSignal: make(chan struct{}), 74 | } 75 | if !opts.Raw { // handle duplicate result 76 | s.resultsv4 = make(map[[net.IPv4len + 2]byte]struct{}) 77 | s.resultsv6 = make(map[[net.IPv6len + 2]byte]struct{}) 78 | } 79 | // set ports 80 | for _, port := range split(ports) { 81 | ports := strings.Split(port, "-") 82 | if len(ports) == 1 { // single port 83 | err := checkPort(ports[0]) 84 | if err != nil { 85 | return nil, err 86 | } 87 | s.ports[ports[0]] = struct{}{} 88 | } else { // with "-" 89 | err := checkPort(ports[0]) 90 | if err != nil { 91 | return nil, err 92 | } 93 | err = checkPort(ports[1]) 94 | if err != nil { 95 | return nil, err 96 | } 97 | start, _ := strconv.Atoi(ports[0]) 98 | stop, _ := strconv.Atoi(ports[1]) 99 | if start > stop { 100 | return nil, errors.New("invalid port: " + port) 101 | } 102 | for { 103 | s.ports[strconv.Itoa(start)] = struct{}{} 104 | if start == stop { 105 | break 106 | } 107 | start += 1 108 | } 109 | } 110 | } 111 | return &s, nil 112 | } 113 | 114 | func (s *Scanner) Start() error { 115 | if !atomic.CompareAndSwapInt32(&s.started, 0, 1) { 116 | return errors.New("started") 117 | } 118 | var err error 119 | s.generator, err = NewGenerator(s.targets) 120 | if err != nil { 121 | return err 122 | } 123 | // calculate host number 124 | n := s.generator.N 125 | s.hostNum = n.Mul(n, big.NewInt(int64(len(s.ports)))) 126 | switch s.method { 127 | case MethodSYN: 128 | if initErr != nil { 129 | s.Stop() 130 | return initErr 131 | } 132 | s.iface, err = SelectInterface(s.opts.Device) 133 | if err != nil { 134 | s.Stop() 135 | return err 136 | } 137 | s.route, err = newRouter(s.iface) 138 | if err != nil { 139 | s.Stop() 140 | return err 141 | } 142 | s.sendQueue = make(chan []byte, 1024*s.opts.Workers) 143 | s.recvQueue = make(chan []byte, 1024*s.opts.Workers) 144 | s.gatewayMACs = make(map[string]net.HardwareAddr, 2) 145 | // init salt for validate 146 | random := rand.New(rand.NewSource(time.Now().UnixNano())) 147 | s.salt = make([]byte, 16) 148 | for i := 0; i < 16; i++ { 149 | s.salt[i] = byte(random.Intn(256)) 150 | } 151 | var ( 152 | sendersWG sync.WaitGroup 153 | parsersWG sync.WaitGroup 154 | scannersWG sync.WaitGroup 155 | ) 156 | errWait := func() { 157 | sendersWG.Wait() 158 | parsersWG.Wait() 159 | scannersWG.Wait() 160 | } 161 | // start packetSenders 162 | for i := 0; i < s.opts.Senders; i++ { 163 | handle, err := s.newSenderHandle() 164 | if err != nil { 165 | s.Stop() 166 | errWait() 167 | return err 168 | } 169 | sendersWG.Add(1) 170 | go s.packetSender(&sendersWG, handle) 171 | } 172 | // start capturer 173 | iHandle, err := pcap.NewInactiveHandle(s.iface.Device) 174 | if err != nil { 175 | s.Stop() 176 | errWait() 177 | return err 178 | } 179 | _ = iHandle.SetSnapLen(snaplen) 180 | _ = iHandle.SetPromisc(false) 181 | _ = iHandle.SetTimeout(pcap.BlockForever) 182 | _ = iHandle.SetImmediateMode(true) 183 | _ = iHandle.SetBufferSize(16 * 1048576) // 16 MB 184 | capHandle, err := iHandle.Activate() 185 | iHandle.CleanUp() 186 | if err != nil { 187 | s.Stop() 188 | errWait() 189 | return err 190 | } 191 | parsersWG.Add(1) 192 | go s.synCapturer(&parsersWG, capHandle) 193 | // start parsers 194 | for i := 0; i < s.opts.Workers; i++ { 195 | parsersWG.Add(1) 196 | go s.synParser(&parsersWG) 197 | } 198 | // wait to prepare read 199 | time.Sleep(250 * time.Millisecond) 200 | // start scanners 201 | for i := 0; i < s.opts.Workers; i++ { 202 | scannersWG.Add(1) 203 | go s.synScanner(&scannersWG) 204 | } 205 | // wait 206 | s.wg.Add(1) 207 | go func() { 208 | // wait scanner 209 | scannersWG.Wait() 210 | // check is s.Stop() or send finish 211 | select { 212 | case <-s.stopSignal: 213 | default: // send finish and wait 214 | time.Sleep(s.opts.Timeout) 215 | } 216 | // close capturer 217 | capHandle.Close() 218 | // wait parsers 219 | parsersWG.Wait() 220 | // wait senders 221 | close(s.sendQueue) 222 | sendersWG.Wait() 223 | // wait addTokenLoop return 224 | s.closeOnce.Do(func() { 225 | close(s.stopSignal) 226 | }) 227 | s.wg.Done() 228 | close(s.Result) 229 | }() 230 | case MethodConnect: 231 | var localIPs []string 232 | if s.opts.Device != "" { 233 | iface, err := SelectInterface(s.opts.Device) 234 | if err != nil { 235 | s.Stop() 236 | return err 237 | } 238 | l := len(iface.IPNets) 239 | localIPs = make([]string, l) 240 | for i := 0; i < l; i++ { 241 | localIPs[i] = iface.IPNets[i].IP.String() 242 | } 243 | } 244 | s.dialer, err = NewDialer(localIPs, s.opts.Timeout) 245 | if err != nil { 246 | s.Stop() 247 | return err 248 | } 249 | workersWG := sync.WaitGroup{} 250 | for i := 0; i < s.opts.Workers; i++ { 251 | workersWG.Add(1) 252 | go s.connectScanner(&workersWG) 253 | } 254 | // wait 255 | s.wg.Add(1) 256 | go func() { 257 | // wait connectScanners 258 | workersWG.Wait() 259 | // wait addTokenLoop return 260 | s.closeOnce.Do(func() { 261 | close(s.stopSignal) 262 | }) 263 | s.wg.Done() 264 | close(s.Result) 265 | }() 266 | default: 267 | s.Stop() 268 | return errors.New("invalid method") 269 | } 270 | s.wg.Add(1) 271 | go s.addTokenLoop() 272 | return err 273 | } 274 | 275 | func (s *Scanner) Stop() { 276 | if atomic.CompareAndSwapInt32(&s.stopped, 0, 1) { 277 | s.generator.Close() 278 | s.closeOnce.Do(func() { 279 | close(s.stopSignal) 280 | }) 281 | s.wg.Wait() 282 | } 283 | } 284 | 285 | func (s *Scanner) addTokenLoop() { 286 | ticker := time.NewTicker(time.Second) 287 | defer func() { 288 | ticker.Stop() 289 | s.wg.Done() 290 | }() 291 | for { 292 | select { 293 | case <-ticker.C: 294 | for i := 0; i < s.opts.Rate; i++ { 295 | select { 296 | case s.tokenBucket <- struct{}{}: 297 | case <-s.stopSignal: 298 | return 299 | } 300 | } 301 | case <-s.stopSignal: 302 | return 303 | } 304 | } 305 | } 306 | 307 | // handle duplicate result 308 | func (s *Scanner) addResult(ip net.IP, port string) (stop bool) { 309 | if s.opts.Raw { 310 | var address string 311 | if ipv4 := ip.To4(); ipv4 != nil { 312 | address = ipv4.String() + ":" + port 313 | } else { 314 | address = "[" + ip.String() + "]:" + port 315 | } 316 | select { 317 | case s.Result <- address: 318 | case <-s.stopSignal: 319 | stop = true 320 | } 321 | } else { 322 | pn, _ := strconv.Atoi(port) // port number 323 | pb := make([]byte, 2) // port slice 324 | binary.BigEndian.PutUint16(pb, uint16(pn)) 325 | if ipv4 := ip.To4(); ipv4 != nil { // ipv4 326 | var array [net.IPv4len + 2]byte 327 | copy(array[:], ipv4) 328 | copy(array[net.IPv4len:], pb) 329 | s.resultsv4Mu.Lock() 330 | if _, ok := s.resultsv4[array]; ok { 331 | s.resultsv4Mu.Unlock() 332 | } else { 333 | s.resultsv4[array] = struct{}{} 334 | s.resultsv4Mu.Unlock() 335 | // send result 336 | select { 337 | case s.Result <- ipv4.String() + ":" + port: 338 | case <-s.stopSignal: 339 | stop = true 340 | } 341 | } 342 | } else { // ipv6 343 | var array [net.IPv6len + 2]byte 344 | copy(array[:], ip) 345 | copy(array[net.IPv6len:], pb) 346 | s.resultsv6Mu.Lock() 347 | if _, ok := s.resultsv6[array]; ok { 348 | s.resultsv6Mu.Unlock() 349 | } else { 350 | s.resultsv6[array] = struct{}{} 351 | s.resultsv6Mu.Unlock() 352 | // send result 353 | select { 354 | case s.Result <- "[" + ip.String() + "]:" + port: 355 | case <-s.stopSignal: 356 | stop = true 357 | } 358 | } 359 | } 360 | } 361 | return 362 | } 363 | 364 | func (s *Scanner) addScanned() { 365 | s.numMu.Lock() 366 | s.scannedNum.Add(s.scannedNum, s.delta) 367 | s.numMu.Unlock() 368 | } 369 | 370 | func (s *Scanner) HostNum() *big.Int { 371 | n := big.Int{} 372 | n.SetBytes(s.hostNum.Bytes()) 373 | return &n 374 | } 375 | 376 | func (s *Scanner) Scanned() *big.Int { 377 | n := big.Int{} 378 | s.numMu.Lock() 379 | n.SetBytes(s.scannedNum.Bytes()) 380 | s.numMu.Unlock() 381 | return &n 382 | } 383 | 384 | func checkPort(port string) error { 385 | n, err := strconv.Atoi(port) 386 | if err != nil { 387 | return errors.WithStack(err) 388 | } 389 | if n < 1 || n > 65535 { 390 | return errors.New("invalid port") 391 | } 392 | return nil 393 | } 394 | -------------------------------------------------------------------------------- /scanner_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestScanner_Start(t *testing.T) { 11 | for addr := range testnewScanner(t).Result { 12 | t.Log(addr) 13 | } 14 | } 15 | 16 | func TestScanner_Stop(t *testing.T) { 17 | testnewScanner(t).Stop() 18 | } 19 | 20 | func testnewScanner(t *testing.T) *Scanner { 21 | port := testListener(t) 22 | iface, err := SelectInterface("") 23 | require.NoError(t, err) 24 | host := iface.IPNets[0].IP.String() 25 | s, err := New(host, port, &Options{Method: MethodConnect}) 26 | require.NoError(t, err) 27 | err = s.Start() 28 | require.NoError(t, err) 29 | return s 30 | } 31 | 32 | func testListener(t *testing.T) string { 33 | listener, err := net.Listen("tcp", ":0") 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | go func() { 38 | for { 39 | conn, err := listener.Accept() 40 | if err != nil { 41 | return 42 | } 43 | go func(conn net.Conn) { 44 | _ = conn.Close() 45 | }(conn) 46 | } 47 | }() 48 | _, port, _ := net.SplitHostPort(listener.Addr().String()) 49 | return port 50 | } 51 | -------------------------------------------------------------------------------- /syn.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/binary" 6 | "errors" 7 | "net" 8 | "runtime" 9 | "strconv" 10 | "sync" 11 | "time" 12 | 13 | "github.com/google/gopacket" 14 | "github.com/google/gopacket/layers" 15 | "github.com/google/gopacket/pcap" 16 | ) 17 | 18 | const ( 19 | snaplen = 65536 20 | ) 21 | 22 | // newSenderHandle will create a *pcap.Handle only for send 23 | // this handle will SetTimeout(1) 24 | func (s *Scanner) newSenderHandle() (*pcap.Handle, error) { 25 | iHandle, err := pcap.NewInactiveHandle(s.iface.Device) 26 | if err != nil { 27 | return nil, err 28 | } 29 | defer iHandle.CleanUp() 30 | _ = iHandle.SetSnapLen(snaplen) 31 | _ = iHandle.SetPromisc(false) 32 | _ = iHandle.SetTimeout(1) // only send 33 | _ = iHandle.SetImmediateMode(true) 34 | return iHandle.Activate() 35 | } 36 | 37 | // packetSender is used to control send packet rate 38 | // synParser & synScanner use it 39 | func (s *Scanner) packetSender(wg *sync.WaitGroup, handle *pcap.Handle) { 40 | runtime.LockOSThread() 41 | defer func() { 42 | runtime.UnlockOSThread() 43 | handle.Close() 44 | wg.Done() 45 | }() 46 | var packet []byte 47 | for { 48 | select { 49 | case packet = <-s.sendQueue: 50 | if packet == nil { 51 | return 52 | } 53 | _ = handle.WritePacketData(packet) 54 | case <-s.stopSignal: 55 | return 56 | } 57 | } 58 | } 59 | 60 | // send packet 61 | // must copy packet slice 62 | // 63 | // unexpected fault address 0x613412a 64 | // fatal error: fault 65 | // [signal 0xc0000005 code=0x0 addr=0x613412a pc=0x45ed82] 66 | func (s *Scanner) sendPacket(packet []byte) { 67 | // rate 68 | select { 69 | case <-s.tokenBucket: 70 | case <-s.stopSignal: 71 | return 72 | } 73 | b := make([]byte, len(packet)) 74 | copy(b, packet) 75 | select { 76 | case s.sendQueue <- b: 77 | case <-s.stopSignal: 78 | return 79 | } 80 | } 81 | 82 | func (s *Scanner) synCapturer(wg *sync.WaitGroup, handle *pcap.Handle) { 83 | runtime.LockOSThread() 84 | defer func() { 85 | runtime.UnlockOSThread() 86 | close(s.recvQueue) // synParser will close 87 | wg.Done() 88 | }() 89 | var ( 90 | data []byte 91 | err error 92 | ) 93 | // TODO BPFFilter for IPv6 94 | // _ = handle.SetBPFFilter("tcp[13] = 0x12") 95 | // is not support ipv6 96 | _ = handle.SetBPFFilter("tcp") 97 | for { 98 | data, _, err = handle.ZeroCopyReadPacketData() 99 | if err != nil { 100 | return 101 | } 102 | d := make([]byte, len(data)) 103 | copy(d, data) 104 | s.recvQueue <- d 105 | } 106 | } 107 | 108 | func (s *Scanner) synParser(wg *sync.WaitGroup) { 109 | defer wg.Done() 110 | var ( 111 | err error 112 | data []byte 113 | eth layers.Ethernet 114 | ipv4 layers.IPv4 115 | ipv6 layers.IPv6 116 | tcp layers.TCP 117 | decoded []gopacket.LayerType 118 | hash []byte 119 | ) 120 | parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, 121 | ð, &ipv4, &ipv6, &tcp) 122 | parser.IgnoreUnsupported = true 123 | // for send RST 124 | opt := gopacket.SerializeOptions{ 125 | FixLengths: true, 126 | ComputeChecksums: true, 127 | } 128 | buf := gopacket.NewSerializeBuffer() 129 | sha := sha256.New() 130 | for data = range s.recvQueue { 131 | err = parser.DecodeLayers(data, &decoded) 132 | if err != nil { 133 | continue 134 | } 135 | // check port 136 | port := strconv.Itoa(int(tcp.SrcPort)) 137 | if _, ok := s.ports[port]; !ok { 138 | continue 139 | } 140 | // send address 141 | for i := 0; i < len(decoded); i++ { 142 | switch decoded[i] { 143 | case layers.LayerTypeIPv4: 144 | // check hash 145 | sha.Reset() 146 | sha.Write(ipv4.SrcIP) 147 | sha.Write(s.salt) 148 | hash = sha.Sum(nil) 149 | // check port and ack 150 | if !(tcp.SYN && tcp.ACK) { 151 | goto getNewData 152 | } 153 | if uint16(tcp.DstPort) != binary.BigEndian.Uint16(hash[:2]) || 154 | tcp.Ack-1 != binary.BigEndian.Uint32(hash[2:6]) { 155 | goto getNewData 156 | } 157 | if s.addResult(ipv4.SrcIP, port) { 158 | return 159 | } 160 | // send RST 161 | // swap 162 | eth.SrcMAC, eth.DstMAC = eth.DstMAC, eth.SrcMAC 163 | ipv4.SrcIP, ipv4.DstIP = ipv4.DstIP, ipv4.SrcIP 164 | tcp.SrcPort, tcp.DstPort = tcp.DstPort, tcp.SrcPort 165 | // tcp.Seq = tcp.Ack 166 | // tcp.Ack = 0 167 | tcp.Seq, tcp.Ack = tcp.Ack, tcp.Seq+1 168 | // set flag 169 | tcp.SYN = false 170 | tcp.ACK = false 171 | tcp.RST = true 172 | // send packet 173 | _ = tcp.SetNetworkLayerForChecksum(&ipv4) 174 | _ = gopacket.SerializeLayers(buf, opt, ð, &ipv4, &tcp) 175 | s.sendPacket(buf.Bytes()) 176 | case layers.LayerTypeIPv6: 177 | // check hash 178 | sha.Reset() 179 | sha.Write(ipv6.SrcIP) 180 | sha.Write(s.salt) 181 | hash = sha.Sum(nil) 182 | // check port and ack 183 | if !(tcp.SYN && tcp.ACK) { 184 | goto getNewData 185 | } 186 | if uint16(tcp.DstPort) != binary.BigEndian.Uint16(hash[:2]) || 187 | tcp.Ack-1 != binary.BigEndian.Uint32(hash[2:6]) { 188 | goto getNewData 189 | } 190 | if s.addResult(ipv6.SrcIP, port) { 191 | return 192 | } 193 | // send RST 194 | // swap 195 | eth.SrcMAC, eth.DstMAC = eth.DstMAC, eth.SrcMAC 196 | ipv6.SrcIP, ipv6.DstIP = ipv6.DstIP, ipv6.SrcIP 197 | tcp.SrcPort, tcp.DstPort = tcp.DstPort, tcp.SrcPort 198 | // tcp.Seq = tcp.Ack 199 | // tcp.Ack = 0 200 | tcp.Seq, tcp.Ack = tcp.Ack, tcp.Seq+1 201 | // set flag 202 | tcp.SYN = false 203 | tcp.ACK = false 204 | tcp.RST = true 205 | // send packet 206 | _ = tcp.SetNetworkLayerForChecksum(&ipv6) 207 | _ = gopacket.SerializeLayers(buf, opt, ð, &ipv6, &tcp) 208 | s.sendPacket(buf.Bytes()) 209 | } 210 | } 211 | getNewData: 212 | } 213 | } 214 | 215 | func (s *Scanner) synScanner(wg *sync.WaitGroup) { 216 | defer wg.Done() 217 | var ( 218 | target net.IP 219 | port string 220 | gateway net.IP 221 | srcIP net.IP 222 | err error 223 | ) 224 | eth := layers.Ethernet{} 225 | ipv4 := layers.IPv4{ 226 | Version: 4, 227 | Flags: layers.IPv4DontFragment, 228 | TTL: 128, 229 | Protocol: layers.IPProtocolTCP, 230 | } 231 | ipv6 := layers.IPv6{ 232 | Version: 6, 233 | HopLimit: 128, 234 | NextHeader: layers.IPProtocolTCP, 235 | } 236 | tcp := layers.TCP{ 237 | SYN: true, 238 | // ECE: true, 239 | // CWR: true, 240 | Window: 8192, 241 | // Options:[]layers.TCPOption{} 242 | } 243 | opt := gopacket.SerializeOptions{ 244 | FixLengths: true, 245 | ComputeChecksums: true, 246 | } 247 | buf := gopacket.NewSerializeBuffer() 248 | scan := func() { 249 | // set dst MAC 250 | if gateway != nil { // send to gateway 251 | eth.DstMAC, err = s.getGatewayHardwareAddr(srcIP, gateway) 252 | } else { // LAN 253 | eth.DstMAC, err = s.getHardwareAddr(srcIP, target) 254 | } 255 | if err != nil { 256 | return 257 | } 258 | eth.SrcMAC = s.iface.MAC 259 | // hash 260 | sha := sha256.New() 261 | sha.Write(target) 262 | sha.Write(s.salt) 263 | hash := sha.Sum(nil) 264 | // set src port 265 | tcp.SrcPort = layers.TCPPort(binary.BigEndian.Uint16(hash[:2])) 266 | tcp.Seq = binary.BigEndian.Uint32(hash[2:6]) 267 | // set dst port 268 | p, _ := strconv.Atoi(port) 269 | tcp.DstPort = layers.TCPPort(p) 270 | // set ip 271 | switch len(target) { 272 | case net.IPv4len: 273 | eth.EthernetType = layers.EthernetTypeIPv4 274 | ipv4.SrcIP = srcIP 275 | ipv4.DstIP = target 276 | _ = tcp.SetNetworkLayerForChecksum(&ipv4) 277 | _ = gopacket.SerializeLayers(buf, opt, ð, &ipv4, &tcp) 278 | case net.IPv6len: 279 | eth.EthernetType = layers.EthernetTypeIPv6 280 | ipv6.SrcIP = srcIP 281 | ipv6.DstIP = target 282 | _ = tcp.SetNetworkLayerForChecksum(&ipv6) 283 | _ = gopacket.SerializeLayers(buf, opt, ð, &ipv6, &tcp) 284 | } 285 | s.sendPacket(buf.Bytes()) 286 | } 287 | portsLen := len(s.ports) 288 | for { 289 | getIP: 290 | select { 291 | case target = <-s.generator.IP: 292 | if target == nil { 293 | return 294 | } 295 | for port = range s.ports { 296 | // check target 297 | if target.Equal(net.IPv4bcast) || 298 | target.IsUnspecified() || 299 | target.IsMulticast() { 300 | for i := 0; i < portsLen; i++ { 301 | s.addScanned() 302 | } 303 | goto getIP 304 | } 305 | // scan loopback 306 | if target.IsLoopback() { 307 | s.simpleScan(target, port) 308 | goto getIP 309 | } 310 | // get router 311 | gateway, srcIP, err = s.route.route(target) 312 | if err != nil { 313 | if err == errRouteSelf { 314 | s.simpleScan(target, port) 315 | goto getIP 316 | } 317 | s.addScanned() 318 | goto getIP 319 | } 320 | scan() 321 | s.addScanned() 322 | } 323 | case <-s.stopSignal: 324 | return 325 | } 326 | } 327 | } 328 | 329 | func (s *Scanner) simpleScan(ip net.IP, port string) { 330 | var address string 331 | if len(ip) == net.IPv4len { 332 | address = ip.String() + ":" + port 333 | } else { 334 | address = "[" + ip.String() + "]:" + port 335 | } 336 | dialer := net.Dialer{Timeout: s.opts.Timeout} 337 | conn, err := dialer.Dial("tcp", address) 338 | s.addScanned() 339 | if err != nil { 340 | return 341 | } 342 | dsrIP := conn.RemoteAddr().(*net.TCPAddr).IP 343 | _ = conn.Close() 344 | s.addResult(dsrIP, port) 345 | } 346 | 347 | func (s *Scanner) getGatewayHardwareAddr(srcIP, gateway net.IP) (net.HardwareAddr, error) { 348 | s.gatewayMu.Lock() 349 | defer s.gatewayMu.Unlock() 350 | gatewayStr := gateway.String() 351 | if mac, ok := s.gatewayMACs[gatewayStr]; ok { 352 | return mac, nil 353 | } 354 | haddr, err := s.getHardwareAddr(srcIP, gateway) 355 | if err != nil { 356 | return nil, err 357 | } 358 | s.gatewayMACs[gatewayStr] = haddr 359 | return haddr, nil 360 | } 361 | 362 | var ( 363 | zeroMAC = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 364 | ipv6mcast = []byte{0x33, 0x33, 0xFF, 0x00, 0x00, 0x00} 365 | icmpv6ns = net.IP{0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 366 | 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00} 367 | ) 368 | 369 | func (s *Scanner) getHardwareAddr(srcIP, dstIP net.IP) (net.HardwareAddr, error) { 370 | srcIPLen := len(srcIP) 371 | dstIPLen := len(dstIP) 372 | if srcIPLen != dstIPLen { 373 | return nil, errors.New("not the same size") 374 | } 375 | // wait 2 seconds for reply 376 | iHandle, err := pcap.NewInactiveHandle(s.iface.Device) 377 | if err != nil { 378 | return nil, err 379 | } 380 | defer iHandle.CleanUp() 381 | _ = iHandle.SetSnapLen(snaplen) 382 | _ = iHandle.SetPromisc(false) 383 | _ = iHandle.SetTimeout(time.Second) 384 | _ = iHandle.SetImmediateMode(true) 385 | handle, err := iHandle.Activate() 386 | if err != nil { 387 | return nil, err 388 | } 389 | defer handle.Close() 390 | // packet 391 | eth := layers.Ethernet{ 392 | SrcMAC: s.iface.MAC, 393 | } 394 | opt := gopacket.SerializeOptions{ 395 | FixLengths: true, 396 | } 397 | buf := gopacket.NewSerializeBuffer() 398 | switch srcIPLen { 399 | case net.IPv4len: // ARP 400 | _ = handle.SetBPFFilter("arp[7] = 0x02") // reply 401 | eth.DstMAC = layers.EthernetBroadcast 402 | eth.EthernetType = layers.EthernetTypeARP 403 | arp := layers.ARP{ 404 | AddrType: layers.LinkTypeEthernet, 405 | Protocol: layers.EthernetTypeIPv4, 406 | HwAddressSize: 6, 407 | ProtAddressSize: 4, 408 | Operation: layers.ARPRequest, 409 | SourceHwAddress: []byte(s.iface.MAC), 410 | SourceProtAddress: []byte(srcIP), 411 | DstHwAddress: zeroMAC, 412 | DstProtAddress: []byte(dstIP), 413 | } 414 | _ = gopacket.SerializeLayers(buf, opt, ð, &arp) 415 | _ = handle.WritePacketData(buf.Bytes()) 416 | // receive reply 417 | var decoded []gopacket.LayerType 418 | parser := gopacket.NewDecodingLayerParser( 419 | layers.LayerTypeEthernet, ð, &arp) 420 | // pass error: "No decoder for layer type Payload" 421 | parser.IgnoreUnsupported = true 422 | for { 423 | data, _, err := handle.ZeroCopyReadPacketData() 424 | if err != nil { 425 | return nil, err 426 | } 427 | err = parser.DecodeLayers(data, &decoded) 428 | if err != nil { 429 | continue 430 | } 431 | if net.IP(arp.SourceProtAddress).Equal(dstIP) { 432 | // must copy 433 | // parser.DecodeLayers is quote from data 434 | // data from handle.ZeroCopyReadPacketData() 435 | hwAddress := make([]byte, 6) 436 | copy(hwAddress, arp.SourceHwAddress) 437 | return hwAddress, nil 438 | } 439 | } 440 | case net.IPv6len: // ICMPv6 441 | _ = handle.SetBPFFilter("icmp6[0] = 0x88") // reply 442 | opt.ComputeChecksums = true 443 | // set dst MAC 444 | mac := make([]byte, 6) // MAC size 445 | copy(mac[:3], ipv6mcast[:3]) 446 | mac[3] = dstIP[13] 447 | mac[4] = dstIP[14] 448 | mac[5] = dstIP[15] 449 | eth.DstMAC = mac 450 | eth.EthernetType = layers.EthernetTypeIPv6 451 | // set dst ip 452 | dIP := make([]byte, net.IPv6len) 453 | copy(dIP, icmpv6ns) 454 | dIP[13] = dstIP[13] 455 | dIP[14] = dstIP[14] 456 | dIP[15] = dstIP[15] 457 | ipv6 := layers.IPv6{ 458 | Version: 6, 459 | NextHeader: layers.IPProtocolICMPv6, 460 | HopLimit: 255, 461 | SrcIP: srcIP, 462 | DstIP: dIP, 463 | } 464 | typ := layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborSolicitation, 0) 465 | icmpv6 := layers.ICMPv6{ 466 | TypeCode: typ, 467 | } 468 | ns := layers.ICMPv6NeighborSolicitation{ 469 | TargetAddress: dstIP, 470 | } 471 | icmpv6Opt := layers.ICMPv6Option{ 472 | Type: layers.ICMPv6OptSourceAddress, 473 | Data: s.iface.MAC, 474 | } 475 | ns.Options = append(ns.Options, icmpv6Opt) 476 | _ = icmpv6.SetNetworkLayerForChecksum(&ipv6) 477 | _ = gopacket.SerializeLayers(buf, opt, ð, &ipv6, &icmpv6, &ns) 478 | _ = handle.WritePacketData(buf.Bytes()) 479 | // receive reply 480 | var ( 481 | decoded []gopacket.LayerType 482 | na layers.ICMPv6NeighborAdvertisement 483 | ) 484 | parser := gopacket.NewDecodingLayerParser( 485 | layers.LayerTypeEthernet, ð, &ipv6, &icmpv6, &na) 486 | // pass error: "No decoder for layer type Payload" 487 | parser.IgnoreUnsupported = true 488 | for { 489 | data, _, err := handle.ZeroCopyReadPacketData() 490 | if err != nil { 491 | return nil, err 492 | } 493 | err = parser.DecodeLayers(data, &decoded) 494 | if err != nil { 495 | continue 496 | } 497 | if ipv6.SrcIP.Equal(dstIP) { 498 | if len(na.Options) != 1 { 499 | continue 500 | } 501 | if na.Options[0].Type != layers.ICMPv6OptTargetAddress { // type 502 | continue 503 | } 504 | if len(na.Options[0].Data) != 6 { // MAC size 505 | continue 506 | } 507 | // must copy 508 | // parser.DecodeLayers is quote from data 509 | // data from handle.ZeroCopyReadPacketData() 510 | hwAddress := make([]byte, 6) 511 | copy(hwAddress, na.Options[0].Data) 512 | return hwAddress, nil 513 | } 514 | } 515 | default: 516 | return nil, errors.New("invalid ip size") 517 | } 518 | } 519 | -------------------------------------------------------------------------------- /syn_test.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestSynScanner(t *testing.T) { 12 | start := time.Now() 13 | targets := "8.8.8.8-8.8.8.10, 2606:4700:4700::1001-2606:4700:4700::1003" 14 | ports := "53,54,55-57" 15 | opt := Options{ 16 | Timeout: 5 * time.Second, 17 | Rate: 2000, 18 | Workers: 2, 19 | } 20 | scanner, err := New(targets, ports, &opt) 21 | require.NoError(t, err) 22 | err = scanner.Start() 23 | require.NoError(t, err) 24 | result := make(map[string]struct{}) 25 | for address := range scanner.Result { 26 | result[address] = struct{}{} 27 | t.Log(address) 28 | } 29 | expected := []string{ 30 | "8.8.8.8:53", 31 | "[2606:4700:4700::1001]:53", 32 | } 33 | for i := 0; i < len(expected); i++ { 34 | if _, ok := result[expected[i]]; !ok { 35 | t.Fatal(expected[i], "is lost") 36 | } 37 | } 38 | t.Log("result:", len(result), "time:", time.Since(start)) 39 | require.Equal(t, scanner.HostNum().String(), "30") 40 | require.Equal(t, scanner.Scanned().String(), "30") 41 | } 42 | 43 | func TestSynScanner_simple(t *testing.T) { 44 | start := time.Now() 45 | targets := "127.0.0.1, ::1" 46 | port := testListener(t) 47 | scanner, err := New(targets, port, nil) 48 | require.NoError(t, err) 49 | err = scanner.Start() 50 | require.NoError(t, err) 51 | result := make(map[string]struct{}) 52 | for address := range scanner.Result { 53 | result[address] = struct{}{} 54 | t.Log(address) 55 | } 56 | expected := []string{ 57 | "127.0.0.1:" + port, 58 | "[::1]:" + port, 59 | } 60 | for i := 0; i < len(expected); i++ { 61 | if _, ok := result[expected[i]]; !ok { 62 | t.Fatal(expected[i], "is lost") 63 | } 64 | } 65 | t.Log("result:", len(result), "time:", time.Since(start)) 66 | require.Equal(t, scanner.HostNum().String(), "2") 67 | require.Equal(t, scanner.Scanned().String(), "2") 68 | } 69 | 70 | func TestSynScanner_Stop(t *testing.T) { 71 | targets := "8.8.8.8/16" 72 | ports := "53,54,55-57" 73 | opt := Options{ 74 | Timeout: 10 * time.Second, 75 | Rate: 10, 76 | } 77 | scanner, err := New(targets, ports, &opt) 78 | require.NoError(t, err) 79 | err = scanner.Start() 80 | require.NoError(t, err) 81 | go func() { 82 | err = scanner.Start() 83 | require.Error(t, err) 84 | }() 85 | time.Sleep(2 * time.Second) 86 | scanner.Stop() 87 | go func() { scanner.Stop() }() 88 | time.Sleep(250 * time.Millisecond) 89 | require.Equal(t, 2, runtime.NumGoroutine()) 90 | } 91 | 92 | func TestSynScanner_Duplicate(t *testing.T) { 93 | start := time.Now() 94 | targets := "123.206.1.1/16" 95 | ports := "80" 96 | opt := Options{ 97 | Timeout: 5 * time.Second, 98 | Rate: 30000, 99 | } 100 | scanner, err := New(targets, ports, &opt) 101 | require.NoError(t, err) 102 | err = scanner.Start() 103 | require.NoError(t, err) 104 | result := make(map[string]struct{}) 105 | for address := range scanner.Result { 106 | if _, ok := result[address]; ok { 107 | t.Fatal("duplicate:", address) 108 | } 109 | result[address] = struct{}{} 110 | } 111 | t.Log("result:", len(result), "time:", time.Since(start)) 112 | require.Equal(t, scanner.HostNum().String(), "65536") 113 | require.Equal(t, scanner.Scanned().String(), "65536") 114 | } 115 | 116 | func TestSynScanner_Raw(t *testing.T) { 117 | start := time.Now() 118 | targets := "123.206.1.1/16" 119 | ports := "80" 120 | opt := Options{ 121 | Timeout: 5 * time.Second, 122 | Rate: 2800, 123 | Raw: true, 124 | } 125 | scanner, err := New(targets, ports, &opt) 126 | require.NoError(t, err) 127 | err = scanner.Start() 128 | require.NoError(t, err) 129 | result := make(map[string]struct{}) 130 | for address := range scanner.Result { 131 | if _, ok := result[address]; ok { 132 | t.Log("duplicate:", address) 133 | continue 134 | } 135 | result[address] = struct{}{} 136 | } 137 | t.Log("result:", len(result), "time:", time.Since(start)) 138 | require.Equal(t, scanner.HostNum().String(), "65536") 139 | require.Equal(t, scanner.Scanned().String(), "65536") 140 | } 141 | 142 | func TestSynScannerAccuracy(t *testing.T) { 143 | targets := "123.206.1.1/16" 144 | ports := "80" 145 | opt := Options{ 146 | Device: "Ethernet0", 147 | Method: MethodConnect, 148 | Timeout: 5 * time.Second, 149 | Rate: 1000, 150 | } 151 | // connect 152 | start := time.Now() 153 | scanner, err := New(targets, ports, &opt) 154 | require.NoError(t, err) 155 | err = scanner.Start() 156 | require.NoError(t, err) 157 | connectResult := make(map[string]struct{}) 158 | for address := range scanner.Result { 159 | connectResult[address] = struct{}{} 160 | } 161 | t.Log("tcp ok", time.Since(start)) 162 | time.Sleep(2 * opt.Timeout) 163 | // syn 164 | start = time.Now() 165 | opt.Method = MethodSYN 166 | opt.Rate = 2000 167 | opt.Workers = 16 168 | scanner, err = New(targets, ports, &opt) 169 | require.NoError(t, err) 170 | err = scanner.Start() 171 | require.NoError(t, err) 172 | synResult := make(map[string]struct{}) 173 | for address := range scanner.Result { 174 | synResult[address] = struct{}{} 175 | } 176 | t.Log("syn ok", time.Since(start)) 177 | // compare 178 | var synScanned int 179 | for address := range connectResult { 180 | if _, ok := synResult[address]; ok { 181 | synScanned += 1 182 | } else { 183 | t.Log(address, "is lost") 184 | } 185 | } 186 | connectResultL := len(connectResult) 187 | synResultL := len(synResult) 188 | t.Logf("connect: %d syn: %d", connectResultL, synResultL) 189 | accuracy := float64(synScanned) / float64(connectResultL) * 100 190 | t.Logf("accuracy: %f%%", accuracy) 191 | } 192 | --------------------------------------------------------------------------------