mxs před 1 rokem
rodič
revize
7b8f62d285
4 změnil soubory, kde provedl 349 přidání a 0 odebrání
  1. 6 0
      .gitignore
  2. 18 0
      src/config.json
  3. 140 0
      src/main.go
  4. 185 0
      src/util.go

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+pkg
+bin
+*.exe
+*.log
+*/bin
+.idea

+ 18 - 0
src/config.json

@@ -0,0 +1,18 @@
+{
+    "ports": [
+        8002,
+        8003,
+        8004
+    ],
+    "sendip": true,
+    "sendipaddr": "http://127.0.0.1:7011/proxy/msg",
+    "vpsname": "IP切换",
+    "interval": 20,
+    "sendinterval": 10,
+    "localudpaddr": ":12345",
+    "serveraddr": "127.0.0.1:801",
+    "checkspeedaddr": "spdata.jianyu360.com:12009",
+    "iptype": "2",
+	"downspeed":0.2,
+	"uploadspeed":0.05
+}

+ 140 - 0
src/main.go

@@ -0,0 +1,140 @@
+package main
+
+import (
+	"fmt"
+	mu "mfw/util"
+	"net"
+	"net/http"
+	"net/url"
+	qu "qfw/util"
+	"sync"
+	"time"
+)
+
+type Config struct {
+	Interval         int     `json:"interval"`
+	SendInterval     int     `json:"sendinterval"`
+	LocalUdpAddr     string  `json:"localudpaddr"`
+	ServerUdpAddr    string  `json:"serverudpaddr"`
+	ServerAddr       string  `json:"serveraddr"`
+	VpsName          string  `json:"vpsname"`
+	DownSpeedLimit   float64 `json:"downspeed"`
+	UploadSpeedLimit float64 `json:"uploadspeed"`
+	CheckSpeedAddr   string  `json:"checkspeedaddr"`
+	//代理监控
+	Ports      []int  `json:"ports"`      //需要检测的端口
+	SendIp     bool   `json:"sendip"`     //是否发送IP到代理中心
+	SendIpAddr string `json:"sendipaddr"` //信息推送地址
+	IpType     string `json:"iptype"`     //ip类型
+}
+
+type DownloaderItem struct {
+	Addr *net.UDPAddr
+	Code string
+}
+
+const MSG_TPL = "%s(D:%.4fM U:%.4fM)"
+
+var (
+	config    Config
+	cache     map[string]*DownloaderItem = make(map[string]*DownloaderItem)
+	cacheLock                            = &sync.Mutex{}
+	udpclient mu.UdpClient
+	client    *mu.Client
+
+	myoldip        string //本轮ip切换的最终结果
+	lifeTime       int64
+	available      bool //是否有效切换ip(ip改变且网速够快)
+	changing       bool //当前是否在切换ip
+	ischangedfirst bool
+)
+
+func init() {
+	qu.ReadConfig(&config)
+}
+
+func main() {
+	go timeTaskSendMsg() //IP代理中心推送消息
+	go func() {
+		time.Sleep(2 * time.Minute) //2分钟后开始切换IP
+		for {
+			runChangeIP()
+			time.Sleep(time.Duration(config.Interval) * time.Minute)
+		}
+	}()
+	client, _ = mu.StartClient(func(p *mu.Packet) {}, config.ServerAddr, config.VpsName, []int{})
+	udpclient = mu.UdpClient{Local: config.LocalUdpAddr, BufSize: 128}
+	udpclient.Listen(processUdpMsg)
+	fmt.Println("Udp服务监听:", config.LocalUdpAddr)
+	lock := make(chan bool)
+	<-lock
+}
+
+func runChangeIP() {
+	if changing { //防止同时切换IP
+		return
+	}
+	available = false //正在切换ip,设置ip已不可用
+	changing = true   //正在切换ip
+	fmt.Println("准备切换IP...")
+	//发布要换IP了
+	changeIP() //切换ip
+	available = true
+	changing = false                                              //ip切换完成
+	lifeTime = time.Now().Unix() + int64(config.Interval)*60 - 10 //ip有效时间(默认减少10秒)
+	ischangedfirst = true                                         //开启定时检测的标记
+	fmt.Println("完成切换IP...")
+	//ip切换成功,推送消息
+}
+
+func processUdpMsg(act byte, data []byte, ra *net.UDPAddr) {
+	switch act {
+	case mu.OP_JOIN:
+		cacheLock.Lock()
+		cache[ra.String()] = &DownloaderItem{
+			Addr: ra,
+			Code: string(data),
+		}
+		cacheLock.Unlock()
+		fmt.Println("加入", ra.String(), string(data))
+	}
+}
+
+//推送消息
+func timeTaskSendMsg() {
+	if config.SendIp {
+		for {
+			if !ischangedfirst { //服务启动,切换一次IP,再推送消息
+				time.Sleep(10 * time.Second)
+				continue
+			}
+			sPorts, fPorts := checkIpAndPort() //检测IP和端口可用性
+			//推送消息
+			value := url.Values{
+				"name":      []string{config.VpsName},              //vps服务器名称(唯一)
+				"ip":        []string{qu.SE.EncodeString(myoldip)}, //加密ip
+				"lifetime":  []string{fmt.Sprint(lifeTime)},        //ip有效期
+				"okport":    sPorts,                                //可用端口
+				"noport":    fPorts,                                //不可用端口
+				"available": []string{fmt.Sprint(available)},       //ip是否可用
+				"changing":  []string{fmt.Sprint(changing)},        //是否正在切换ip
+				"iptype":    []string{config.IpType},               //ip类型 0:普通vps; 1:阿里云; 2:linux_vps
+			}
+			for i := 1; i <= 3; i++ {
+				client := &http.Client{
+					Timeout: 3 * time.Second, // 设置超时时间为5秒
+				}
+				// 发送POST请求
+				resp, err := client.PostForm(config.SendIpAddr, value)
+				if err != nil {
+					fmt.Println("POST请求失败:", err)
+					continue
+				}
+				resp.Body.Close()
+				break
+			}
+			qu.Debug("---send success---", time.Now().Unix())
+			time.Sleep(time.Duration(config.SendInterval) * time.Second)
+		}
+	}
+}

