init
This commit is contained in:
commit
de0925ef48
110
cap.go
Normal file
110
cap.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcap"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 打印可用的网络接口信息
|
||||||
|
func printAvailableInterfaces() {
|
||||||
|
devices, err := pcap.FindAllDevs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("可用的网络接口:")
|
||||||
|
for _, device := range devices {
|
||||||
|
fmt.Printf("名称: %s\n描述: %s\n地址: %+v\n\n", device.Name, device.Description, device.Addresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断 IP 是否在链表中
|
||||||
|
func isIPInList(ip net.IP) bool {
|
||||||
|
for e := ipList.Front(); e != nil; e = e.Next() {
|
||||||
|
if e.Value.(net.IP).Equal(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印捕获到的每个数据包的信息
|
||||||
|
func printPacketInfo(packet gopacket.Packet) {
|
||||||
|
ipLayer := packet.Layer(layers.LayerTypeIPv4)
|
||||||
|
|
||||||
|
if ipLayer != nil {
|
||||||
|
ip, _ := ipLayer.(*layers.IPv4)
|
||||||
|
ipMutex.Lock()
|
||||||
|
defer ipMutex.Unlock()
|
||||||
|
if !isIPInList(ip.SrcIP) {
|
||||||
|
ipList.PushBack(ip.SrcIP)
|
||||||
|
log.Printf("已添加源 IP: %s 到链表\n", ip.SrcIP)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startPacketCapture() {
|
||||||
|
handle, err := pcap.OpenLive(*interfaceName, 65535, true, pcap.BlockForever)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("打开网络接口 %s 出错: %v", *interfaceName, err)
|
||||||
|
}
|
||||||
|
defer handle.Close()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
fmt.Println("清理资源...")
|
||||||
|
if handle != nil {
|
||||||
|
handle.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = handle.SetBPFFilter(*filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("设置 BPF 过滤器出错: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pcapWriter *pcapgo.Writer
|
||||||
|
if *outputFile != "" {
|
||||||
|
file, err := os.Create(*outputFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("创建输出文件出错: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
pcapWriter = pcapgo.NewWriter(file)
|
||||||
|
err = pcapWriter.WriteFileHeader(65535, layers.LinkTypeEthernet)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("写入全局头部出错: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
|
||||||
|
fmt.Printf("正在监听网络接口 %s,使用过滤器 '%s'...\n", *interfaceName, *filter)
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for packet := range packetSource.Packets() {
|
||||||
|
printPacketInfo(packet)
|
||||||
|
if pcapWriter != nil {
|
||||||
|
err := pcapWriter.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("写入数据包出错: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-sigChan
|
||||||
|
fmt.Println("\n停止抓包...")
|
||||||
|
}
|
91
curl.go
Normal file
91
curl.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IPInfo struct {
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Data struct {
|
||||||
|
Continent string `json:"continent"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidIP(ip string) bool {
|
||||||
|
return net.ParseIP(ip) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func curl_(ip string) (string, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var url string
|
||||||
|
|
||||||
|
if !isValidIP(ip) {
|
||||||
|
log.Printf("无效的 IP 地址: %s", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 目标 URL
|
||||||
|
url = "https://qifu.baidu.com/ip/geo/v1/district?ip=" + ip
|
||||||
|
|
||||||
|
// 创建 HTTP 请求
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("创建请求时出错: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36")
|
||||||
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||||
|
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||||
|
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
|
||||||
|
req.Header.Set("Cache-Control", "max-age=0")
|
||||||
|
req.Header.Set("Connection", "keep-alive")
|
||||||
|
req.Header.Set("Cookie", "BIDUPSID=04922F7E0DDC6A5B7E0BA15C33FD2176; PSTM=1722408897; BAIDUID=8313999060289847CAE0A6F34A9C4206:FG=1; Hm_lvt_edc762f9f8f5f525fad4770a32b5edda=1724987641; Hm_lvt_28a17f66627d87f1d046eae152a1c93d=1724987645; AGL_USER_ID=186badbd-dd36-4d39-89f4-8d2aa7a01414; BDUSS=1pvWmdhYThIelRSS1NuQkJESHNYV0REcG9teXMxOW9VaURTU2tyRlZTZ3Z4dmhtRVFBQUFBJCQAAAAAAAAAAAEAAADGRkRexaPT8cHrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC850WYvOdFmM")
|
||||||
|
req.Header.Set("Host", "qifu.baidu.com")
|
||||||
|
req.Header.Set("Sec-Ch-Ua", `"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"`)
|
||||||
|
req.Header.Set("Sec-Ch-Ua-Mobile", "?1")
|
||||||
|
req.Header.Set("Sec-Ch-Ua-Platform", `"Android"`)
|
||||||
|
req.Header.Set("Sec-Fetch-Dest", "document")
|
||||||
|
req.Header.Set("Sec-Fetch-Mode", "navigate")
|
||||||
|
req.Header.Set("Sec-Fetch-Site", "none")
|
||||||
|
req.Header.Set("Sec-Fetch-User", "?1")
|
||||||
|
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||||
|
|
||||||
|
// 创建 HTTP 客户端并设置超时时间
|
||||||
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("发送请求时出错: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 检查 HTTP 响应状态码
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
log.Printf("HTTP 请求失败,状态码: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取响应体
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("读取响应体时出错: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 JSON 数据
|
||||||
|
var ipInfo IPInfo
|
||||||
|
if err := json.Unmarshal(body, &ipInfo); err != nil {
|
||||||
|
log.Printf("解析 JSON 时出错: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("%s%s\n", ipInfo.Data.Continent, ipInfo.Data.Country)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%s\n", ipInfo.Data.Continent, ipInfo.Data.Country), err
|
||||||
|
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module denyip
|
||||||
|
|
||||||
|
go 1.23.4
|
||||||
|
|
||||||
|
require github.com/google/gopacket v1.1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230 // indirect
|
||||||
|
golang.org/x/net v0.33.0 // indirect
|
||||||
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
|
)
|
20
go.sum
Normal file
20
go.sum
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230 h1:B0oaMTAQKDZd8cwYT0qsAI7+c3KbFeBNA8GhgoBMXWw=
|
||||||
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
43
ip2region.go
Normal file
43
ip2region.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ip2region(ip string) (string, error) {
|
||||||
|
var dbPath = "ip2region/ip2region.xdb"
|
||||||
|
searcher, err := xdb.NewWithFileOnly(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create searcher: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
defer searcher.Close()
|
||||||
|
|
||||||
|
// 备注:并发使用,每个 goroutine 需要创建一个独立的 searcher 对象。
|
||||||
|
|
||||||
|
// 1、从 dbPath 加载整个 xdb 到内存
|
||||||
|
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to load content from `%s`: %s\n", dbPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、用全局的 cBuff 创建完全基于内存的查询对象。
|
||||||
|
searcher, err = xdb.NewWithBuffer(cBuff)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create searcher with content: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
region, err := searcher.SearchByStr(ip)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//tStart := time.Time{}
|
||||||
|
//fmt.Printf("{region: %s, took: %s}\n", region, time.Since(tStart))
|
||||||
|
|
||||||
|
// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。
|
||||||
|
|
||||||
|
return region, err
|
||||||
|
}
|
BIN
ip2region/ip2region.xdb
Normal file
BIN
ip2region/ip2region.xdb
Normal file
Binary file not shown.
126
ipset.go
Normal file
126
ipset.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createIPSet(setName string) error {
|
||||||
|
cmd := exec.Command("ipset", "create", setName, "hash:ip")
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
// 记录错误信息,但不退出
|
||||||
|
log.Printf("failed to execute command: %v, stderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
return err // 返回错误以便调用者处理
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddIPSet(setName string, ip string) error {
|
||||||
|
cmd := exec.Command("ipset", "add", setName, ip)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
// 记录错误信息,但不退出
|
||||||
|
log.Printf("failed to add IP to set: %v, stderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
return err // 返回错误以便调用者处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumIPSet returns the number of entries in the specified ipset set.
|
||||||
|
func NumIPSet(setName string) (int, error) {
|
||||||
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("ipset list %s | grep \"Number of entries\" | cut -d ':' -f 2 | sed 's/ //g'", setName))
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("cmd.Run() failed with %v, stderr: %s\n", err, stderr.String())
|
||||||
|
return 0, fmt.Errorf("failed to execute command: %w, stderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
output := strings.TrimSpace(stdout.String())
|
||||||
|
numEntries, err := strconv.Atoi(output)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to parse output as integer: %v, output: %s\n", err, output)
|
||||||
|
return 0, fmt.Errorf("failed to parse output as integer: %w, output: %s", err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return numEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIpset 检查名为 setName 的 ipset 是否存在,通过返回 0 表示存在,非零表示不存在或其他错误。
|
||||||
|
func IsIpset(setName string) int {
|
||||||
|
cmd := exec.Command("ipset", "list", setName)
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
// The program has exited with an exit code != 0
|
||||||
|
return exitError.ExitCode()
|
||||||
|
} else {
|
||||||
|
// Another error occurred (e.g., command not found)
|
||||||
|
return -1 // 或者你可以选择其他方式来标识这种情况
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command executed successfully, the set exists
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func iptables(setName string) error {
|
||||||
|
|
||||||
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("iptables -A INPUT -p tcp -m set --match-set %s src -j DROP", setName))
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("cmd.Run() failed with %v, stderr: %s\n", err, stderr.String())
|
||||||
|
err = fmt.Errorf("failed to execute command: %w, stderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func main() {
|
||||||
|
// 创建 IPSet,但即使出错也继续执行
|
||||||
|
err := createIPSet("root0")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("创建 IPSet 出错:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("IPSet 创建成功!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 IP 到 IPSet,出错时继续执行
|
||||||
|
err = AddIPSet("root0", "1.1.1.1")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("添加 IP 到 IPSet 出错:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 IPSet 条目数,出错时继续执行
|
||||||
|
num, err := NumIPSet("root0")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("获取 IPSet 条目数出错:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("IPSet 条目数: %d\n", num)
|
||||||
|
}
|
||||||
|
|
||||||
|
iptables("root0")
|
||||||
|
}
|
||||||
|
*/
|
278
main.go
Normal file
278
main.go
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Daemon = flag.Bool("d", false, "守护进程模式")
|
||||||
|
child = flag.Bool("child", false, "子进程模式")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
listInterfaces bool
|
||||||
|
interfaceName *string
|
||||||
|
outputFile *string
|
||||||
|
filter *string
|
||||||
|
|
||||||
|
SIG int
|
||||||
|
MAX_SIG = 10
|
||||||
|
SIG_NAME *string
|
||||||
|
|
||||||
|
// 存储 IPv4 地址的链表
|
||||||
|
ipList = list.New()
|
||||||
|
ipMutex sync.Mutex // 用于保护 ipList 的互斥锁
|
||||||
|
)
|
||||||
|
|
||||||
|
// 启动子进程
|
||||||
|
func startChildProcess() (*exec.Cmd, error) {
|
||||||
|
cmd := exec.Command(os.Args[0], "-child=true", "-i", *interfaceName)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("启动子进程失败: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("子进程已启动, PID: %d\n", cmd.Process.Pid)
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止子进程
|
||||||
|
func stopChildProcess(cmd *exec.Cmd) error {
|
||||||
|
if cmd == nil || cmd.Process == nil {
|
||||||
|
return fmt.Errorf("子进程无效")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试优雅地停止子进程
|
||||||
|
if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||||
|
return fmt.Errorf("停止子进程失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一段时间让子进程退出
|
||||||
|
done := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
done <- cmd.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("等待子进程退出时出错: %w", err)
|
||||||
|
}
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
fmt.Println("子进程未在规定时间内退出,强制终止...")
|
||||||
|
if err := cmd.Process.Kill(); err != nil {
|
||||||
|
return fmt.Errorf("强制终止子进程失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("子进程已停止, PID: %d\n", cmd.Process.Pid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待信号并优雅退出
|
||||||
|
func waitForSignalAndCleanUp(cmd *exec.Cmd) {
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
<-sigChan
|
||||||
|
fmt.Println("主进程收到终止信号,准备清理子进程...")
|
||||||
|
if err := stopChildProcess(cmd); err != nil {
|
||||||
|
log.Fatalf("清理子进程时遇到错误: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("主进程退出")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 守护进程模式(简化版)
|
||||||
|
func startDaemon() {
|
||||||
|
// 注意:此方法不会创建真正的守护进程,它只是以分离的方式启动一个新的进程。
|
||||||
|
// 对于创建真正的守护进程,你应该使用 systemd 或其他服务管理器。
|
||||||
|
cmd := exec.Command(os.Args[0], "-d=false", "-child=false", "-i", *interfaceName)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("无法启动新实例: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("新实例已启动,当前进程将退出")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runChildProcess() { // 子进程逻辑
|
||||||
|
//fmt.Println("子进程启动中...")
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
ticker := time.NewTicker(3 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-sigChan:
|
||||||
|
fmt.Println("子进程收到终止信号,退出中...")
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
//fmt.Println("子进程!!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMainProcess() { // 主进程逻辑
|
||||||
|
|
||||||
|
fmt.Println("主进程启动...")
|
||||||
|
|
||||||
|
cmd, err := startChildProcess()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("子进程启动失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SIG = 0
|
||||||
|
SIG_NAME := fmt.Sprintf("root%d", SIG)
|
||||||
|
if return_value := IsIpset(SIG_NAME); return_value == 1 {
|
||||||
|
createIPSet(SIG_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动抓包
|
||||||
|
go startPacketCapture()
|
||||||
|
|
||||||
|
// 启动IP地域判断,管理
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
ipMutex.Lock() // 锁定互斥锁
|
||||||
|
if ipList.Len() > 0 { // 链表不为空
|
||||||
|
e1 := ipList.Front() // 获取链表第一个元素
|
||||||
|
//fmt.Println(e1.Value) // 输出第一个元素
|
||||||
|
|
||||||
|
region, _ := ip2region(e1.Value.(net.IP).String())
|
||||||
|
log.Printf("当前 Ipset 链 %s\n", SIG_NAME)
|
||||||
|
|
||||||
|
if !strings.Contains(region, "中国") && !strings.Contains(region, "内网") {
|
||||||
|
if position, err := curl_(e1.Value.(net.IP).String()); err != nil { //判断地域
|
||||||
|
log.Printf("获取Ip地域出错: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("curl_ %s 位置 %s\n", e1.Value.(net.IP).String(), position) // 打印地域
|
||||||
|
AddIPSet(SIG_NAME, e1.Value.(net.IP).String()) // 添加 Ip 到 ipset 集合
|
||||||
|
|
||||||
|
ipList.Remove(e1) // 移除第一个元素
|
||||||
|
}
|
||||||
|
} else { // 这时是国内地址
|
||||||
|
ipList.Remove(e1)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Ip 链表长度:%d\n", ipList.Len()) // 链表长度
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
}
|
||||||
|
ipMutex.Unlock() // 链表解锁互斥锁
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 启动防火墙管理
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
if ipset_len, _ := NumIPSet(SIG_NAME); ipset_len >= 100 {
|
||||||
|
log.Printf("ipset %s 列表已满 %dd\n", SIG_NAME, ipset_len)
|
||||||
|
|
||||||
|
// 创建新的 ipset 集合
|
||||||
|
SIG++
|
||||||
|
|
||||||
|
if SIG >= MAX_SIG {
|
||||||
|
log.Printf("已创建 %s 个集合!!!", MAX_SIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
SIG_NAME = fmt.Sprintf("root%d", SIG)
|
||||||
|
if return_value := IsIpset(SIG_NAME); return_value == 1 {
|
||||||
|
createIPSet(SIG_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待信号并清理
|
||||||
|
waitForSignalAndCleanUp(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCmd() {
|
||||||
|
// 定义命令行标志
|
||||||
|
var instruction string
|
||||||
|
var help bool
|
||||||
|
interfaceName = flag.String("i", "", "指定要使用的网络接口")
|
||||||
|
flag.BoolVar(&listInterfaces, "l", false, "列出可用的网络接口")
|
||||||
|
filter = flag.String("f", "tcp", "指定 BPF 过滤器")
|
||||||
|
outputFile = flag.String("o", "", "保存捕获数据的输出文件(可选)")
|
||||||
|
flag.StringVar(&instruction, "s", "", "-s start 启动 Iptables 规则\n-s stop 停止 Iptables 规则")
|
||||||
|
flag.BoolVar(&help, "h", false, "")
|
||||||
|
flag.BoolVar(&help, "help", false, "display this message")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if help {
|
||||||
|
fmt.Printf(
|
||||||
|
"\t\tDenyip firewall\n" +
|
||||||
|
"\tVersion 0.1\n" +
|
||||||
|
"\tE-mail: aixiao@aixiao.me\n" +
|
||||||
|
"\tDate: 20250102\n")
|
||||||
|
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch instruction {
|
||||||
|
case "start":
|
||||||
|
fmt.Println("启动 Iptables 规则")
|
||||||
|
for i := 0; i < MAX_SIG; i++ {
|
||||||
|
_name := fmt.Sprintf("root%d", i)
|
||||||
|
iptables(_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
case "stop":
|
||||||
|
fmt.Println("停止 Iptables 规则")
|
||||||
|
os.Exit(0)
|
||||||
|
default:
|
||||||
|
log.Fatalf("未知的操作: %s. 请使用 'start' 或 'stop'.", instruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listInterfaces {
|
||||||
|
printAvailableInterfaces()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *interfaceName == "" {
|
||||||
|
log.Fatal("请使用 -i 标志指定网络接口,或者使用 -l 列出接口。")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
handleCmd()
|
||||||
|
|
||||||
|
// 守护进程模式
|
||||||
|
if *Daemon {
|
||||||
|
startDaemon()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子进程逻辑
|
||||||
|
if *child {
|
||||||
|
runChildProcess()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
runMainProcess()
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user