update
This commit is contained in:
469
proxy/duosk5.go
Normal file
469
proxy/duosk5.go
Normal file
@@ -0,0 +1,469 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// 颜色常量(ANSI转义码)
|
||||
ColorReset = "\033[0m"
|
||||
ColorRed = "\033[31m"
|
||||
ColorGreen = "\033[32m"
|
||||
ColorYellow = "\033[33m"
|
||||
ColorCyan = "\033[36m"
|
||||
|
||||
// 构建:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sk5 main.go
|
||||
// 脚本过期时间以及其他变量
|
||||
EXPIRE_DATE = "2025-06-08 02:01:01"
|
||||
CONFIG_FILE = "/usr/local/etc/xray/config.json"
|
||||
SOCKS_FILE = "/home/socks.txt"
|
||||
XRAY_INSTALL_URL = "https://github.com/XTLS/Xray-install/raw/main/install-release.sh"
|
||||
XRAY_VERSION = "v1.8.4"
|
||||
START_PORT = 10001
|
||||
)
|
||||
|
||||
// 彩色打印函数
|
||||
func colorPrint(colorCode, format string, a ...interface{}) {
|
||||
fmt.Printf(colorCode+format+ColorReset+"\n", a...)
|
||||
}
|
||||
|
||||
// XrayConfig represents the Xray configuration structure
|
||||
type XrayConfig struct {
|
||||
Inbounds []Inbound `json:"inbounds"`
|
||||
Outbounds []Outbound `json:"outbounds"`
|
||||
Routing Routing `json:"routing"`
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
Port int `json:"port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Settings InboundSettings `json:"settings"`
|
||||
StreamSettings StreamSettings `json:"streamSettings"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type InboundSettings struct {
|
||||
Auth string `json:"auth"`
|
||||
Accounts []Account `json:"accounts"`
|
||||
UDP bool `json:"udp"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
User string `json:"user"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
|
||||
type StreamSettings struct {
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Settings interface{} `json:"settings"`
|
||||
SendThrough string `json:"sendThrough"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type Routing struct {
|
||||
Rules []Rule `json:"rules"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Type string `json:"type"`
|
||||
InboundTag []string `json:"inboundTag"`
|
||||
OutboundTag string `json:"outboundTag"`
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
IP string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// generateRandomString generates a random string of specified length
|
||||
func generateRandomString(length int) string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
b := make([]byte, length)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := range b {
|
||||
b[i] = charset[b[i]%byte(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// checkExpiration checks if the script has expired
|
||||
func checkExpiration() error {
|
||||
colorPrint(ColorCyan, "开始运行...")
|
||||
|
||||
// Get timestamp from cloudflare
|
||||
resp, err := http.Get("https://www.cloudflare.com/cdn-cgi/trace")
|
||||
if err != nil {
|
||||
return fmt.Errorf("网络错误")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取响应失败")
|
||||
}
|
||||
|
||||
// Extract timestamp
|
||||
re := regexp.MustCompile(`ts=(\d+)`)
|
||||
matches := re.FindStringSubmatch(string(body))
|
||||
if len(matches) < 2 {
|
||||
return fmt.Errorf("无法解析时间")
|
||||
}
|
||||
|
||||
timestamp, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("时间转换失败")
|
||||
}
|
||||
|
||||
// Convert to Beijing time
|
||||
currentTime := time.Unix(timestamp, 0).In(time.FixedZone("CST", 8*3600))
|
||||
expireTime, _ := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600))
|
||||
|
||||
if currentTime.After(expireTime) {
|
||||
return fmt.Errorf("当前脚本已过期,请联系作者")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// commandExists checks if a command exists in PATH
|
||||
func commandExists(cmd string) bool {
|
||||
_, err := exec.LookPath(cmd)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// installJQ installs jq if not present
|
||||
func installJQ() error {
|
||||
if commandExists("jq") {
|
||||
colorPrint(ColorGreen, "jq 已安装")
|
||||
return nil
|
||||
}
|
||||
|
||||
colorPrint(ColorYellow, "jq 未安装,正在安装 jq...")
|
||||
|
||||
// Detect OS
|
||||
if _, err := os.Stat("/etc/debian_version"); err == nil {
|
||||
// Debian/Ubuntu
|
||||
cmd := exec.Command("bash", "-c", "apt update && apt install -yq jq")
|
||||
return cmd.Run()
|
||||
} else if _, err := os.Stat("/etc/redhat-release"); err == nil {
|
||||
// RHEL/CentOS
|
||||
cmd := exec.Command("yum", "install", "-y", "epel-release", "jq")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
return fmt.Errorf("无法确定系统发行版,请手动安装 jq")
|
||||
}
|
||||
|
||||
// installXray installs Xray if not present
|
||||
func installXray() error {
|
||||
if commandExists("xray") {
|
||||
colorPrint(ColorGreen, "Xray 已安装")
|
||||
return nil
|
||||
}
|
||||
|
||||
colorPrint(ColorYellow, "Xray 未安装,正在安装 Xray...")
|
||||
|
||||
cmd := exec.Command("bash", "-c", fmt.Sprintf("curl -L %s | bash -s install --version %s", XRAY_INSTALL_URL, XRAY_VERSION))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Xray 安装失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 安装完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPublicIPv4 gets all public IPv4 addresses
|
||||
func getPublicIPv4() ([]string, error) {
|
||||
var publicIPs []string
|
||||
|
||||
// Get all network interfaces
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
ip := ipNet.IP.String()
|
||||
// Check if it's a public IP
|
||||
if isPublicIP(ip) {
|
||||
publicIPs = append(publicIPs, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return publicIPs, nil
|
||||
}
|
||||
|
||||
// isPublicIP checks if an IP is public
|
||||
func isPublicIP(ip string) bool {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for private IP ranges
|
||||
privateRanges := []string{
|
||||
"127.0.0.0/8", // loopback
|
||||
"10.0.0.0/8", // private
|
||||
"172.16.0.0/12", // private
|
||||
"192.168.0.0/16", // private
|
||||
"169.254.0.0/16", // link-local
|
||||
}
|
||||
|
||||
for _, cidr := range privateRanges {
|
||||
_, network, _ := net.ParseCIDR(cidr)
|
||||
if network.Contains(parsedIP) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ensureSocksFileExists creates socks.txt if it doesn't exist
|
||||
func ensureSocksFileExists() error {
|
||||
if _, err := os.Stat(SOCKS_FILE); os.IsNotExist(err) {
|
||||
colorPrint(ColorYellow, "socks.txt 文件不存在,正在创建...")
|
||||
file, err := os.Create(SOCKS_FILE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveNodeInfo saves node information to file and prints it
|
||||
func saveNodeInfo(node NodeInfo) error {
|
||||
// Print node info with colors
|
||||
fmt.Printf(" IP: %s%s%s 端口: %s%d%s 用户名: %s%s%s 密码: %s%s%s\n",
|
||||
ColorGreen, node.IP, ColorReset,
|
||||
ColorGreen, node.Port, ColorReset,
|
||||
ColorGreen, node.Username, ColorReset,
|
||||
ColorGreen, node.Password, ColorReset)
|
||||
|
||||
// Save to file
|
||||
file, err := os.OpenFile(SOCKS_FILE, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = fmt.Fprintf(file, "%s %d %s %s\n", node.IP, node.Port, node.Username, node.Password)
|
||||
return err
|
||||
}
|
||||
|
||||
// configureXray configures Xray with multiple IPs
|
||||
func configureXray() error {
|
||||
publicIPs, err := getPublicIPv4()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取公网IP失败: %v", err)
|
||||
}
|
||||
|
||||
if len(publicIPs) == 0 {
|
||||
return fmt.Errorf("未找到额外IP地址")
|
||||
}
|
||||
|
||||
colorPrint(ColorCyan, "找到的公网 IPv4 地址: %v", publicIPs)
|
||||
|
||||
// Create initial config
|
||||
config := XrayConfig{
|
||||
Inbounds: []Inbound{},
|
||||
Outbounds: []Outbound{},
|
||||
Routing: Routing{
|
||||
Rules: []Rule{},
|
||||
},
|
||||
}
|
||||
|
||||
// Configure each IP
|
||||
port := START_PORT
|
||||
for _, ip := range publicIPs {
|
||||
colorPrint(ColorCyan, "正在配置 IP: %s 端口: %d", ip, port)
|
||||
|
||||
username := generateRandomString(8)
|
||||
password := generateRandomString(8)
|
||||
|
||||
// Create inbound
|
||||
inbound := Inbound{
|
||||
Port: port,
|
||||
Protocol: "socks",
|
||||
Settings: InboundSettings{
|
||||
Auth: "password",
|
||||
Accounts: []Account{
|
||||
{User: username, Pass: password},
|
||||
},
|
||||
UDP: true,
|
||||
IP: "0.0.0.0",
|
||||
},
|
||||
StreamSettings: StreamSettings{
|
||||
Network: "tcp",
|
||||
},
|
||||
Tag: fmt.Sprintf("in-%d", port),
|
||||
}
|
||||
|
||||
// Create outbound
|
||||
outbound := Outbound{
|
||||
Protocol: "freedom",
|
||||
Settings: map[string]interface{}{},
|
||||
SendThrough: ip,
|
||||
Tag: fmt.Sprintf("out-%d", port),
|
||||
}
|
||||
|
||||
// Create routing rule
|
||||
rule := Rule{
|
||||
Type: "field",
|
||||
InboundTag: []string{fmt.Sprintf("in-%d", port)},
|
||||
OutboundTag: fmt.Sprintf("out-%d", port),
|
||||
}
|
||||
|
||||
config.Inbounds = append(config.Inbounds, inbound)
|
||||
config.Outbounds = append(config.Outbounds, outbound)
|
||||
config.Routing.Rules = append(config.Routing.Rules, rule)
|
||||
|
||||
// Save node info
|
||||
node := NodeInfo{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if err := saveNodeInfo(node); err != nil {
|
||||
return fmt.Errorf("保存节点信息失败: %v", err)
|
||||
}
|
||||
|
||||
port++
|
||||
}
|
||||
|
||||
// Write config file
|
||||
configData, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(CONFIG_FILE, configData, 0644); err != nil {
|
||||
return fmt.Errorf("写入配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 配置完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// restartXray restarts the Xray service
|
||||
func restartXray() error {
|
||||
colorPrint(ColorCyan, "正在重启 Xray 服务...")
|
||||
|
||||
// Restart service
|
||||
cmd := exec.Command("systemctl", "restart", "xray")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Xray 服务重启失败: %v", err)
|
||||
}
|
||||
|
||||
// Enable service
|
||||
cmd = exec.Command("systemctl", "enable", "xray")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("启用 Xray 服务失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 服务已重启")
|
||||
return nil
|
||||
}
|
||||
|
||||
// readUserInput reads user input for confirmation
|
||||
func readUserInput(prompt string) string {
|
||||
fmt.Print(prompt)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ := reader.ReadString('\n')
|
||||
return strings.TrimSpace(input)
|
||||
}
|
||||
|
||||
func main() {
|
||||
colorPrint(ColorCyan, "站群多IP源进源出sk5协议一键脚本")
|
||||
colorPrint(ColorCyan, "当前为测试版,可以联系作者获取源码")
|
||||
expireTime, err := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600))
|
||||
if err == nil {
|
||||
expireStr := fmt.Sprintf("%d年%d月%d日%d点%d分%d秒",
|
||||
expireTime.Year(),
|
||||
expireTime.Month(),
|
||||
expireTime.Day(),
|
||||
expireTime.Hour(),
|
||||
expireTime.Minute(),
|
||||
expireTime.Second())
|
||||
colorPrint(ColorCyan, "脚本过期时间: %s", expireStr)
|
||||
} else {
|
||||
colorPrint(ColorYellow, "脚本过期时间解析失败")
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Check expiration
|
||||
if err := checkExpiration(); err != nil {
|
||||
colorPrint(ColorRed, "错误: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure socks file exists
|
||||
if err := ensureSocksFileExists(); err != nil {
|
||||
colorPrint(ColorRed, "创建socks文件失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Install jq
|
||||
if err := installJQ(); err != nil {
|
||||
colorPrint(ColorRed, "安装jq失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Install Xray
|
||||
if err := installXray(); err != nil {
|
||||
colorPrint(ColorRed, "安装Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Configure Xray
|
||||
if err := configureXray(); err != nil {
|
||||
colorPrint(ColorRed, "配置Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Restart Xray
|
||||
if err := restartXray(); err != nil {
|
||||
colorPrint(ColorRed, "重启Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "部署完成,所有节点信息已保存到 %s", SOCKS_FILE)
|
||||
}
|
||||
Reference in New Issue
Block a user