├── .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 | 
95 |
96 | `./Sppmagic -m native -f proxy.txt`
97 | 
98 |
99 | `./Sppmagic -m fofa`
100 | 
101 |
102 | `./Sppmagic -m quake`
103 | 
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 |
--------------------------------------------------------------------------------