add HTTPPath, fix Host
[gscan_quic.git] / gscan.go
blobc2b9a414c24cf49e1c9535f17ab4f378eab04061
1 package main
3 import (
4 "bytes"
5 "flag"
6 "fmt"
7 "io/ioutil"
8 "log"
9 "math/rand"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "runtime"
14 "sort"
15 "strings"
16 "time"
19 type ScanConfig struct {
20 ScanCountPerIP int
21 ServerName []string
22 HTTPVerifyHosts []string
23 VerifyCommonName string
24 HTTPPath string
25 ValidStatusCode int
26 HandshakeTimeout time.Duration
27 ScanMinRTT time.Duration
28 ScanMaxRTT time.Duration
29 RecordLimit int
30 InputFile string
31 OutputFile string
32 OutputSeparator string
33 Level int
36 type GScanConfig struct {
37 ScanWorker int
38 VerifyPing bool
39 ScanMinPingRTT time.Duration
40 ScanMaxPingRTT time.Duration
41 DisablePause bool
42 EnableBackup bool
43 BackupDir string
45 ScanMode string
46 Ping ScanConfig
47 Quic ScanConfig
48 Tls ScanConfig
49 Sni ScanConfig
52 func init() {
53 rand.Seed(time.Now().Unix())
55 log.SetFlags(log.LstdFlags | log.Lshortfile)
58 func initConfig(cfgfile, execFolder string) *GScanConfig {
59 if strings.HasPrefix(cfgfile, "./") {
60 cfgfile = filepath.Join(execFolder, cfgfile)
63 gcfg := new(GScanConfig)
64 if err := readJsonConfig(cfgfile, gcfg); err != nil {
65 log.Panicln(err)
68 if gcfg.EnableBackup {
69 if strings.HasPrefix(gcfg.BackupDir, "./") {
70 gcfg.BackupDir = filepath.Join(execFolder, gcfg.BackupDir)
72 if _, err := os.Stat(gcfg.BackupDir); os.IsNotExist(err) {
73 if err := os.MkdirAll(gcfg.BackupDir, 0755); err != nil {
74 log.Println(err)
79 gcfg.ScanMode = strings.ToLower(gcfg.ScanMode)
80 if gcfg.ScanMode == "ping" {
81 gcfg.VerifyPing = false
84 gcfg.ScanMinPingRTT = gcfg.ScanMinPingRTT * time.Millisecond
85 gcfg.ScanMaxPingRTT = gcfg.ScanMaxPingRTT * time.Millisecond
87 cfgs := []*ScanConfig{&gcfg.Quic, &gcfg.Tls, &gcfg.Sni, &gcfg.Ping}
88 for _, c := range cfgs {
89 if strings.HasPrefix(c.InputFile, "./") {
90 c.InputFile = filepath.Join(execFolder, c.InputFile)
91 } else {
92 c.InputFile, _ = filepath.Abs(c.InputFile)
94 if strings.HasPrefix(c.OutputFile, "./") {
95 c.OutputFile = filepath.Join(execFolder, c.OutputFile)
96 } else {
97 c.OutputFile, _ = filepath.Abs(c.OutputFile)
99 if _, err := os.Stat(c.InputFile); os.IsNotExist(err) {
100 os.OpenFile(c.InputFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
103 c.ScanMinRTT *= time.Millisecond
104 c.ScanMaxRTT *= time.Millisecond
105 c.HandshakeTimeout *= time.Millisecond
107 return gcfg
110 func main() {
111 var disablePause bool
112 defer func() {
113 if r := recover(); r != nil {
114 fmt.Println("panic:", r)
116 fmt.Println()
117 if !disablePause {
118 if runtime.GOOS == "windows" {
119 cmd := exec.Command("cmd", "/C", "pause")
120 cmd.Stdout = os.Stdout
121 cmd.Stdin = os.Stdin
122 // 改为 start, 程序可以正常退出, 这样一些程序监视工具可以正常测到程序结束了
123 cmd.Start()
124 } else {
125 fmt.Println("Press [Enter] to exit...")
126 fmt.Scanln()
131 var cfgfile string
132 flag.StringVar(&cfgfile, "Config File", "./config.json", "Config file, json format")
133 flag.Parse()
135 var execFolder = "./"
136 if e, err := os.Executable(); err != nil {
137 log.Panicln(err)
138 } else {
139 execFolder = filepath.Dir(e)
141 // execFolder = "./"
143 gcfg := initConfig(cfgfile, execFolder)
144 disablePause = gcfg.DisablePause
146 var cfg *ScanConfig
147 scanMode := gcfg.ScanMode
148 switch scanMode {
149 case "quic":
150 cfg = &gcfg.Quic
151 testIPFunc = testQuic
152 case "tls":
153 cfg = &gcfg.Tls
154 testIPFunc = testTls
155 case "sni":
156 cfg = &gcfg.Sni
157 testIPFunc = testSni
158 case "ping":
159 cfg = &gcfg.Ping
160 testIPFunc = testPing
161 case "socks5":
162 // testIPFunc = testSocks5
163 default:
166 iprangeFile := cfg.InputFile
167 if _, err := os.Stat(iprangeFile); os.IsNotExist(err) {
168 log.Panicln(err)
171 srs := &ScanRecords{}
173 log.Printf("Start loading IP Range file: %s\n", iprangeFile)
174 ipqueue, err := parseIPRangeFile(iprangeFile)
175 if err != nil {
176 log.Panicln(err)
179 log.Printf("Start scanning available IP\n")
180 startTime := time.Now()
181 StartScan(srs, gcfg, cfg, ipqueue)
182 log.Printf("Scanned %d IP in %s, found %d records\n", srs.ScanCount(), time.Since(startTime).String(), len(srs.records))
184 if records := srs.records; len(records) > 0 {
185 sort.Slice(records, func(i, j int) bool {
186 return records[i].RTT < records[j].RTT
188 a := make([]string, len(records))
189 for i, r := range records {
190 a[i] = r.IP
192 b := new(bytes.Buffer)
193 if cfg.OutputSeparator == "gop" {
194 out := strings.Join(a, `", "`)
195 b.WriteString(`"` + out + `",`)
196 } else {
197 out := strings.Join(a, cfg.OutputSeparator)
198 b.WriteString(out)
201 if err := ioutil.WriteFile(cfg.OutputFile, b.Bytes(), 0644); err != nil {
202 log.Printf("Failed to write output file:%s for reason:%v\n", cfg.OutputFile, err)
203 } else {
204 log.Printf("All results writed to %s\n", cfg.OutputFile)
206 if gcfg.EnableBackup {
207 filename := fmt.Sprintf("%s_%s_lv%d.txt", scanMode, time.Now().Format("20060102_150405"), cfg.Level)
208 bakfilename := filepath.Join(gcfg.BackupDir, filename)
209 if err := ioutil.WriteFile(bakfilename, b.Bytes(), 0644); err != nil {
210 log.Printf("Failed to write output file:%s for reason:%v\n", bakfilename, err)
211 } else {
212 log.Printf("All results writed to %s\n", bakfilename)