+ 185 - 0
src/util.go

@@ -0,0 +1,185 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	mu "mfw/util"
+	"net"
+	"net/http"
+	"os/exec"
+	"strings"
+	"time"
+)
+
+//检测端口是否可用
+func checkPort() (sPorts, fPorts []string) {
+	output, err := exec.Command("netstat", "-tuln").Output()
+	if err != nil {
+		fmt.Println("执行netstat命令时出错:", err)
+		return
+	}
+	lines := strings.Split(string(output), "\n")
+	for _, port := range config.Ports {
+		portOk := false
+		for _, line := range lines {
+			if line == "" {
+				continue
+			}
+			fields := strings.Fields(line)
+			if len(fields) >= 4 && strings.Contains(fields[3], fmt.Sprint(port)) { //端口存活
+				portOk = true
+			}
+		}
+		if portOk {
+			sPorts = append(sPorts, fmt.Sprint(port))
+		} else {
+			fPorts = append(fPorts, fmt.Sprint(port))
+		}
+	}
+	return
+}
+
+//测速
+func checkSpeed(addr string, updatename bool) (float64, float64) {
+	conn, err := net.Dial("tcp", addr)
+	if err != nil {
+		return 0, 0
+	}
+	defer conn.Close()
+	t1 := time.Now()
+	buf := make([]byte, 1024)
+	for i := 0; i < 50; i++ {
+		io.ReadFull(conn, buf)
+	}
+	dd := time.Since(t1)
+	t1 = time.Now()
+	for i := 0; i < 50; i++ {
+		conn.Write(buf)
+	}
+	for {
+		_, err := conn.Write(buf)
+		if err != nil {
+			break
+		}
+		time.Sleep(10 * time.Millisecond)
+	}
+	ud := time.Since(t1)
+	dp, up := float64(0.05)/dd.Seconds(), float64(0.05)/ud.Seconds()
+	msg := fmt.Sprintf(MSG_TPL, config.VpsName, float64(0.05)/dd.Seconds(), float64(0.05)/ud.Seconds())
+	fmt.Println("网速测试,", msg)
+	if updatename {
+		client.WriteObj("", "", mu.EVENT_UPDATE_MYNAME, mu.SENDTO_TYPE_P2P, []byte(msg))
+	}
+	return dp, up
+}
+
+//切换IP
+func changeIP() {
+	sendMessageBeforChangeIp()
+	defer sendMessageAfterChangeIp()
+	for {
+		fmt.Println("start change ip...")
+		exec.Command("pppoe-stop").Run()
+		exec.Command("pppoe-start").Run()
+		fmt.Println("wait...")
+		time.Sleep(10 * time.Second) //避免换IP后无法获取IP
+		newIp := getMyIp()           //获取新IP
+		fmt.Println("newIp:", newIp)
+		if newIp != "" && newIp != myoldip {
+			fmt.Println("公网IP由", myoldip, "换为", newIp)
+			ds, us := checkSpeed(config.CheckSpeedAddr, true)                 //测速
+			if ds >= config.DownSpeedLimit && us >= config.UploadSpeedLimit { //网速达标
+				fmt.Println("IP测速通过", newIp)
+				myoldip = newIp
+				break
+			}
+		}
+		time.Sleep(5 * time.Second)
+	}
+}
+func sendMessageBeforChangeIp() {
+	fmt.Println("切换IP前发送消息...")
+	bs := []byte{}
+	for _, v := range cache {
+		udpclient.WriteUdp([]byte{}, mu.OP_WILLCHANGEIP, v.Addr) //udp通知本服务器的下载器节点要切换ip,下掉所有服务
+		bs = append(bs, []byte(v.Code)...)
+	}
+	client.WriteObj("", "", mu.SERVICE_DOWNLOAD_DELETE_NODE, mu.SENDTO_TYPE_ALL_RECIVER, bs)
+}
+
+func sendMessageAfterChangeIp() {
+	fmt.Println("切换IP后发送消息...")
+	cacheLock.Lock()
+	for k, v := range cache { //告诉下载器重连
+		udpclient.WriteUdp([]byte{}, mu.OP_NEWCLIENT, v.Addr)
+		delete(cache, k)
+	}
+	cacheLock.Unlock()
+	go func() {
+		for i := 1; i <= 5; i++ {
+			time.Sleep(5 * time.Second)
+			if len(cache) > 0 {
+				bs := []byte{}
+				for k, v := range cache {
+					fmt.Println("本机下载器机器码:", k, v.Code)
+					bs = append(bs, []byte(v.Code)...)
+				}
+				client.WriteObj("", "", mu.SERVICE_DOWNLOAD_APPEND_NODE, mu.SENDTO_TYPE_ALL_RECIVER, bs)
+				return
+			}
+		}
+	}()
+}
+
+//获取IP
+func getMyIp() (ip string) {
+	for i := 1; i <= 3; i++ {
+		client := &http.Client{
+			Timeout: 5 * time.Second, // 设置超时时间为5秒
+		}
+		resp, err := client.Get("https://api.ipify.org") // 使用ipify.org提供的IP查询API
+		if err != nil {
+			fmt.Println("获取公网IP失败:", err)
+			continue
+		}
+		ipByte, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			fmt.Println("获取公网IP失败:", err)
+			continue
+		}
+		resp.Body.Close()
+		ip = string(ipByte)
+		return
+	}
+	return
+}
+
+func checkIpAndPort() (sPorts, fPorts []string) {
+	//检测IP
+	if !changing { //正在切换IP,不做测速检测
+		fmt.Println("测速...")
+		ds, us := checkSpeed(config.CheckSpeedAddr, false)              //测速
+		if ds < config.DownSpeedLimit || us < config.UploadSpeedLimit { //网速不达标,切换IP
+			go runChangeIP() //重新切换IP
+		}
+	}
+	fmt.Println("检测端口...")
+	//检测端口
+	sPorts, fPorts = checkPort()
+	if len(fPorts) == len(config.Ports) { //端口都不可用,重启端口
+		go reStartPorts() //本轮检测重启,不可用
+	}
+	time.Sleep(1 * time.Second)
+	return
+}
+
+//端口重启
+func reStartPorts() {
+	tmpPort := []string{}
+	for _, port := range config.Ports {
+		tmpPort = append(tmpPort, ":"+fmt.Sprint(port))
+	}
+	exec.Command("/bin/sh", "-c", `kill -9 $(pidof "proxy")`).Run()                                                         //杀proxy
+	exec.Command("proxy", "socks", "-t", "tcp", "-p", strings.Join(tmpPort, ","), "--forever", "--nolog", "--daemon").Run() //启动proxy
+}