├── README.md ├── go.mod ├── go.sum ├── gomassscan_test.go ├── scan.go ├── scan_unix.go └── tcpsequencer.go /README.md: -------------------------------------------------------------------------------- 1 | # gomasscan 2 | gomasscan是一个纯go编写的masscan扫描库 3 | 4 | ## 前提 5 | 6 | - 只支持darwin/linux 7 | - 需要高权限 8 | - 需要安装libpcap 9 | 10 | ## 写在前面 11 | 12 | - 部署PF_RING DNA设备的情况下,理论发包量可以达到到1000万/秒 13 | - darwin上的理论发包量能够达到30万/秒,linux则能达到150万 14 | - darwin上实测,发包量可以达到14万/秒,linux暂未测试,为什么会存在这样的折扣,原因未知 15 | 16 | ## 使用 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "github.com/lcvvvv/gomasscan" 24 | "time" 25 | ) 26 | 27 | func main() { 28 | //创建扫描器对象 29 | client, err := gomasscan.NewScanner() 30 | if err != nil { 31 | panic(err) 32 | } 33 | defer client.Done() 34 | //开放端口处理函数 35 | client.HandlerOpen = func(ip string, port int) { 36 | //输出开放端口 37 | fmt.Println(ip, port) 38 | } 39 | //将IP地址加入筛选范围内 40 | var ip = "192.168.0.1" 41 | var startTime = time.Now() 42 | _ = client.Add(ip) 43 | //开始扫描 44 | go func() { 45 | for i := 0; i < 65536; i++ { 46 | client.SendSYN(ip, i, gomasscan.SYN) 47 | } 48 | }() 49 | for { 50 | time.Sleep(time.Second) 51 | elapsed := time.Since(startTime) 52 | seconds := elapsed.Seconds() 53 | fmt.Println("发包量", client.Count()/uint64(seconds), "/s") 54 | } 55 | 56 | } 57 | ``` 58 | 59 | ## 感谢 60 | 61 | - [naabu](https://github.com/projectdiscovery/naabu) 62 | - [masscan](https://github.com/zan8in/masscan) -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lcvvvv/gomasscan 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/google/gopacket v1.1.19 7 | github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 8 | github.com/projectdiscovery/ipranger v0.0.3-0.20210831161617-ac80efae0961 9 | github.com/projectdiscovery/networkpolicy v0.0.1 10 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 11 | ) 12 | 13 | require ( 14 | github.com/golang/snappy v0.0.3 // indirect 15 | github.com/projectdiscovery/blackrock v0.0.0-20210415162320-b38689ae3a2e // indirect 16 | github.com/projectdiscovery/hmap v0.0.1 // indirect 17 | github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3 // indirect 18 | github.com/projectdiscovery/mapcidr v0.0.8 // indirect 19 | github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 // indirect 20 | github.com/syndtr/goleveldb v1.0.0 // indirect 21 | github.com/yl2chen/cidranger v1.0.2 // indirect 22 | golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 6 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 7 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 8 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 9 | github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= 10 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 11 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 12 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 13 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 14 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 15 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 16 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 17 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 18 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 19 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 20 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 21 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 22 | github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 23 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 24 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 26 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 27 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 28 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 29 | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= 30 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 31 | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= 32 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 33 | github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= 34 | github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 35 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 36 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 37 | github.com/projectdiscovery/blackrock v0.0.0-20210415162320-b38689ae3a2e h1:7bwaFH1jvtOo5ndhTQgoA349ozhX+1dc4b6tbaPnBOA= 38 | github.com/projectdiscovery/blackrock v0.0.0-20210415162320-b38689ae3a2e/go.mod h1:/IsapnEYiWG+yEDPXp0e8NWj3npzB9Ccy9lXEUJwMZs= 39 | github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE= 40 | github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= 41 | github.com/projectdiscovery/hmap v0.0.1 h1:VAONbJw5jP+syI5smhsfkrq9XPGn4aiYy5pR6KR1wog= 42 | github.com/projectdiscovery/hmap v0.0.1/go.mod h1:VDEfgzkKQdq7iGTKz8Ooul0NuYHQ8qiDs6r8bPD1Sb0= 43 | github.com/projectdiscovery/ipranger v0.0.2/go.mod h1:kcAIk/lo5rW+IzUrFkeYyXnFJ+dKwYooEOHGVPP/RWE= 44 | github.com/projectdiscovery/ipranger v0.0.3-0.20210831161617-ac80efae0961 h1:WqZewGI1Oe8uAwiFaHb8NNM1CLxoxxy6OV5NNePmyxM= 45 | github.com/projectdiscovery/ipranger v0.0.3-0.20210831161617-ac80efae0961/go.mod h1:lh3xQA3Hv7PSwA5t0L9MwTq/mhE6io7zMXmiKHxrxGg= 46 | github.com/projectdiscovery/iputil v0.0.0-20210414194613-4b4d2517acf0/go.mod h1:PQAqn5h5NXsQTF4ZA00ZTYLRzGCjOtcCq8llAqrsd1A= 47 | github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3 h1:VZ9H51A7tI7R/9I5W5l960Nkq7eMJqGd3y1wsuwzdjE= 48 | github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3/go.mod h1:blmYJkS8lSrrx3QcmcgS2tZIxlojeVmoGeA9twslCBU= 49 | github.com/projectdiscovery/mapcidr v0.0.4/go.mod h1:ALOIj6ptkWujNoX8RdQwB2mZ+kAmKuLJBq9T5gR5wG0= 50 | github.com/projectdiscovery/mapcidr v0.0.6/go.mod h1:ZEBhMmBU3laUl3g9QGTrzJku1VJOzjdFwW01f/zVVzM= 51 | github.com/projectdiscovery/mapcidr v0.0.7/go.mod h1:7CzdUdjuLVI0s33dQ33lWgjg3vPuLFw2rQzZ0RxkT00= 52 | github.com/projectdiscovery/mapcidr v0.0.8 h1:16U05F2x3o/jSTsxSCY2hCuCs9xOSwVxjo2zlsL4L4E= 53 | github.com/projectdiscovery/mapcidr v0.0.8/go.mod h1:7CzdUdjuLVI0s33dQ33lWgjg3vPuLFw2rQzZ0RxkT00= 54 | github.com/projectdiscovery/networkpolicy v0.0.0-20210414200240-b3fa8abf324c/go.mod h1:KZIP5x7t+bH2dASgnYaqbiLI4z0gxXzekwUtarrQMdc= 55 | github.com/projectdiscovery/networkpolicy v0.0.1 h1:RGRuPlxE8WLFF9tdKSjTsYiTIKHNHW20Kl0nGGiRb1I= 56 | github.com/projectdiscovery/networkpolicy v0.0.1/go.mod h1:asvdg5wMy3LPVMGALatebKeOYH5n5fV5RCTv6DbxpIs= 57 | github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 h1:xbL1/7h0k6HE3RzPdYk9W/8pUxESrGWewTaZdIB5Pes= 58 | github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= 59 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 60 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 61 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 62 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 63 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 64 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 65 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 66 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 67 | github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= 68 | github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= 69 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 70 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 71 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 72 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 73 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 74 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 75 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 76 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 77 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 78 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 79 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 80 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 81 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 82 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 84 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 85 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= 86 | golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 87 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 88 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 89 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 90 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 91 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 92 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 93 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 94 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 95 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 96 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 97 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 98 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 99 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 100 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 101 | -------------------------------------------------------------------------------- /gomassscan_test.go: -------------------------------------------------------------------------------- 1 | package gomasscan 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestScanner(t *testing.T) { 10 | //创建扫描器对象 11 | client, err := NewScanner() 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer client.Done() 16 | //开放端口处理函数 17 | client.HandlerOpen = func(ip string, port int) { 18 | fmt.Println(ip, port) 19 | } 20 | 21 | var ip = "192.168.0.1" 22 | //将IP地址加入筛选范围内 23 | err = client.Add(ip) 24 | if err != nil { 25 | panic(err) 26 | } 27 | startTime := time.Now() 28 | fmt.Println("开始发送数据") 29 | go func() { 30 | for { 31 | client.SendSYN(ip, 81, SYN) 32 | } 33 | }() 34 | client.SetRate(655350) 35 | time.Sleep(time.Second) 36 | for { 37 | elapsed := time.Since(startTime) 38 | seconds := elapsed.Seconds() 39 | fmt.Println("发包量", client.Count()/uint64(seconds), "/s") 40 | time.Sleep(time.Second * 5) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /scan.go: -------------------------------------------------------------------------------- 1 | package gomasscan 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "golang.org/x/time/rate" 7 | "log" 8 | "math/rand" 9 | "net" 10 | "os" 11 | "strings" 12 | "sync/atomic" 13 | "time" 14 | 15 | "github.com/google/gopacket" 16 | "github.com/google/gopacket/layers" 17 | "github.com/projectdiscovery/ipranger" 18 | "github.com/projectdiscovery/networkpolicy" 19 | ) 20 | 21 | var logger = Logger(log.New(os.Stderr, "[scanner]", log.Ldate|log.Ltime)) 22 | 23 | type Logger interface { 24 | Println(...interface{}) 25 | Printf(string, ...interface{}) 26 | } 27 | 28 | func SetLogger(log Logger) { 29 | logger = log 30 | } 31 | 32 | // PkgFlag represent the TCP packet flag 33 | type PkgFlag int 34 | 35 | const ( 36 | SYN PkgFlag = iota 37 | ACK 38 | ) 39 | 40 | type Scanner struct { 41 | //发包源IP 42 | sourceIP net.IP 43 | //发包源网卡 44 | networkInterface *net.Interface 45 | //发包源端口 46 | listenPort int 47 | //目的IP,地址范围,便于处理返回数据包 48 | ipRanger *ipranger.IPRanger 49 | //监听网卡组 50 | handlers Handlers 51 | //端口扫描成功结果发送信道 52 | tcpResultChan chan *PkgResult 53 | //发包相关 54 | tcpSequencer *TCPSequencer 55 | tcpPacketListener net.PacketConn 56 | tcpPacketSend chan *PkgSend 57 | serializeOptions gopacket.SerializeOptions 58 | //结束标识符 59 | done bool 60 | HandlerOpen func(ip string, port int) 61 | //发包速率 62 | limit *rate.Limiter 63 | limitCount uint64 64 | 65 | //retries int 66 | //rate int 67 | //timeout time.Duration 68 | //icmpPacketListener net.PacketConn 69 | //proxyDialer proxy.Dialer 70 | //Ports []int 71 | //icmpChan chan *PkgResult 72 | //icmpPacketSend chan *PkgSend 73 | } 74 | 75 | // PkgSend is a TCP package 76 | type PkgSend struct { 77 | ip string 78 | port int 79 | flag PkgFlag 80 | SourceIP string 81 | } 82 | 83 | // PkgResult contains the results of sending TCP packages 84 | type PkgResult struct { 85 | ip string 86 | port int 87 | } 88 | 89 | var ( 90 | newScannerCallback func(s *Scanner) error 91 | setupHandlerCallback func(s *Scanner, interfaceName string) error 92 | tcpReadWorkerPCAPCallback func(s *Scanner) 93 | cleanupHandlersCallback func(s *Scanner) 94 | ) 95 | 96 | const ( 97 | maxRetries = 10 98 | sendDelayMsec = 10 99 | chanSize = 1000 100 | packetSendSize = 2500 101 | snapLength = 65536 102 | readTimeout = 1500 103 | ) 104 | 105 | // NewScanner creates a new full port scanner that scans all ports using SYN packets. 106 | func NewScanner() (*Scanner, error) { 107 | rand.Seed(time.Now().UnixNano()) 108 | 109 | iprang, err := ipranger.New() 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | var nPolicyOptions networkpolicy.Options 115 | //黑名单 116 | //nPolicyOptions.DenyList = append(nPolicyOptions.DenyList, options.ExcludedIps...) 117 | nPolicy, err := networkpolicy.New(nPolicyOptions) 118 | if err != nil { 119 | return nil, err 120 | } 121 | iprang.Np = nPolicy 122 | 123 | scanner := &Scanner{ 124 | serializeOptions: gopacket.SerializeOptions{ 125 | FixLengths: true, 126 | ComputeChecksums: true, 127 | }, 128 | 129 | tcpSequencer: newTCPSequencer(), 130 | ipRanger: iprang, 131 | HandlerOpen: func(ip string, port int) {}, 132 | 133 | limitCount: 0, 134 | 135 | //timeout: time.Second * 3, 136 | //retries: 3, 137 | //rate: 1024, 138 | } 139 | 140 | scanner.SetRate(1024) 141 | 142 | if newScannerCallback != nil { 143 | if err := newScannerCallback(scanner); err != nil { 144 | return nil, err 145 | } 146 | } 147 | //初始化 148 | err = scanner.init() 149 | if err != nil { 150 | return nil, err 151 | } 152 | return scanner, nil 153 | } 154 | 155 | // Done the scanner and terminate all workers 156 | func (s *Scanner) Done() { 157 | s.cleanupHandlers() 158 | s.tcpPacketListener.Close() 159 | s.done = true 160 | } 161 | 162 | // Add the scanner and add ip filter in ipRanger 163 | func (s *Scanner) Add(host string) error { 164 | return s.ipRanger.Add(host) 165 | } 166 | 167 | // Count of scanner send syn packet 168 | func (s *Scanner) Count() uint64 { 169 | return s.limitCount 170 | } 171 | 172 | // startWorkers of the scanner 173 | func (s *Scanner) startWorkers() { 174 | go s.tcpReadWorker() 175 | go s.tcpReadWorkerPCAP() 176 | go s.tcpWriteWorker() 177 | go s.tcpResultWorker() 178 | } 179 | 180 | // tcpWriteWorker that sends out TCP packets 181 | func (s *Scanner) tcpWriteWorker() { 182 | for pkg := range s.tcpPacketSend { 183 | s.sendAsyncPkg(pkg.ip, pkg.port, pkg.flag) 184 | } 185 | } 186 | 187 | //sendAsyncPkg sends a single packet to a port 188 | func (s *Scanner) sendAsyncPkg(ip string, port int, pkgFlag PkgFlag) { 189 | // Construct all the network layers we need. 190 | ip4 := layers.IPv4{ 191 | SrcIP: s.sourceIP, 192 | DstIP: net.ParseIP(ip), 193 | Version: 4, 194 | TTL: 255, 195 | Protocol: layers.IPProtocolTCP, 196 | } 197 | tcpOption := layers.TCPOption{ 198 | OptionType: layers.TCPOptionKindMSS, 199 | OptionLength: 4, 200 | OptionData: []byte{0x05, 0xB4}, 201 | } 202 | tcp := layers.TCP{ 203 | SrcPort: layers.TCPPort(s.listenPort), 204 | DstPort: layers.TCPPort(port), 205 | Window: 1024, 206 | Seq: s.tcpSequencer.Next(), 207 | Options: []layers.TCPOption{tcpOption}, 208 | } 209 | 210 | if pkgFlag == SYN { 211 | tcp.SYN = true 212 | } else if pkgFlag == ACK { 213 | tcp.ACK = true 214 | } 215 | 216 | err := tcp.SetNetworkLayerForChecksum(&ip4) 217 | if err != nil { 218 | logger.Printf("Can not set network layer for %s:%d port: %s\n", ip, port, err) 219 | } else { 220 | err = s.send(ip, s.tcpPacketListener, &tcp) 221 | if err != nil { 222 | logger.Printf("Can not send packet to %s:%d port: %s\n", ip, port, err) 223 | } 224 | } 225 | } 226 | 227 | // send sends the given layers as a single packet on the network. 228 | func (s *Scanner) send(destIP string, conn net.PacketConn, l ...gopacket.SerializableLayer) error { 229 | buf := gopacket.NewSerializeBuffer() 230 | if err := gopacket.SerializeLayers(buf, s.serializeOptions, l...); err != nil { 231 | return err 232 | } 233 | 234 | var ( 235 | retries int 236 | err error 237 | ) 238 | 239 | send: 240 | if retries >= maxRetries { 241 | return err 242 | } 243 | _, err = conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: net.ParseIP(destIP)}) 244 | if err != nil { 245 | retries++ 246 | // introduce a small delay to allow the network interface to flush the queue 247 | time.Sleep(time.Duration(sendDelayMsec) * time.Millisecond) 248 | goto send 249 | } 250 | return err 251 | } 252 | 253 | // tcpReadWorker reads and parse incoming TCP packets 254 | func (s *Scanner) tcpReadWorker() { 255 | defer s.tcpPacketListener.Close() 256 | data := make([]byte, 4096) 257 | for { 258 | if s.done == true { 259 | break 260 | } 261 | // nolint:errcheck // just empty the buffer 262 | s.tcpPacketListener.ReadFrom(data) 263 | } 264 | } 265 | 266 | // tcpReadWorkerPCAP reads and parse incoming TCP packets with pcap 267 | func (s *Scanner) tcpReadWorkerPCAP() { 268 | if tcpReadWorkerPCAPCallback != nil { 269 | tcpReadWorkerPCAPCallback(s) 270 | } 271 | } 272 | 273 | // tcpResultWorker handles probes and scan results 274 | // 输出处理函数 275 | func (s *Scanner) tcpResultWorker() { 276 | for ip := range s.tcpResultChan { 277 | s.HandlerOpen(ip.ip, ip.port) 278 | } 279 | } 280 | 281 | // SendSYN outgoing TCP packets 282 | func (s *Scanner) SendSYN(ip string, port int, pkgtype PkgFlag) { 283 | err := s.limit.Wait(context.Background()) 284 | if err != nil { 285 | panic(err) 286 | } 287 | s.tcpPacketSend <- &PkgSend{ip, port, pkgtype, ""} 288 | atomic.AddUint64(&s.limitCount, 1) 289 | } 290 | 291 | func (s *Scanner) init() error { 292 | //初始化发包源IP及网卡 293 | err := s.initSource(DefaultExternalTargetForGetSource) 294 | if err != nil { 295 | return err 296 | } 297 | //初始化监听网卡流量 298 | err = s.listen() 299 | if err != nil { 300 | return err 301 | } 302 | //启动相关函数 303 | s.startWorkers() 304 | return nil 305 | } 306 | 307 | // listen to listen on all interfaces 308 | func (s *Scanner) listen() error { 309 | if s.networkInterface != nil { 310 | return s.listenInterface(s.networkInterface.Name) 311 | } 312 | itfs, err := net.Interfaces() 313 | if err != nil { 314 | return err 315 | } 316 | for _, itf := range itfs { 317 | if itf.Flags&net.FlagUp == 0 { 318 | continue // interface down 319 | } 320 | if err := s.listenInterface(itf.Name); err != nil { 321 | logger.Printf("Error on interface %s: %s", itf.Name, err) 322 | } 323 | } 324 | 325 | return nil 326 | } 327 | 328 | // listenInterface to listen on the specified interface 329 | func (s *Scanner) listenInterface(interfaceName string) error { 330 | if setupHandlerCallback != nil { 331 | return setupHandlerCallback(s, interfaceName) 332 | } 333 | 334 | return nil 335 | } 336 | 337 | // cleanupHandlers for all interfaces 338 | func (s *Scanner) cleanupHandlers() { 339 | if cleanupHandlersCallback != nil { 340 | cleanupHandlersCallback(s) 341 | } 342 | } 343 | 344 | var DefaultExternalTargetForGetSource = "8.8.8.8" 345 | 346 | // initSource automatically with ip and interface 347 | func (s *Scanner) initSource(ip string) error { 348 | var err error 349 | s.sourceIP, s.networkInterface, err = getSrcParameters(ip) 350 | if err != nil { 351 | return err 352 | } 353 | 354 | return nil 355 | } 356 | 357 | func (s *Scanner) SetRate(i int) { 358 | s.limit = rate.NewLimiter(rate.Every(time.Second/time.Duration(i)), 1) 359 | } 360 | 361 | // getSrcParameters gets the network parameters from the destination ip 362 | func getSrcParameters(destIP string) (srcIP net.IP, networkInterface *net.Interface, err error) { 363 | srcIP, err = getSourceIP(net.ParseIP(destIP)) 364 | if err != nil { 365 | return 366 | } 367 | networkInterface, err = getInterfaceFromIP(srcIP) 368 | if err != nil { 369 | return 370 | } 371 | return 372 | } 373 | 374 | // getSourceIP gets the local ip based on our destination ip 375 | func getSourceIP(dstip net.IP) (net.IP, error) { 376 | serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") 377 | if err != nil { 378 | return nil, err 379 | } 380 | 381 | con, dialUpErr := net.DialUDP("udp", nil, serverAddr) 382 | if dialUpErr != nil { 383 | return nil, dialUpErr 384 | } 385 | 386 | defer con.Close() 387 | if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { 388 | return udpaddr.IP, nil 389 | } 390 | 391 | return nil, nil 392 | } 393 | 394 | // getInterfaceFromIP gets the name of the network interface from local ip address 395 | func getInterfaceFromIP(ip net.IP) (*net.Interface, error) { 396 | address := ip.String() 397 | 398 | interfaces, err := net.Interfaces() 399 | if err != nil { 400 | return nil, err 401 | } 402 | 403 | for _, i := range interfaces { 404 | byNameInterface, err := net.InterfaceByName(i.Name) 405 | if err != nil { 406 | return nil, err 407 | } 408 | 409 | addresses, err := byNameInterface.Addrs() 410 | if err != nil { 411 | return nil, err 412 | } 413 | 414 | for _, v := range addresses { 415 | // Check if the IP for the current interface is our 416 | // source IP. If yes, return the interface 417 | if strings.HasPrefix(v.String(), address+"/") { 418 | return byNameInterface, nil 419 | } 420 | } 421 | } 422 | 423 | return nil, fmt.Errorf("no interface found for ip %s", address) 424 | } 425 | 426 | // ICMPReadWorker reads packets from the network layer 427 | //func (s *Scanner) ICMPReadWorker() { 428 | // defer s.icmpPacketListener.Close() 429 | // data := make([]byte, 1500) 430 | // for { 431 | // if s.State == done { 432 | // break 433 | // } 434 | // n, addr, err := s.icmpPacketListener.ReadFrom(data) 435 | // if err != nil { 436 | // continue 437 | // } 438 | // 439 | // if s.State == Guard { 440 | // continue 441 | // } 442 | // 443 | // rm, err := icmp.ParseMessage(ProtocolICMP, data[:n]) 444 | // if err != nil { 445 | // continue 446 | // } 447 | // 448 | // switch rm.Type { 449 | // case ipv4.ICMPTypeEchoReply, ipv4.ICMPTypeTimestamp: 450 | // s.icmpChan <- &PkgResult{ip: addr.String()} 451 | // } 452 | // } 453 | //} 454 | 455 | //// ConnectPort a single host and port 456 | //func (s *Scanner) ConnectPort(host string, port int, timeout time.Duration) (bool, error) { 457 | // hostport := net.JoinHostPort(host, fmt.Sprint(port)) 458 | // var ( 459 | // err error 460 | // conn net.Conn 461 | // ) 462 | // if s.proxyDialer != nil { 463 | // conn, err = s.proxyDialer.Dial("tcp", hostport) 464 | // if err != nil { 465 | // return false, err 466 | // } 467 | // } else { 468 | // conn, err = net.DialTimeout("tcp", hostport, timeout) 469 | // } 470 | // if err != nil { 471 | // return false, err 472 | // } 473 | // conn.Close() 474 | // return true, err 475 | //} 476 | 477 | // ScanSyn a target ip 478 | //func (s *Scanner) ScanSyn(ip string) { 479 | // for _, port := range s.Ports { 480 | // s.SendSYN(ip, port, SYN) 481 | // } 482 | //} 483 | 484 | // ACKPort sends an ACK packet to a port 485 | //func (s *Scanner) ACKPort(dstIP string, port int, timeout time.Duration) (bool, error) { 486 | // conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0") 487 | // if err != nil { 488 | // return false, err 489 | // } 490 | // defer conn.Close() 491 | // 492 | // rawPort, err := freeport.GetFreePort() 493 | // if err != nil { 494 | // return false, err 495 | // } 496 | // 497 | // // Construct all the network layers we need. 498 | // ip4 := layers.IPv4{ 499 | // SrcIP: s.sourceIP, 500 | // DstIP: net.ParseIP(dstIP), 501 | // Version: 4, 502 | // TTL: 255, 503 | // Protocol: layers.IPProtocolTCP, 504 | // } 505 | // tcpOption := layers.TCPOption{ 506 | // OptionType: layers.TCPOptionKindMSS, 507 | // OptionLength: 4, 508 | // OptionData: []byte{0x12, 0x34}, 509 | // } 510 | // 511 | // tcp := layers.TCP{ 512 | // SrcPort: layers.TCPPort(rawPort), 513 | // DstPort: layers.TCPPort(port), 514 | // ACK: true, 515 | // Window: 1024, 516 | // Seq: s.tcpSequencer.Next(), 517 | // Options: []layers.TCPOption{tcpOption}, 518 | // } 519 | // 520 | // err = tcp.SetNetworkLayerForChecksum(&ip4) 521 | // if err != nil { 522 | // return false, err 523 | // } 524 | // 525 | // err = s.send(dstIP, conn, &tcp) 526 | // if err != nil { 527 | // return false, err 528 | // } 529 | // 530 | // data := make([]byte, 4096) 531 | // for { 532 | // n, addr, err := conn.ReadFrom(data) 533 | // if err != nil { 534 | // break 535 | // } 536 | // 537 | // // not matching ip 538 | // if addr.String() != dstIP { 539 | // logger.Printf("Discarding TCP packet from non target ip %s for %s\n", dstIP, addr.String()) 540 | // continue 541 | // } 542 | // 543 | // packet := gopacket.NewPacket(data[:n], layers.LayerTypeTCP, gopacket.Default) 544 | // if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { 545 | // tcp, ok := tcpLayer.(*layers.TCP) 546 | // if !ok { 547 | // continue 548 | // } 549 | // // We consider only incoming packets 550 | // if tcp.DstPort != layers.TCPPort(rawPort) { 551 | // logger.Printf("Discarding TCP packet from %s:%d not matching %s:%d port\n", addr.String(), tcp.DstPort, dstIP, rawPort) 552 | // continue 553 | // } else if tcp.RST { 554 | // logger.Printf("Accepting RST packet from %s:%d\n", addr.String(), tcp.DstPort) 555 | // return true, nil 556 | // } 557 | // } 558 | // } 559 | // 560 | // return false, nil 561 | //} 562 | 563 | // EnqueueICMP outgoing ICMP packets 564 | //func (s *Scanner) EnqueueICMP(ip string, pkgtype PkgFlag) { 565 | // s.icmpPacketSend <- &PkgSend{ 566 | // ip: ip, 567 | // flag: pkgtype, 568 | // } 569 | //} 570 | 571 | // ICMPWriteWorker writes packet to the network layer 572 | //func (s *Scanner) ICMPWriteWorker() { 573 | // for pkg := range s.icmpPacketSend { 574 | // if pkg.flag == ICMPECHOREQUEST && pingIcmpEchoRequestAsyncCallback != nil { 575 | // pingIcmpEchoRequestAsyncCallback(s, pkg.ip) 576 | // } else if pkg.flag == ICMPTIMESTAMPREQUEST && pingIcmpTimestampRequestAsyncCallback != nil { 577 | // pingIcmpTimestampRequestAsyncCallback(s, pkg.ip) 578 | // } 579 | // } 580 | //} 581 | -------------------------------------------------------------------------------- /scan_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | 3 | package gomasscan 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "net" 9 | "sync" 10 | "time" 11 | 12 | "github.com/google/gopacket" 13 | "github.com/google/gopacket/layers" 14 | "github.com/google/gopacket/pcap" 15 | "github.com/phayes/freeport" 16 | ) 17 | 18 | func init() { 19 | newScannerCallback = newScannerUnix 20 | setupHandlerCallback = setupHandlerUnix 21 | tcpReadWorkerPCAPCallback = tcpReadWorkerPCAPUnix 22 | cleanupHandlersCallback = cleanupHandlersUnix 23 | } 24 | 25 | type Handlers struct { 26 | Active []*pcap.Handle 27 | Inactive []*pcap.InactiveHandle 28 | } 29 | 30 | func newScannerUnix(scanner *Scanner) error { 31 | rawPort, err := freeport.GetFreePort() 32 | if err != nil { 33 | return err 34 | } 35 | scanner.listenPort = rawPort 36 | 37 | tcpConn, err := net.ListenIP("ip4:tcp", &net.IPAddr{IP: net.ParseIP(fmt.Sprintf("0.0.0.0:%d", rawPort))}) 38 | if err != nil { 39 | return err 40 | } 41 | scanner.tcpPacketListener = tcpConn 42 | 43 | var handlers Handlers 44 | scanner.handlers = handlers 45 | 46 | scanner.tcpResultChan = make(chan *PkgResult, chanSize) 47 | scanner.tcpPacketSend = make(chan *PkgSend, packetSendSize) 48 | return nil 49 | } 50 | 51 | func setupHandlerUnix(s *Scanner, interfaceName string) error { 52 | inactive, err := pcap.NewInactiveHandle(interfaceName) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | err = inactive.SetSnapLen(snapLength) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | readTimeout := time.Duration(readTimeout) * time.Millisecond 63 | if err = inactive.SetTimeout(readTimeout); err != nil { 64 | s.cleanupHandlers() 65 | return err 66 | } 67 | err = inactive.SetImmediateMode(true) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | handlers := s.handlers 73 | handlers.Inactive = append(handlers.Inactive, inactive) 74 | 75 | handle, err := inactive.Activate() 76 | 77 | if err != nil { 78 | s.cleanupHandlers() 79 | return err 80 | } 81 | 82 | handlers.Active = append(handlers.Active, handle) 83 | 84 | // Strict BPF filter 85 | // + Packets coming from target ip 86 | // + Destination port equals to sender socket source port 87 | err = handle.SetBPFFilter(fmt.Sprintf("tcp and dst port %d and tcp[13]=18", s.listenPort)) 88 | 89 | if err != nil { 90 | s.cleanupHandlers() 91 | return err 92 | } 93 | 94 | s.handlers = handlers 95 | 96 | return nil 97 | } 98 | 99 | func tcpReadWorkerPCAPUnix(s *Scanner) { 100 | defer s.cleanupHandlers() 101 | 102 | var wgread sync.WaitGroup 103 | 104 | handlers := s.handlers 105 | 106 | for _, handler := range handlers.Active { 107 | wgread.Add(1) 108 | go func(handler *pcap.Handle) { 109 | defer wgread.Done() 110 | 111 | var ( 112 | eth layers.Ethernet 113 | ip4 layers.IPv4 114 | tcp layers.TCP 115 | ) 116 | 117 | // Interfaces with MAC (Physical + Virtualized) 118 | parserMac := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &tcp) 119 | // Interfaces without MAC (TUN/TAP) 120 | parserNoMac := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &tcp) 121 | 122 | var parsers []*gopacket.DecodingLayerParser 123 | parsers = append(parsers, parserMac, parserNoMac) 124 | 125 | var decoded []gopacket.LayerType 126 | 127 | for { 128 | data, _, err := handler.ReadPacketData() 129 | if err != nil { 130 | if err == io.EOF { 131 | break 132 | } 133 | continue 134 | } 135 | for _, parser := range parsers { 136 | if err := parser.DecodeLayers(data, &decoded); err != nil { 137 | continue 138 | } 139 | for _, layerType := range decoded { 140 | if layerType != layers.LayerTypeTCP { 141 | continue 142 | } 143 | if s.ipRanger.Contains(ip4.SrcIP.String()) == false { 144 | //gologger.Debug().Msgf("Discarding TCP packet from non target ip %s\n", ip4.SrcIP.String()) 145 | continue 146 | } 147 | if tcp.DstPort != layers.TCPPort(s.listenPort) { 148 | continue 149 | } 150 | // We consider only incoming packets 151 | if tcp.ACK == false || tcp.SYN == false { 152 | continue 153 | } 154 | s.tcpResultChan <- &PkgResult{ip: ip4.SrcIP.String(), port: int(tcp.SrcPort)} 155 | } 156 | } 157 | } 158 | }(handler) 159 | } 160 | 161 | wgread.Wait() 162 | } 163 | 164 | // cleanupHandlers for all interfaces 165 | func cleanupHandlersUnix(s *Scanner) { 166 | handler := s.handlers 167 | for _, handler := range handler.Active { 168 | handler.Close() 169 | } 170 | for _, inactiveHandler := range handler.Inactive { 171 | inactiveHandler.CleanUp() 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tcpsequencer.go: -------------------------------------------------------------------------------- 1 | package gomasscan 2 | 3 | import ( 4 | "math" 5 | "sync/atomic" 6 | ) 7 | 8 | // TCPSequencer generates linear TCP sequence numbers that wrap 9 | // around after reaching their maximum value. 10 | // 11 | // According to specs, this is the correct way to approach TCP sequence 12 | // number since linearity will be guaranteed by the wrapping around to initial 0. 13 | type TCPSequencer struct { 14 | current uint32 15 | } 16 | 17 | // newTCPSequencer creates a new linear tcp sequenc enumber generator 18 | func newTCPSequencer() *TCPSequencer { 19 | // Start the sequence with math.MaxUint32, which will then wrap around 20 | // when incremented starting the sequence with 0 as desired. 21 | return &TCPSequencer{current: math.MaxUint32} 22 | } 23 | 24 | // Next returns the next number in the sequence of tcp sequence numbers 25 | func (t *TCPSequencer) Next() uint32 { 26 | value := atomic.AddUint32(&t.current, 1) 27 | return value 28 | } 29 | --------------------------------------------------------------------------------