├── .DS_Store ├── image ├── 1.png ├── 2.png ├── 3.png └── 4.png ├── function ├── .DS_Store ├── logger │ ├── level.go │ └── log.go ├── gather │ ├── native.go │ ├── fofa.go │ └── quake.go ├── subassembly │ ├── directory.go │ ├── excel.go │ ├── readfiletxt.go │ └── means.go ├── balance │ └── load_balance.go ├── config.go ├── proxyService │ └── CoreAgent.go ├── command │ └── cmd.go └── inspect │ └── checkAlive.go ├── .idea ├── .gitignore ├── vcs.xml ├── modules.xml └── SmallProxyPool-main.iml ├── config.ini ├── go.mod ├── README.md ├── main.go └── go.sum /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/.DS_Store -------------------------------------------------------------------------------- /image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/image/1.png -------------------------------------------------------------------------------- /image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/image/2.png -------------------------------------------------------------------------------- /image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/image/3.png -------------------------------------------------------------------------------- /image/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/image/4.png -------------------------------------------------------------------------------- /function/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh1yan/Sppmagic/HEAD/function/.DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /function/logger/level.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | type Level int 4 | 5 | const ( 6 | LevelFatal Level = iota // 值 0 7 | LevelError // 值 1 8 | LevelInfo // 值 2 9 | LevelWarning // 值 3 10 | LevelDebug // 值 4 11 | LevelVerbose // 值 5 12 | ) 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/SmallProxyPool-main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [global] 2 | bind_ip = 127.0.0.1 3 | bind_port = 1080 4 | thread = 1000 5 | 6 | [quake] 7 | quaketoken = "" 8 | rule = service:"socks5" and response:"Accepted Auth Method: 0x0" and country:CN 9 | size = 500 10 | # permissions 为账号的权限,需要手工填写,0为注册用户,1为高级会员/终身会员 11 | permissions = 0 12 | 13 | [fofa] 14 | email = 15 | key = 16 | rule = 'protocol=="socks5" && "Version:5 Method:No Authentication(0x00)" && country="CN"' 17 | 18 | [rule] 19 | # 是否开启轮询策略,也就是每请求一次就换一次代理,不开启的话就是固定速度最快的代理ip 20 | polling= true -------------------------------------------------------------------------------- /function/gather/native.go: -------------------------------------------------------------------------------- 1 | package gather 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "main/function" 7 | log "main/function/logger" 8 | "main/function/subassembly" 9 | "os" 10 | ) 11 | 12 | // GetNativeSocks5Data 获取本地代理地址文件数据 13 | func GetNativeSocks5Data() { 14 | 15 | log.Debug(fmt.Sprint("当前传入文件路径为:", function.Txtfilepath)) // debug 查看下是否有路径参数传过来 16 | rst := subassembly.FindTextIpPort(function.Txtfilepath) 17 | if len(rst) == 0 { 18 | if len(function.Txtfilepath) == 0 { 19 | log.Error("未输入 -f 参数的值,请按照flag提示进行输入") 20 | flag.PrintDefaults() 21 | os.Exit(0) 22 | } 23 | log.Error(fmt.Sprintf("当前导入的路径文件内容为空,请在文本里填入socks5代理地址:%v", function.Txtfilepath)) 24 | os.Exit(0) 25 | } 26 | function.Address = rst 27 | } 28 | -------------------------------------------------------------------------------- /function/subassembly/directory.go: -------------------------------------------------------------------------------- 1 | package subassembly 2 | 3 | import ( 4 | "fmt" 5 | "main/function/logger" 6 | "os" 7 | ) 8 | 9 | // MkdirResult 创建报告存放目录 10 | func MkdirResult() { 11 | // 获取当前目录地址 12 | dir, err := os.Getwd() 13 | if err != nil { 14 | logger.DebugError(err) 15 | } 16 | logger.Debug(fmt.Sprintf("当前路径:%s", dir)) 17 | Dir_mk(dir + "/result/") 18 | logger.Debug("当前目录下创建 /result/ 目录成功") 19 | 20 | } 21 | 22 | // dir_mk 判断目录是否存在,若不存在则进行创建 23 | func Dir_mk(path string) { 24 | // 判断目录是否存在 25 | if _, err := os.Stat(path); os.IsNotExist(err) { 26 | err = os.Mkdir(path, 0777) 27 | if err != nil { 28 | logger.DebugError(err) 29 | } 30 | logger.Debug(fmt.Sprintf("当前以创建好该目录:%s", path)) 31 | return 32 | } else { 33 | logger.Debug("当前目录为存在状态,无续进行创建") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /function/balance/load_balance.go: -------------------------------------------------------------------------------- 1 | package balance 2 | 3 | import "errors" 4 | 5 | // RoundRobinBalance 数组循环取值结构体,应用于轮询策略,也就是每请求一次就换一次代理 6 | type RoundRobinBalance struct { 7 | curIndex int // 索引 8 | rss []string // 订阅 9 | } 10 | 11 | // Set 将字符串数组放入到循环平衡结构体的订阅数组中 12 | func (r *RoundRobinBalance) Set(s []string) error { 13 | if len(s) == 0 { 14 | return errors.New("input []string") 15 | } 16 | 17 | r.rss = s 18 | return nil 19 | } 20 | 21 | // next 通过取余数操作来循环遍历整个数组中的地址信息 22 | func (r *RoundRobinBalance) next() string { 23 | if len(r.rss) == 0 { 24 | return "" 25 | } 26 | lens := len(r.rss) // 将结构体订阅数组中的长度整理成数字 27 | if r.curIndex >= lens { // 如果结构体索引大于或等于数组长度,则将索引值替换为空 28 | r.curIndex = 0 29 | } 30 | 31 | curAddr := r.rss[r.curIndex] // 通过索引进行获取地址信息 32 | 33 | // 这行代码的目的是将 r.curIndex 增加1,并在达到数组或切片的长度(lens)时回绕到0。这是通过取余数操作来实现的,因此无论 r.curIndex 增加到多少,都会保持在 0 到 lens-1 的范围内。 34 | r.curIndex = (r.curIndex + 1) % lens 35 | return curAddr 36 | } 37 | 38 | // Get 执行 r.next() 函数,来获得地址信息 39 | func (r *RoundRobinBalance) Get() string { 40 | return r.next() 41 | } 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/panjf2000/ants/v2 v2.7.1 7 | github.com/valyala/fastjson v1.6.4 8 | ) 9 | 10 | require github.com/go-ini/ini v1.67.0 11 | 12 | require ( 13 | github.com/gookit/color v1.5.4 14 | github.com/tealeg/xlsx/v3 v3.3.2 15 | github.com/tidwall/gjson v1.16.0 16 | golang.org/x/text v0.6.0 17 | ) 18 | 19 | require ( 20 | github.com/frankban/quicktest v1.14.6 // indirect 21 | github.com/google/btree v1.0.0 // indirect 22 | github.com/google/go-cmp v0.5.9 // indirect 23 | github.com/kr/pretty v0.3.1 // indirect 24 | github.com/kr/text v0.2.0 // indirect 25 | github.com/peterbourgon/diskv/v3 v3.0.1 // indirect 26 | github.com/rogpeppe/fastuuid v1.2.0 // indirect 27 | github.com/rogpeppe/go-internal v1.9.0 // indirect 28 | github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect 29 | github.com/tidwall/match v1.1.1 // indirect 30 | github.com/tidwall/pretty v1.2.0 // indirect 31 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect 32 | golang.org/x/sys v0.10.0 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /function/subassembly/excel.go: -------------------------------------------------------------------------------- 1 | package subassembly 2 | 3 | import ( 4 | "fmt" 5 | "github.com/tealeg/xlsx/v3" 6 | log "main/function/logger" 7 | "time" 8 | ) 9 | 10 | // MakeApiResultDown 用于对API结果下载到Excel里面 11 | func MakeApiResultDown(name string, data []string) { 12 | // 指定要写入的 Excel 文件名 13 | excelName := name 14 | // 要写入的字符串数组 15 | datas := data 16 | day := time.Now().Format("2006.1.2") 17 | fileName := fmt.Sprintf("./result/%v-API测绘结果下载-%v.xlsx", excelName, day) 18 | 19 | // 打开 Excel 文件,如果文件不存在则创建新文件 20 | file, err := xlsx.OpenFile(fileName) 21 | if err != nil { 22 | file = xlsx.NewFile() 23 | } 24 | 25 | // 获取或创建一个工作表 26 | sheet, found := file.Sheet["Sheet1"] 27 | if !found { 28 | sheet, err = file.AddSheet("Sheet1") 29 | if err != nil { 30 | log.Debug(fmt.Sprintf("创建工作表失败: %s\n", err)) 31 | return 32 | } 33 | } 34 | 35 | // 创建一行,并将数据写入单元格 36 | for _, value := range datas { 37 | row := sheet.AddRow() 38 | cell := row.AddCell() 39 | cell.Value = value 40 | } 41 | 42 | // 保存 Excel 文件 43 | err = file.Save(fileName) 44 | if err != nil { 45 | log.Debug(fmt.Sprintf("保存 Excel 文件失败: %s", err)) 46 | return 47 | } 48 | 49 | log.Debug(fmt.Sprintf("Excel 文件已追加保存:%s", fileName)) 50 | } 51 | -------------------------------------------------------------------------------- /function/subassembly/readfiletxt.go: -------------------------------------------------------------------------------- 1 | package subassembly 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | log "main/function/logger" 7 | "os" 8 | ) 9 | 10 | // FindTextIpPort 获取本地text中 ip:port 地址列表 11 | func FindTextIpPort(filepath string) []string { 12 | filePath := filepath // 替换为实际的txt文件路径 13 | 14 | // 打开文件 15 | file, err := os.Open(filePath) 16 | log.Debug(fmt.Sprint("判断当前是否读取到数据验证:", file)) 17 | if err != nil { 18 | log.DebugError(err) 19 | return []string{} 20 | } 21 | defer file.Close() 22 | 23 | ipports := []string{} // 用于存储IP:PORT地址的列表 24 | 25 | scanner := bufio.NewScanner(file) 26 | for scanner.Scan() { 27 | line := scanner.Text() 28 | // 提取IP:PORT地址 29 | ipport := examineIpPort(line) 30 | if ipport != "" { 31 | log.Verbose(fmt.Sprint("当前分析文本里读取到的值为:", ipport)) 32 | ipports = append(ipports, ipport) 33 | } 34 | } 35 | 36 | if err := scanner.Err(); err != nil { 37 | log.DebugError(err) 38 | return []string{} 39 | } 40 | log.Debug(fmt.Sprint("当前全部读取并确认的文本里数据长度为:", len(ipports))) 41 | return ipports 42 | } 43 | 44 | // examineIpPort 分析字符串参数,若为IP:PORT格式的则,并生成url地址 45 | func examineIpPort(line string) string { 46 | 47 | // 判断当前输入的ip地址或者域名地址是否包含 http 或者 https 48 | if IsIPAddressWithPort(line) || IsDomainNameWithPort(line) { 49 | return line 50 | } 51 | 52 | return "" 53 | } 54 | -------------------------------------------------------------------------------- /function/config.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "github.com/go-ini/ini" 5 | "main/function/balance" 6 | "sync" 7 | ) 8 | 9 | // 当前版本信息 10 | var Version = "1.0.2" 11 | 12 | // logo 13 | var Slogan = ` 14 | 15 | ________ ________ ________ _____ ______ ________ ________ ___ ________ 16 | |\ ____\|\ __ \|\ __ \|\ _ \ _ \|\ __ \|\ ____\|\ \|\ ____\ 17 | \ \ \___|\ \ \|\ \ \ \|\ \ \ \\\__\ \ \ \ \|\ \ \ \___|\ \ \ \ \___| 18 | \ \_____ \ \ ____\ \ ____\ \ \\|__| \ \ \ __ \ \ \ __\ \ \ \ \ 19 | \|____|\ \ \ \___|\ \ \___|\ \ \ \ \ \ \ \ \ \ \ \|\ \ \ \ \ \____ 20 | ____\_\ \ \__\ \ \__\ \ \__\ \ \__\ \__\ \__\ \_______\ \__\ \_______\ 21 | |\_________\|__| \|__| \|__| \|__|\|__|\|__|\|_______|\|__|\|_______| 22 | \|_________| 23 | 24 | Sppmagic version: ` + Version + ` 25 | 26 | ` 27 | 28 | var ( 29 | Txtfilepath string // 用于存放免费代理路径的参数 30 | Model string // 用于选择数据获取模式的参数 31 | ) 32 | 33 | var ( 34 | Conf *ini.File // 用于后期存在 config.ini 中的参数信息 35 | Fofa_email string // 用于存放fofa的邮箱信息 36 | Fofa_key string // 用于存放fofa的token信息 37 | Thread int // 这个是存活校验的线程参数 38 | Quake_token string // 用于存放quake的token 39 | Quake_size int // 用于设置每次quake查询的返回最大条数 40 | Quake_permissions int // 用于判断当前用户是注册用户还是会员用户,做出一些提醒 41 | ) 42 | 43 | var ( 44 | Address []string // 用于存在通过采集模块获取到的socks5的IP:PORT的数据 45 | Alive_address []string // 用于存在存活的socks5的地址栏 46 | Alive_address_time [][]string // 用于存放这三个信息 代理IP、响应时间、物理位置 47 | B = &balance.RoundRobinBalance{} // 这是一个数组循环使用的结构体 48 | Wg sync.WaitGroup // 定义一个等待组 49 | ) 50 | 51 | var ( 52 | Tmp int 53 | Tmp_try int // 该参数表示使用代理比较快的前三名的哪一个,参数固定为 0,1,2 默认初始化为 0 ,使用最快的呢个 54 | Tmp_addr string // 临时IP代理使用地址 55 | ) 56 | 57 | var ( 58 | SetProxy = false // 设置固定代理地址 59 | UseProxy = "" 60 | ) 61 | 62 | var ( 63 | LogLevel int // log等级,默认设置3级 64 | NoColor bool // 是否开启log输出非颜色版设置 65 | OutputFileName string // 用于设置log输出名称设置 66 | NoSave bool // not save file // logsync.go 中设置不进行日志写入的设置, 注:在常规的logger中并没有设置该参数 67 | ) 68 | -------------------------------------------------------------------------------- /function/gather/fofa.go: -------------------------------------------------------------------------------- 1 | package gather 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/base64" 6 | "fmt" 7 | "github.com/valyala/fastjson" 8 | "io" 9 | "main/function" 10 | log "main/function/logger" 11 | "main/function/subassembly" 12 | "net/http" 13 | "time" 14 | ) 15 | 16 | // GetFofaSocks5Data 通过fofa获取到现有的socks5不需要认证代理的国内数据,fofa是每60条获取下最新的 17 | func GetFofaSocks5Data() error { 18 | 19 | function.Tmp = 1 20 | for { // 外循环使用了并发标识进行并发,下列又实用了tmp进行结果延缓等待,避免应并发过快,导致程序结束 21 | if function.Tmp > 1 { // 第一次运行不需要挂在60秒,第二次for循环时将挂在1分钟后再执行 22 | time.Sleep(60 * time.Second) 23 | } 24 | function.Tmp = function.Tmp + 1 25 | req, err := http.NewRequest("GET", "https://fofa.info/api/v1/search/all", nil) // 获取fofa API 接口数据 26 | if err != nil { 27 | log.LogError(err) 28 | return err 29 | } 30 | 31 | // 下行代码创建了一个自定义的http.Transport对象,其中包含一个TLS客户端配置,该配置允许忽略服务器证书验证。 32 | tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 33 | // 参数介绍 rule := `protocol=="socks5" && "Version:5 Method:No Authentication(0x00)" && country="CN"` 34 | rule := function.Conf.Section("fofa").Key("rule").Value() // 获取下fofa的socks5不需账号验证的搜索语法 35 | rule = base64.StdEncoding.EncodeToString([]byte(rule)) // 对搜索语法进行base64位编码 36 | r := req.URL.Query() 37 | r.Add("email", function.Fofa_email) 38 | r.Add("key", function.Fofa_key) 39 | r.Add("qbase64", rule) 40 | r.Add("size", "2000") // 获取每页查询数量,这个参数感觉可以修改到配置文件里,进行手工修改 41 | req.URL.RawQuery = r.Encode() // 组合上述添加的URL参数 42 | req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") 43 | resp, err := (&http.Client{Transport: tr}).Do(req) // 获取到fofa上前2000个socks5代理地址信息 44 | if err != nil { 45 | log.LogError(err) 46 | return err 47 | } 48 | defer resp.Body.Close() // 在函数结束后自动关闭响应体 49 | body, _ := io.ReadAll(resp.Body) // 获取到比特形式的界面返回结果 50 | var p fastjson.Parser // 定义一个 json 格式的参数 51 | v, _ := p.Parse(string(body)) // 分析界面返回的请求数据 52 | if v.GetStringBytes("errmsg") != nil { // 判断是否获取失败,若获取失败,则返回报告信息 53 | log.Error(fmt.Sprint(string(body))) 54 | return err 55 | } 56 | var rst []string // 定义一个存放结果的字符串数组 57 | for _, i := range v.GetArray("results") { // 获取JSON中results对应的值 58 | ipaddr := string(i.GetStringBytes("1")) + ":" + string(i.GetStringBytes("2")) // 获取 IP:port 数据 59 | rst = append(rst, ipaddr) // 保存到字符串数据组 60 | } 61 | function.Address = rst 62 | subassembly.MakeApiResultDown("Fofa", rst) 63 | log.Debug(fmt.Sprint("获取成功,总查询数量:", len(function.Address))) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /function/proxyService/CoreAgent.go: -------------------------------------------------------------------------------- 1 | package proxyService 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "main/function" 7 | log "main/function/logger" 8 | "net" 9 | "sort" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | // Process 负责把代理服务端获取的数据互相传递给客户端地址 16 | func Process(client net.Conn) { 17 | defer func() { // 若程序报错,则继续执行当前函数 18 | if err := recover(); err != nil { 19 | Process(client) 20 | } 21 | }() 22 | if len(function.Alive_address) == 0 { // 若存活socks5代理池没有存活地址了,则等待1秒再继续 23 | time.Sleep(1 * time.Second) 24 | Process(client) 25 | } 26 | defer client.Close() // 程序结束时,关停监听客户端 27 | addr := getproxy(function.Tmp_try) // 用于获取当前需要使用的socks5代理地址 28 | function.Tmp_addr = addr // 将当前需要使用的地址传递给 tmp_addr 参数 29 | log.Info(fmt.Sprint("当前使用用的ip是", addr)) 30 | cc, err := net.DialTimeout("tcp", addr, 5*time.Second) // 建立一个5秒超时的tcp客户端 31 | if err != nil { 32 | log.Error(fmt.Sprint("connect error:", err)) 33 | function.Tmp_try = 1 + function.Tmp_try // 则进行使用测速第二名的地址 34 | Process(client) 35 | } 36 | function.Tmp_try = 0 // 重置使用测速第一的代理地址 37 | defer cc.Close() // 程序结束时,关闭tcp客户端 38 | go io.Copy(cc, client) // 代理地址端数据传递给客户端地址数据 39 | io.Copy(client, cc) // 客户端地址数据传递给代理地址端数据 40 | } 41 | 42 | // getproxy 根据配置及人工输入,分配是使用固定代理IP,还是轮询策略代理IP,还是速度最快的代理地址IP 43 | func getproxy(arg int) string { 44 | polling := function.Conf.Section("rule").Key("polling").Value() // 获取配置文件中轮询策略设置情况 45 | polling = strings.ToLower(polling) // 大小换成小写 46 | if function.SetProxy == true { // 判断是否使用了固定代理地址,若使用了则返回代理地址 47 | return function.UseProxy 48 | } else { 49 | if polling == "true" { 50 | return function.B.Get() // 轮询策略,也就是每请求一次就换一次代理,默认是开启状态 51 | } else { // 如果不使用固定IP,也没有使用轮询,将使用下面步骤,也就是默认使用速度最快的 52 | var times []int64 53 | for _, i := range function.Alive_address_time { 54 | t, err := strconv.ParseInt(i[1], 10, 64) 55 | if err != nil { 56 | log.Error(fmt.Sprint("代理 ", function.Alive_address_time[0], " 时间转换失败")) 57 | return "" 58 | } 59 | times = append(times, t) // 存放所有测试时间 60 | sort.Slice(times, func(i, j int) bool { // 时间测试排序 61 | return times[i] < times[j] 62 | }) 63 | } 64 | one := times[0] 65 | two := times[1] 66 | three := times[2] 67 | for _, i := range function.Alive_address_time { // 筛选测速结果中比较快的前三名的socks5代理地址,并返回 68 | t, _ := strconv.ParseInt(i[1], 10, 64) 69 | switch arg { 70 | case 0: 71 | if t == one { 72 | return i[0] 73 | } 74 | case 1: 75 | if t == two { 76 | return i[0] 77 | } 78 | case 2: 79 | if t == three { 80 | return i[0] 81 | } 82 | default: 83 | if t == one { 84 | return i[0] 85 | } 86 | } 87 | 88 | } 89 | log.Failed("当前无代理") 90 | return "" 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sppmagic 2 | 一款免费自用的socks5代理小工具,支持从fofa、quake、native三种途径进行socks5代理地址的采集和使用。 3 | 4 | ## 1. 工具介绍 5 | 6 | 本工具是 [SmallProxyPool](https://github.com/Ggasdfg321/SmallProxyPool) 的魔改版本,原工具只支持从fofa上持续自动采集2000条免费socks5地址,然后循环代理,由于fofa的API需要高级会员权限才能使用,我自己用起来不太顺手,故进行二次开发,增加了native(本地读取socks5地址)和360quake一次性自动采集的模块,然后进行循环代理。这里我依旧保留了原作者姓名,毕竟核心代理功能模块以及轮询模块的代码我没变动,且整个项目代码中我均保留了我在阅读时批注的相关注解,便于大家阅读,了解学习socks5代理工具编写相关经验。 7 | 8 | 优化部分: 9 | 10 | 1. 增加层级显示的log日志输出 11 | 2. 增加native本地采集socks5数据功能 12 | 3. 增加360quake采集socks5数据功能 13 | 4. 增加自动采集socks5数据文件保存 14 | 5. 增加存活验证后的socks5代理地址本地保存 15 | 6. 项目目录框架重构,便于后期功能拓展和代码阅读 16 | 7. 交互命令行增加退出按钮 17 | 8. 增加多个拓展复用功能函数 18 | 9. 优化主程序函数执行逻辑 19 | 10. ............... 20 | 21 | ## 2. 工具原理 22 | 23 | 一切以免费自动为前提,工具本身的原理很简单,通过调用fofa或quake的API接口,去查询不需要进行账号验证的socks5代理服务,采集过来后进行存活校验,对可以使用的代理地址放入到轮询代理池中,也可设置使用固定代理地址或最佳代理功能,进行使用。 24 | 25 | fofa采集模块:该模块是每隔60秒进行一次API调用查询,把获取到的socks5代理地址放入待验活序列中进行验活后使用 26 | 27 | native采集模块:手动读取本地TXT文件中的socks5代理地址,只进行一次存活校验后,便进行代理使用 28 | 29 | quake采集模块:该模块为一次性API调用查询,把获取到socks5代理地址放入存活校验后,再进行使用 30 | 31 | 代理轮询模块:把存活的代理地址放入一个数组中,使用.next()函数进行不断传递每一个代理地址到本地代理服务器中 32 | 33 | 最佳代理功能:把所有存活的代理地址放入一个数组中,同时放入的还有每个存活代理的响应时间,把响应时间最佳的3个地址放入到top3选择机制中,默认选择第1个最佳代理地址 34 | 35 | ## 3. 使用说明 36 | 37 | 下载安装编译: 38 | 39 | ```go 40 | 1、git clone https://github.com/sh1yan/Sppmagic.git 41 | 2、go build -ldflags="-s -w " -trimpath main.go 42 | ``` 43 | 44 | 常规用法: 45 | 46 | ``` go 47 | 注:手动运行工具第一次后,会在工具同目录下生成一个 config.ini 文件,需要按照个人想法进行配置对应的参数信息 48 | 特殊情况:假如出现程序卡顿,失效等情况,可以对工具强制退出后,再次运行时增加个 -logl 4 或者 -logl 5 进行查看具体问题原因 49 | 特殊情况示例:./Sppmagic -m fofa -logl 4 或者 ./Sppmagic -m quake -logl 4 等 50 | 51 | ./Sppmagic -m native -f proxy.txt (读取本地 proxy.txt 代理地址) 52 | ./Sppmagic -m fofa (使用 fofa API接口模式进行不断获取socks5代理地址) 53 | ./Sppmagic -m quake (使用 quake API接口模式进行一次性获取socks5代理地址) 54 | 55 | 交互式命令: 56 | show all // 显示全部可用的代理 57 | show ip // 显示当前使用的代理IP 58 | use ip:port // 使用ip:port作为当前代理IP 59 | use random // 恢复默认的代理IP 60 | exit // 退出程序 61 | ``` 62 | 63 | 参数列表: 64 | 65 | ```go 66 | 67 | ________ ________ ________ _____ ______ ________ ________ ___ ________ 68 | |\ ____\|\ __ \|\ __ \|\ _ \ _ \|\ __ \|\ ____\|\ \|\ ____\ 69 | \ \ \___|\ \ \|\ \ \ \|\ \ \ \\\__\ \ \ \ \|\ \ \ \___|\ \ \ \ \___| 70 | \ \_____ \ \ ____\ \ ____\ \ \\|__| \ \ \ __ \ \ \ __\ \ \ \ \ 71 | \|____|\ \ \ \___|\ \ \___|\ \ \ \ \ \ \ \ \ \ \ \|\ \ \ \ \ \____ 72 | ____\_\ \ \__\ \ \__\ \ \__\ \ \__\ \__\ \__\ \_______\ \__\ \_______\ 73 | |\_________\|__| \|__| \|__| \|__|\|__|\|__|\|_______|\|__|\|_______| 74 | \|_________| 75 | 76 | Sppmagic version: 1.0.1 77 | 78 | Usage of ./Sppmagic_Arm64_20230922: 79 | -f string 80 | URL文件路径地址,请参照格式输入, -f D://proxy.txt 81 | -logl int 82 | 设置日志输出等级,默认为3级,-logl 3 (default 3) 83 | -m string 84 | 目前存在3种数据获取模式:native | quake | fofa , -m native 85 | -o string 86 | (default "outcome.txt") 87 | 88 | ``` 89 | 90 | ## 4. 运行展示 91 | 以下运行截图均为代码Debug测试中运行截图,非最终结果样式(2023.09.23) 92 | 93 | `./Sppmagic -h` 94 | ![1](./image/1.png) 95 | 96 | `./Sppmagic -m native -f proxy.txt` 97 | ![2](./image/2.png) 98 | 99 | `./Sppmagic -m fofa` 100 | ![3](./image/3.png) 101 | 102 | `./Sppmagic -m quake` 103 | ![4](./image/4.png) 104 | 105 | ## 5. 更新概况 106 | [+] 2023.9.23 初版代码工具完工 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /function/subassembly/means.go: -------------------------------------------------------------------------------- 1 | package subassembly 2 | 3 | import ( 4 | "flag" 5 | "golang.org/x/text/encoding/simplifiedchinese" 6 | "main/function" 7 | log "main/function/logger" 8 | "regexp" 9 | ) 10 | 11 | func Flag() { 12 | flag.StringVar(&function.Txtfilepath, "f", "", "URL文件路径地址,请参照格式输入, -f D://proxy.txt") 13 | flag.StringVar(&function.Model, "m", "", "目前存在3种数据获取模式:native | quake | fofa , -m native") 14 | flag.IntVar(&function.LogLevel, "logl", 3, "设置日志输出等级,默认为3级,-logl 3") 15 | flag.StringVar(&function.OutputFileName, "o", "outcome.txt", "") 16 | flag.Parse() 17 | } 18 | 19 | // RemoveDuplicates 删除重复项 20 | func RemoveDuplicates(arr []string) []string { 21 | encountered := map[string]bool{} // 用于记录已经遇到的元素 22 | result := []string{} // 存储去重后的结果 23 | 24 | for _, value := range arr { 25 | if !encountered[value] { 26 | encountered[value] = true 27 | result = append(result, value) 28 | } 29 | } 30 | 31 | return result 32 | } 33 | 34 | // IsDomainNameWithPort 判断字符串是否是域名加端口形式 35 | func IsDomainNameWithPort(str string) bool { 36 | domainPortPattern := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}:\d+$` // 判断主域名加端口形式的正则 37 | match, _ := regexp.MatchString(domainPortPattern, str) 38 | if !match { 39 | subdomainsPortPattern := `^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}:\d+$` // 判断子域名加端口形式的正则 40 | match1, _ := regexp.MatchString(subdomainsPortPattern, str) 41 | return match1 42 | } 43 | return match 44 | } 45 | 46 | // IsIPAddressWithPort 判断字符串是否是IP地址加端口号的格式 47 | func IsIPAddressWithPort(str string) bool { 48 | ipPortPattern := `^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$` 49 | match, _ := regexp.MatchString(ipPortPattern, str) 50 | return match 51 | } 52 | 53 | // IsCharacterEmpty 判断当前输入的文件是否为空 54 | func IsCharacterEmpty(char string) bool { 55 | return len(char) != 0 56 | } 57 | 58 | // SliceDelete 接收传入的数组并删除制定的参数 59 | func SliceDelete(tslice any, val any) any { 60 | 61 | if _, ok := tslice.([]string); ok { // 字符串数组 删除其中与指定值相等的元素,然后返回修改后的切片 62 | slice := tslice.([]string) 63 | for i := 0; i < len(slice); i++ { 64 | if slice[i] == val { 65 | slice = append(slice[:i], slice[i+1:]...) 66 | i-- 67 | } 68 | } 69 | return slice 70 | 71 | } else if _, ok := tslice.([]int64); ok { // 数字数组 删除其中与指定值相等的元素,然后返回修改后的切片 72 | slice := tslice.([]int64) 73 | for i := 0; i < len(slice); i++ { 74 | if slice[i] == val { 75 | slice = append(slice[:i], slice[i+1:]...) 76 | i-- 77 | } 78 | } 79 | return slice 80 | } else if _, ok := tslice.([][]string); ok { // 双重字符串数组 删除其中与指定值相等的元素,然后返回修改后的切片 81 | slice := tslice.([][]string) 82 | for i := 0; i < len(slice); i++ { 83 | if slice[i][0] == val { 84 | slice = append(slice[:i], slice[i+1:]...) 85 | i-- 86 | } 87 | } 88 | return slice 89 | } 90 | log.Error("暂时不支持这种类型转换") 91 | panic("暂时不支持这种类型转换") 92 | 93 | } 94 | 95 | // SlicesFind 用于遍历参数2是否在参数1的数组中,若在则返回true 96 | func SlicesFind(slice []string, val string) bool { 97 | for _, item := range slice { 98 | if item == val { 99 | return true 100 | } 101 | } 102 | return false 103 | } 104 | 105 | func silcesIndex(slice []string, val string) int { 106 | for n, item := range slice { 107 | if item == val { 108 | return n 109 | } 110 | } 111 | return -1 112 | } 113 | 114 | func ConvertByte2String(byte []byte, charset string) string { 115 | 116 | var str string 117 | switch charset { 118 | case "GB18030": 119 | var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte) 120 | str = string(decodeBytes) 121 | case "UTF8": 122 | fallthrough 123 | default: 124 | str = string(byte) 125 | } 126 | 127 | return str 128 | } 129 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "flag" 6 | "fmt" 7 | "github.com/go-ini/ini" 8 | "io/ioutil" 9 | "main/function" 10 | "main/function/command" 11 | "main/function/gather" 12 | "main/function/inspect" 13 | log "main/function/logger" 14 | "main/function/proxyService" 15 | "main/function/subassembly" 16 | "net" 17 | "os" 18 | "strconv" 19 | ) 20 | 21 | // init 初始配置文件加载显示 22 | func init() { 23 | fmt.Print(function.Slogan) // logo 输出 24 | fmt.Println("原作者:Ggasdfg321 By T00ls.Com") 25 | subassembly.Flag() 26 | 27 | tcfgs, err := ini.Load("config.ini") // 加载默认配置文件 28 | if err != nil { 29 | // 若不存在以上配置文件,则进行在当前文件夹下生成默认的config.ini配置文件 // 以下base64位字符就是配置文件信息 30 | tconf, _ := base64.StdEncoding.DecodeString("W2dsb2JhbF0KYmluZF9pcCA9IDEyNy4wLjAuMQpiaW5kX3BvcnQgPSAxMDgwCnRocmVhZCA9IDEwMDAKCltxdWFrZV0KcXVha2V0b2tlbiA9ICIiCnJ1bGUgPSBzZXJ2aWNlOiJzb2NrczUiIGFuZCByZXNwb25zZToiQWNjZXB0ZWQgQXV0aCBNZXRob2Q6IDB4MCIgYW5kIGNvdW50cnk6Q04Kc2l6ZSA9IDUwMAojIHBlcm1pc3Npb25zIOS4uui0puWPt+eahOadg+mZkO+8jOmcgOimgeaJi+W3peWhq+WGme+8jDDkuLrms6jlhoznlKjmiLfvvIwx5Li66auY57qn5Lya5ZGYL+e7iOi6q+S8muWRmApwZXJtaXNzaW9ucyA9IDAKCltmb2ZhXQplbWFpbCA9CmtleSA9IApydWxlID0gJ3Byb3RvY29sPT0ic29ja3M1IiAmJiAiVmVyc2lvbjo1IE1ldGhvZDpObyBBdXRoZW50aWNhdGlvbigweDAwKSIgJiYgY291bnRyeT0iQ04iJwoKW3J1bGVdCiMg5piv5ZCm5byA5ZCv6L2u6K+i562W55Wl77yM5Lmf5bCx5piv5q+P6K+35rGC5LiA5qyh5bCx5o2i5LiA5qyh5Luj55CG77yM5LiN5byA5ZCv55qE6K+d5bCx5piv5Zu65a6a6YCf5bqm5pyA5b+r55qE5Luj55CGaXAKcG9sbGluZz0gdHJ1ZQ==") 31 | ioutil.WriteFile("config.ini", tconf, 0666) 32 | } 33 | function.Conf = tcfgs // 把本地读取到的信息传递给提前定义的 conf 变量中 34 | if len(function.Model) != 0 { 35 | switch function.Model { 36 | case "native": 37 | log.Debug("当前 -m 输入的参数为:native") 38 | gather.GetNativeSocks5Data() 39 | case "quake": 40 | log.Debug("当前 -m 输入的参数为:quake") 41 | function.Quake_token = function.Conf.Section("quake").Key("quaketoken").Value() // 获取token 42 | function.Quake_size, _ = strconv.Atoi(function.Conf.Section("quake").Key("size").Value()) // 获取JSON数据查询最大值 43 | function.Quake_permissions, _ = strconv.Atoi(function.Conf.Section("quake").Key("permissions").Value()) // 获取当前用户的权限 44 | gather.GetQuakeAccountInfo() // 获取当前quake账号的积分和API接口剩余次数信息 45 | gather.GetQuakeSocks5Data() // 获取API查询获取到的信息 46 | case "fofa": 47 | log.Debug("当前 -m 输入的参数为:fofa") 48 | function.Fofa_email = function.Conf.Section("fofa").Key("email").Value() // 获取邮箱 49 | function.Fofa_key = function.Conf.Section("fofa").Key("key").Value() // 获取秘钥 50 | default: 51 | log.Debug(fmt.Sprintf("当前 -m 输入的参数为:%v", function.Model)) 52 | log.Error("输入的 -m 的参数错误,请重新输入") 53 | flag.PrintDefaults() 54 | os.Exit(0) 55 | } 56 | } else { 57 | log.Error("没有输入指定的参数,使用该工具必须输入 -m 的参数") 58 | flag.PrintDefaults() 59 | os.Exit(0) 60 | } 61 | 62 | function.Thread, _ = strconv.Atoi(function.Conf.Section("global").Key("thread").Value()) // 获取线程数 63 | function.Tmp_try = 0 // 初始化 tmp_try 参数,用于筛选使用测速最快的前三的哪个IP地址 64 | } 65 | 66 | // main 工具入口函数处 67 | func main() { 68 | log.Info("正在获取socks5代理中") 69 | subassembly.MkdirResult() // 创建存放一些结果数据的目录 70 | 71 | if function.Model == "fofa" { 72 | go gather.GetFofaSocks5Data() // 通过fofa自动获取大量socks5代理地址 73 | } 74 | go inspect.CheckAlive() // 对大量socks5地址进行存活性探测 75 | go command.Command() // 通过命令设置,使用固定代理还是使用随机代理地址 76 | 77 | // 获取配置文件中的本地监听地址 127.0.0.1 监听 1080 78 | add := function.Conf.Section("global").Key("bind_ip").Value() + ":" + function.Conf.Section("global").Key("bind_port").Value() 79 | server, err := net.Listen("tcp", add) 80 | if err != nil { 81 | log.LogError(err) 82 | return 83 | } 84 | for { // 持续监听当前端口 85 | client, err := server.Accept() // 接受等待并向监听器返回下一个连接 86 | if err != nil { 87 | log.Error(fmt.Sprintf("Accept failed : %v", err)) 88 | continue 89 | } 90 | go proxyService.Process(client) // 循环监听数据之间的传递 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /function/command/cmd.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "fmt" 7 | "main/function" 8 | log "main/function/logger" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "reflect" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // cmd 存放输入的命令 18 | type cmd struct { 19 | command string 20 | } 21 | 22 | var ( 23 | c = cmd{} // 实例化一个cmd结构体 24 | ) 25 | 26 | // Show 显示当前代理地址延迟情况或者显示当前存活代理池里的IP地址信息 27 | func (c cmd) Show(args []string) { 28 | if len(args) > 1 { 29 | if args[1] == "ip" { 30 | for _, i := range function.Alive_address_time { 31 | if i[0] == function.Tmp_addr { 32 | defer func() { 33 | if err := recover(); err != nil { 34 | log.Info(fmt.Sprint("当前使用的IP地址是 ", function.Tmp_addr, " ", i[2], " 延迟", "错误")) 35 | return 36 | } 37 | }() 38 | socksProxy := "socks5://" + function.Tmp_addr 39 | proxy := func(_ *http.Request) (*url.URL, error) { 40 | return url.Parse(socksProxy) 41 | } 42 | tr := &http.Transport{ 43 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 44 | Proxy: proxy, 45 | } 46 | start := time.Now() 47 | url := "https://opendata.baidu.com/api.php" // {"status":1,"msg":"\u53c2\u6570\u9519\u8bef","data":[]} 48 | req, _ := http.NewRequest("GET", url, nil) 49 | req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") 50 | (&http.Client{Transport: tr, Timeout: 8 * time.Second}).Do(req) 51 | end := time.Since(start) 52 | log.Info(fmt.Sprint("当前使用的IP地址是", function.Tmp_addr, i[2], "延迟", end)) // 延迟测试 53 | return 54 | } 55 | } 56 | fmt.Println("请先使用代理后在执行这条命令") 57 | return 58 | } else if args[1] == "all" { // 显示当前所有socks5代理资源池里的IP地址 59 | log.Info("正在输出全部IP地址") 60 | log.Info("---------------------------------") 61 | for _, i := range function.Alive_address_time { 62 | log.Info(fmt.Sprint(i[0], " ", i[1], " ", i[2])) 63 | } 64 | log.Info("---------------------------------") 65 | log.Info(fmt.Sprint("一共有 ", len(function.Alive_address_time), " 代理")) 66 | return 67 | } else { 68 | log.Error(fmt.Sprint(args[0], " ", args[1], ":参数错误!")) 69 | } 70 | } 71 | 72 | } 73 | 74 | // Use 设置固定代理地址或使用随机代理地址 75 | func (c cmd) Use(args []string) { 76 | if len(args) > 1 { 77 | if strings.Contains(args[1], ":") { 78 | function.SetProxy = true 79 | function.UseProxy = args[1] 80 | log.Success(fmt.Sprint("设置代理 ", function.UseProxy, " 成功")) 81 | return 82 | } else if args[1] == "random" { 83 | function.SetProxy = false 84 | function.UseProxy = "" 85 | log.Success("设置random成功") 86 | return 87 | } 88 | } 89 | log.Error(fmt.Sprint(args[0], ":参数错误!")) 90 | } 91 | 92 | // Exit 退出命令 93 | func (c cmd) Exit(args []string) { 94 | os.Exit(0) 95 | } 96 | 97 | // Command 接收命令并分析命令再执行命令 98 | func Command() { 99 | 100 | for { 101 | if len(function.Alive_address) > 0 { // 判断是否存在存活地址 102 | c.command = "" // 初始化命令 103 | defer func() { // 若程序崩溃,则输出错误,并继续执行 Command() 函数 104 | if err := recover(); err != "" { 105 | // log.LogError(err) // debug的时候使用 106 | log.Error(fmt.Sprint(c.command+" : ", "指令错误")) 107 | Command() 108 | } 109 | }() 110 | log.Command("-> ") // 提示符 111 | reader := bufio.NewReader(os.Stdin) // 接收外部参数传入 112 | c.command, _ = reader.ReadString('\n') // 判断是否接收到换行符了 113 | c.command = strings.TrimSpace(c.command) // 去掉前后无用空白 114 | if c.command == "" { // 若当前命令为空,则跳出当次循环 115 | continue 116 | } 117 | funcs := reflect.ValueOf(&c) // 传入的参数为结构体的函数名 118 | comm := strings.ToUpper(c.command[:1]) + c.command[1:] // 小写变大写 组合成字符串命令 119 | var args []reflect.Value // 定义一个 Value 是 Go 值的反射接口。 120 | if len(strings.Split(c.command, " ")) > 1 { // 判断是否存在输入命令 121 | comm = strings.Split(comm, " ")[0] // 获取到参数的名称 122 | args = []reflect.Value{reflect.ValueOf(strings.Split(c.command, " "))} // 获取到参数的值 123 | } else { 124 | args = []reflect.Value{reflect.ValueOf([]string{c.command})} // 默认获取参数的名称 125 | } 126 | funcs.MethodByName(comm).Call(args) // show ip or show all 127 | } 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /function/inspect/checkAlive.go: -------------------------------------------------------------------------------- 1 | package inspect 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "github.com/panjf2000/ants/v2" 7 | "github.com/valyala/fastjson" 8 | "io" 9 | "main/function" 10 | log "main/function/logger" 11 | "main/function/subassembly" 12 | "net/http" 13 | "net/url" 14 | "strconv" 15 | "strings" 16 | "sync" 17 | "time" 18 | ) 19 | 20 | // CheckAlive 持续对socks5代理地址进行存活性探测,最后再放入循环结构体中 21 | func CheckAlive() { 22 | lock := &sync.Mutex{} // 生成一个互斥锁,用来保护并发时产生的各种问题 23 | var fastj fastjson.Parser 24 | for { 25 | if len(function.Address) == 0 { // 这里因为是采用了并发模式,可能代理池还没有接收到到参数,所以挂在等待地址里存在数据 26 | time.Sleep(1 * time.Second) 27 | } 28 | // log.Debug(fmt.Sprint("正在过滤代理中")) 29 | 30 | // 目前ini配置中的thread线程是1000 31 | // 该并发池主要用于批量判断传入的socks5代理是否为存活的,同时获取代理的延迟和物理位置 32 | // 原理就是创建一个HTTP客户端的代理,然后使用该代理去请求百度open来获得物理位置,若存活则正常存放,若失败得从历史池中取出 33 | p, _ := ants.NewPoolWithFunc(function.Thread, func(i interface{}) { // 使用ants生成一个高效的 goroutine 池 34 | 35 | // 以下程序均是在代理池中运行 36 | 37 | socks5 := i.(string) 38 | 39 | // 当前函数结束时,该函数会判断程序结束时是否因为panic goroutine的程序错误导致的终端,若是的话则删除当前这个无效的socks5代理地址 40 | defer func() { 41 | if err := recover(); err != nil { // 判断是否因为panic的程序错误 42 | if subassembly.SlicesFind(function.Alive_address, socks5) == true { // 若socks5参数在存活的地址栏中则进入下列代码块 43 | function.Alive_address = subassembly.SliceDelete(function.Alive_address, socks5).([]string) // 在存活列表中删除无用的socks5地址 44 | function.Alive_address_time = subassembly.SliceDelete(function.Alive_address_time, socks5).([][]string) // 删除多重数组中信息 45 | } 46 | function.Wg.Done() // 计数器减一 47 | return 48 | } 49 | }() 50 | socksProxy := "socks5://" + socks5 51 | proxy := func(_ *http.Request) (*url.URL, error) { 52 | return url.Parse(socksProxy) // 该代理函数通常用于将HTTP请求路由到代理服务器,以便在客户端和目标服务器之间建立代理连接。 53 | } 54 | tr := &http.Transport{ // 创建一个自定义的 HTTP 客户端 55 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 56 | Proxy: proxy, 57 | } 58 | 59 | // 获取当前代理IP地址的信息 60 | urlProxy := fmt.Sprintf("https://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006", strings.Split(socks5, ":")[0]) 61 | start := time.Now().UnixNano() // 创建一个开始的时间戳 62 | req, _ := http.NewRequest("GET", urlProxy, nil) 63 | req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") 64 | req.Header.Add("Connection", "close") 65 | resp, _ := (&http.Client{Transport: tr, Timeout: 8 * time.Second}).Do(req) 66 | stop := time.Now().UnixNano() - start // 创建一个开始的时间戳 67 | if resp.StatusCode == 200 { // 判断是否正常获取到返回信息 68 | defer resp.Body.Close() 69 | body, _ := io.ReadAll(resp.Body) 70 | v, _ := fastj.Parse(string(body)) // 以JSON格式解析返回的信息内容 71 | 72 | // 获取socks5的IP地址归属地址 73 | location := subassembly.ConvertByte2String(v.GetStringBytes("data", "0", "location"), "GB18030") 74 | if location != "" { // 判断socks5代理地址的物理位置是否为空 75 | if subassembly.SlicesFind(function.Alive_address, socks5) == false { // 判断当前代理地址是否不在socks5存活代理池中,若不在,则加入代理池中 76 | sliceAlive := []string{socks5, strconv.FormatInt(stop, 10), location} // 放入数组中三个值:代理IP、响应时间、物理位置 77 | lock.Lock() // 以下操作加锁 78 | function.Alive_address = append(function.Alive_address, socks5) // 往存活socks5代理池中存放socks5代理地址 79 | log.Debug(fmt.Sprintf("当前socks5验活过程中,该地址为存活地址:%v", socks5)) 80 | function.Alive_address_time = append(function.Alive_address_time, sliceAlive) // 往[][]string 中存放一个数组 81 | lock.Unlock() // 以上操作解锁 82 | log.LogWriteInfo(socks5) // 将存活的socks5代理地址放入报本地保存记录中 83 | } 84 | } 85 | } 86 | function.Wg.Done() // 并发线程减一 87 | }) 88 | defer p.Release() // 函数结束时,Release 关闭该池并释放工人队列。 89 | for _, i := range function.Address { // 获取从fofa中搜集到的socks5代理地址 90 | function.Wg.Add(1) // 工作池进程加一 91 | _ = p.Invoke(i) // 向任务池提交任务,提交socks5地址 92 | } 93 | function.Wg.Wait() // 等待工作池全部运行完毕 94 | log.Debug("程序当前所有导入到验活功能里socks5地址以全部验活完成") 95 | function.B.Set(function.Alive_address) // 将存活socks5代理地址都放入循环体中,进行循环使用 96 | log.Debug(fmt.Sprintf("截止到本轮验活中,共有 %v 条存活地址", len(function.Alive_address))) 97 | // fmt.Println("[*]", "过滤完成,一共有", len(alive_address), "/", len(alive_address_time), "个代理可用") 98 | time.Sleep(10 * time.Second) // 挂载十秒钟 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 6 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 7 | github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= 8 | github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 9 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 10 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 11 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 12 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 13 | github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 14 | github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= 15 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 16 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 17 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 18 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 19 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 20 | github.com/panjf2000/ants/v2 v2.7.1 h1:qBy5lfSdbxvrR0yUnZfaEDjf0FlCw4ufsbcsxmE7r+M= 21 | github.com/panjf2000/ants/v2 v2.7.1/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= 22 | github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= 23 | github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= 24 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 25 | github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= 26 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 27 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 28 | github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= 29 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 30 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 31 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 32 | github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk= 33 | github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8= 34 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 35 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 36 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 37 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 38 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 39 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 40 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 41 | github.com/tealeg/xlsx/v3 v3.3.2 h1:S8Z9YTzpItYSnCNm3BKo7TSgpoX09NpUG+C/heV/Sh4= 42 | github.com/tealeg/xlsx/v3 v3.3.2/go.mod h1:KV4FTFtvGy0TBlOivJLZu/YNZk6e0Qtk7eOSglWksuA= 43 | github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= 44 | github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 45 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 46 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 47 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 48 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 49 | github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= 50 | github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= 51 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= 52 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= 53 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 54 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 55 | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 56 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 58 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= 59 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 60 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 61 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 62 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 63 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 64 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 65 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 66 | -------------------------------------------------------------------------------- /function/logger/log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gookit/color" 6 | fc "main/function" 7 | "os" 8 | "path" 9 | "regexp" 10 | "runtime" 11 | "time" 12 | ) 13 | 14 | var ( // 输出颜色设置 15 | Red = color.Red.Render 16 | Cyan = color.Cyan.Render 17 | Yellow = color.Yellow.Render 18 | White = color.White.Render 19 | Blue = color.Blue.Render 20 | Purple = color.Style{color.Magenta, color.OpBold}.Render 21 | LightRed = color.Style{color.Red, color.OpBold}.Render 22 | LightGreen = color.Style{color.Green, color.OpBold}.Render 23 | LightWhite = color.Style{color.White, color.OpBold}.Render 24 | LightCyan = color.Style{color.Cyan, color.OpBold}.Render 25 | LightYellow = color.Style{color.Yellow, color.OpBold}.Render 26 | ) 27 | 28 | var ( 29 | defaultLevel = LevelWarning // 输出等级 30 | noWrite int // 不进行写入log 31 | Num int64 32 | End int64 33 | LogSucTime int64 34 | LogErrTime int64 35 | WaitTime int64 36 | ) 37 | 38 | // SetLevel 设置输出等级 39 | func SetLevel(l Level) { 40 | defaultLevel = l 41 | } 42 | 43 | func getCallerInfo(skip int) (info string) { 44 | _, file, lineNo, ok := runtime.Caller(skip) 45 | if !ok { 46 | info = "runtime.Caller() failed" 47 | } 48 | 49 | fileName := path.Base(file) // Base函数返回路径的最后一个元素 50 | return fmt.Sprintf("%s line:%d", fileName, lineNo) 51 | } 52 | 53 | // log 日志输出效验判断 54 | func log(l Level, nw int, detail string) { 55 | switch fc.LogLevel { // 判断配置文件中的日志输出等级,并设置到日志等级 56 | case 0: 57 | SetLevel(0) 58 | case 1: 59 | SetLevel(1) 60 | case 2: 61 | SetLevel(2) 62 | case 3: 63 | SetLevel(3) 64 | case 4: 65 | SetLevel(4) 66 | case 5: 67 | SetLevel(5) 68 | } 69 | 70 | if l > defaultLevel { // 判断输入等级是否大于设置等级,若大于则当前日志则不进行输出 71 | return 72 | } 73 | 74 | if nw == 0 { // 在该项目中,只有logwriteinfo函数才进行写入,剩下的均不写入本地文件 75 | // 目前只写入 info 信息 和 Success 的信息 76 | //strTrim := fmt.Sprintf("[%s] ", Cyan(getDate())) // 匹配log日志前面的日期 77 | //detail := strings.TrimPrefix(detail, strTrim) // 去除日期信息 78 | writeLogFile(clean(detail), fc.OutputFileName) // 写入到本地文件 79 | return 80 | } 81 | 82 | if fc.NoColor { // 判断是否关闭颜色输出 83 | fmt.Println(clean(detail)) 84 | return 85 | } else { 86 | fmt.Println(detail) 87 | } 88 | 89 | if l == LevelFatal { 90 | os.Exit(0) 91 | } 92 | } 93 | 94 | // Fatal 严重级别日志 log等级:0 95 | func Fatal(detail string) { 96 | noWrite = 1 97 | log(LevelFatal, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("FATAL"), detail)) 98 | } 99 | 100 | // LogWriteInfo log写入本地消息日志 log等级:2 101 | func LogWriteInfo(detail string) { 102 | noWrite = 0 103 | log(LevelInfo, noWrite, detail) 104 | } 105 | 106 | // Error 错误日志 log等级:1 107 | func Error(detail string) { 108 | noWrite = 1 109 | log(LevelError, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("ERROR"), detail)) 110 | } 111 | 112 | // Info 消息日志 log等级:2 113 | func Info(detail string) { 114 | noWrite = 1 115 | log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("INFO"), detail)) 116 | } 117 | 118 | // Warning 告警日志 log等级:3 119 | func Warning(detail string) { 120 | noWrite = 1 121 | log(LevelWarning, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightYellow("WARNING"), detail)) 122 | } 123 | 124 | // Debug 调试日志 log等级:4 125 | func Debug(detail string) { 126 | noWrite = 1 127 | log(LevelDebug, noWrite, fmt.Sprintf("[%s] [%s] [%s] %s", Cyan(getDate()), LightWhite("DEBUG"), Yellow(getCallerInfo(2)), detail)) 128 | } 129 | 130 | // Verbose 详细调试信息日志 log等级:5 131 | func Verbose(detail string) { 132 | noWrite = 1 133 | log(LevelVerbose, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightCyan("VERBOSE"), detail)) 134 | } 135 | 136 | // Success 成功信息日志 log等级:2 137 | func Success(detail string) { 138 | noWrite = 1 139 | log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("+"), detail)) 140 | } 141 | 142 | // Failed 失败信息日志 log等级:2 143 | func Failed(detail string) { 144 | noWrite = 1 145 | log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("-"), detail)) 146 | } 147 | 148 | // Common 普通信息日志 log等级:2 149 | func Common(detail string) { 150 | noWrite = 1 151 | log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("*"), detail)) 152 | } 153 | 154 | // Command 接收命令信息日志,该参数log不换行输出 log等级:2 155 | func Command(detail string) { 156 | // 该命令接收参数信息,主要用于人工的参数输入,任何情况都不写入文件,在log函数中 157 | cmdInfo := fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("✎"), detail) 158 | fmt.Print(cmdInfo) 159 | } 160 | 161 | func LogError(errinfo interface{}) { 162 | if WaitTime == 0 { 163 | fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo)) 164 | } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { 165 | fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo)) 166 | LogErrTime = time.Now().Unix() 167 | } 168 | } 169 | 170 | func getTime() string { 171 | return time.Now().Format("15:04:05") 172 | } 173 | 174 | func getDate() string { 175 | return time.Now().Format("2006.1.2") 176 | } 177 | 178 | func DebugError(err error) bool { 179 | /* Processing error display */ 180 | if err != nil { 181 | pc, _, line, _ := runtime.Caller(1) 182 | Debug(fmt.Sprintf("%s%s%s", 183 | White(runtime.FuncForPC(pc).Name()), 184 | LightWhite(fmt.Sprintf(" line:%d ", line)), 185 | White(err))) 186 | return true 187 | } 188 | return false 189 | } 190 | 191 | // Clean by https://github.com/acarl005/stripansi/blob/master/stripansi.go 192 | func clean(str string) string { 193 | const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" 194 | var re = regexp.MustCompile(ansi) 195 | return re.ReplaceAllString(str, "") 196 | } 197 | 198 | func writeLogFile(result string, filename string) { 199 | var text = []byte(result + "\n") 200 | fl, err := os.OpenFile(fmt.Sprintf("./result/%v", filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) 201 | if err != nil { 202 | fmt.Printf("Open %s error, %v\n", filename, err) 203 | return 204 | } 205 | _, err = fl.Write(text) 206 | fl.Close() 207 | if err != nil { 208 | fmt.Printf("Write %s error, %v ", filename, err) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /function/gather/quake.go: -------------------------------------------------------------------------------- 1 | package gather 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/tidwall/gjson" 9 | "io" 10 | "main/function" 11 | log "main/function/logger" 12 | "main/function/subassembly" 13 | "net/http" 14 | "strconv" 15 | "time" 16 | ) 17 | 18 | // GetQuakeSocks5Data 用于获取quake里的socks5数据 19 | func GetQuakeSocks5Data() { 20 | 21 | GETRES := []string{} 22 | SearchSyntax := function.Conf.Section("quake").Key("rule").Value() 23 | quake_timeout := strconv.Itoa(120) // 因为作者是终身会员,这里为防止抓取1万条数据时卡住而导致失败 24 | postdata := quake_api_search(SearchSyntax, 0, function.Quake_size, function.Quake_permissions) 25 | res := postQuakeHttp(postdata, function.Quake_token, quake_timeout) 26 | log.Debug(fmt.Sprint(res.Value())) 27 | for i := range res.Get("data").Array() { 28 | log.Verbose(fmt.Sprint(res.Get("data").Array()[i].Get("ip"), ":", res.Get("data").Array()[i].Get("port"))) 29 | ip_port := fmt.Sprintf("%v:%v", res.Get("data").Array()[i].Get("ip"), res.Get("data").Array()[i].Get("port")) 30 | GETRES = append(GETRES, ip_port) 31 | } 32 | function.Address = GETRES 33 | subassembly.MakeApiResultDown("Quake", GETRES) 34 | log.Debug(fmt.Sprintf("获取成功,总查询数量:%v", len(function.Address))) 35 | } 36 | 37 | var ( 38 | Exclude_Senior_Member = []string{"hostname", "transport", "asn", "org", "service.name", "location.country_cn", "location.province_cn", "location.city_cn", "service.http.host", "time", "service.http.title", "service.response", "service.cert", "components.product_catalog", "components.product_type", "components.product_level", "components.product_vendor", "location.country_en", "location.province_en", "location.city_en", "location.district_en", "location.district_cn", "location.isp", "service.http.body", "components.product_name_cn", "components.version", "service.http.infomation.mail", "service.http.favicon.hash", "service.http.favicon.data", "domain", "service.http.status_code"} 39 | Exclude_Registered_Members = []string{"hostname", "transport", "asn", "org", "service.name", "location.country_cn", "location.province_cn", "location.city_cn", "service.http.host", "service.http.title", "service.http.server"} 40 | ) 41 | 42 | // QuakeData 该参数需要保留,用于post数据包查询时使用 43 | type quakeData struct { 44 | Query string `json:"query"` 45 | Start int `json:"start"` 46 | Size int `json:"size"` 47 | Include []string `json:"include"` 48 | Exclude []string `json:"exclude"` 49 | } 50 | 51 | // quake_api_search 用于填充JSON格式的API数据 52 | func quake_api_search(keyword string, start int, size int, grade int) quakeData { 53 | // grade 如果结果大于0,则是高级会员,如果等于0则是普通会员 54 | 55 | reqData := quakeData{} 56 | reqData.Query = keyword 57 | reqData.Start = start 58 | reqData.Size = size 59 | reqData.Include = []string{"ip", "port"} 60 | if grade > 0 { 61 | reqData.Exclude = Exclude_Senior_Member 62 | } else { 63 | reqData.Exclude = Exclude_Registered_Members 64 | } 65 | 66 | return reqData 67 | } 68 | 69 | // quakehttp 核心代码块,用于API请求数据 70 | func postQuakeHttp(postdata quakeData, key string, timeout string) gjson.Result { 71 | var itime, err = strconv.Atoi(timeout) 72 | if err != nil { 73 | log.Error(fmt.Sprintf("设置 Quake 超时参数错误: %v", err)) 74 | } 75 | transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 76 | client := &http.Client{ 77 | Timeout: time.Duration(itime) * time.Second, 78 | Transport: transport, 79 | } 80 | url := "https://quake.360.cn/api/v3/search/quake_service" 81 | payload, _ := json.Marshal(postdata) 82 | 83 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload)) 84 | if err != nil { 85 | log.Error(fmt.Sprint(err)) 86 | } 87 | req.Header.Set("content-type", "application/json") 88 | req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36") 89 | req.Header.Set("X-QuakeToken", key) 90 | resp, err := client.Do(req) 91 | if err != nil { 92 | log.Error(fmt.Sprint(err)) 93 | } 94 | defer resp.Body.Close() 95 | body, _ := io.ReadAll(resp.Body) 96 | jsoninfostr := string(body) 97 | log.Debug(jsoninfostr) 98 | res := gjson.Parse(jsoninfostr) 99 | return res 100 | 101 | } 102 | 103 | func GetQuakeAccountInfo() { 104 | 105 | quake_token := function.Quake_token 106 | 107 | var API_Remain = "0" // 用于存放实时的API剩余次数的参数 108 | 109 | // 创建一个自定义请求对象 110 | req, err := http.NewRequest("GET", "https://quake.360.net/api/v3/user/info", nil) 111 | if err != nil { 112 | log.Error(fmt.Sprintf("创建GET形式请求请求失败:%v", err)) 113 | return 114 | } 115 | // 添加自定义头部信息 116 | req.Header.Add("X-QuakeToken", quake_token) 117 | // 创建HTTP客户端并发送请求 118 | client := &http.Client{} 119 | response, err := client.Do(req) 120 | if err != nil { 121 | log.Error(fmt.Sprintf("进行GET数据请求失败:%v", err)) 122 | return 123 | } 124 | defer response.Body.Close() 125 | // 读取响应的内容 126 | body, err := io.ReadAll(response.Body) 127 | if err != nil { 128 | log.Error(fmt.Sprintf("进行读取API响应信息失败:%v", err)) 129 | return 130 | } 131 | 132 | // 打印响应内容 133 | log.Debug("响应内容:") 134 | log.Debug(string(body)) 135 | jsoninfostr := string(body) 136 | 137 | result := gjson.Parse(jsoninfostr) 138 | f_query_api_count := result.Get("data").Get("f_query_api_count").Value() 139 | if f_query_api_count != nil { 140 | log.Debug(fmt.Sprintf("当前API剩余次数_注册用户:%v", f_query_api_count)) 141 | API_Remain = fmt.Sprint(f_query_api_count) 142 | } else { 143 | free_query_api_count := result.Get("data").Get("free_query_api_count").Value() 144 | log.Debug(fmt.Sprintf("当前API剩余次数_会员用户:%v", free_query_api_count)) 145 | API_Remain = fmt.Sprint(free_query_api_count) 146 | } 147 | 148 | if API_Remain == "0" { 149 | log.Info("当前免费API次数已经用完了,本次将会使用月度积分或长效积分进行数据获取") 150 | log.Info(fmt.Sprintf("当前配置文件中设置的数据范围最大值为:%v", strconv.Itoa(function.Quake_size))) 151 | log.Info(fmt.Sprintf("当前月度剩余积分数:%v", result.Get("data").Get("month_remaining_credit").Value())) 152 | log.Info(fmt.Sprintf("当前长效积分剩余数:%v", result.Get("data").Get("persistent_credit").Value())) 153 | } else { 154 | log.Info(fmt.Sprintf("当前免费API次数还剩余:%v", API_Remain)) 155 | if function.Quake_permissions == 0 { 156 | if function.Quake_size > 500 { 157 | log.Warning("您当前Quake账号为注册用户,默认数据长度为500条,超出部分将会以积分形式扣除") 158 | } 159 | } 160 | log.Info(fmt.Sprintf("当前配置文件中设置的数据范围最大值为:", strconv.Itoa(function.Quake_size))) 161 | } 162 | } 163 | --------------------------------------------------------------------------------