mirror of https://github.com/dnomd343/XProxy.git
dnomd343
2 years ago
16 changed files with 515 additions and 402 deletions
@ -0,0 +1,25 @@ |
|||||
|
package asset |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
) |
||||
|
|
||||
|
func extractGeoFile(archivePath string, geoFile string, targetDir string) { |
||||
|
if common.IsFileExist(targetDir + "/" + geoFile) { |
||||
|
log.Debugf("Asset %s exist -> skip extract", geoFile) |
||||
|
return |
||||
|
} |
||||
|
log.Infof("Extract asset file -> %s", targetDir+"/"+geoFile) |
||||
|
common.RunCommand("tar", "xvf", archivePath, "./"+geoFile, "-C", targetDir) |
||||
|
} |
||||
|
|
||||
|
func LoadGeoIp(assetFile string, assetDir string) { |
||||
|
common.CreateFolder(assetDir) |
||||
|
extractGeoFile(assetFile, "geoip.dat", assetDir) |
||||
|
} |
||||
|
|
||||
|
func LoadGeoSite(assetFile string, assetDir string) { |
||||
|
common.CreateFolder(assetDir) |
||||
|
extractGeoFile(assetFile, "geosite.dat", assetDir) |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package asset |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
) |
||||
|
|
||||
|
func UpdateAssets(urls map[string]string, assetDir string) { |
||||
|
if len(urls) != 0 { |
||||
|
log.Info("Start update assets") |
||||
|
for file, url := range urls { |
||||
|
common.DownloadFile(url, assetDir+"/"+file) |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
package common |
||||
|
|
||||
|
import ( |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
func CreateFolder(folderPath string) { |
||||
|
log.Debugf("Create folder -> %s", folderPath) |
||||
|
err := os.MkdirAll(folderPath, 0755) |
||||
|
if err != nil { |
||||
|
log.Panicf("Failed to create folder -> %s", folderPath) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func IsFileExist(filePath string) bool { |
||||
|
s, err := os.Stat(filePath) |
||||
|
if err != nil { // file or folder not exist
|
||||
|
return false |
||||
|
} |
||||
|
return !s.IsDir() |
||||
|
} |
||||
|
|
||||
|
func WriteFile(filePath string, content string, overwrite bool) { |
||||
|
if !overwrite && IsFileExist(filePath) { // file exist and don't overwrite
|
||||
|
log.Debugf("File `%s` exist -> skip write", filePath) |
||||
|
return |
||||
|
} |
||||
|
log.Debugf("Write file `%s` -> \n%s", filePath, content) |
||||
|
err := os.WriteFile(filePath, []byte(content), 0644) |
||||
|
if err != nil { |
||||
|
log.Panicf("Failed to write `%s` -> %v", filePath, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func ListFiles(folderPath string, suffix string) []string { |
||||
|
var fileList []string |
||||
|
files, err := ioutil.ReadDir(folderPath) |
||||
|
if err != nil { |
||||
|
log.Panicf("Failed to list folder -> %s", folderPath) |
||||
|
} |
||||
|
for _, file := range files { |
||||
|
if strings.HasSuffix(file.Name(), suffix) { |
||||
|
fileList = append(fileList, file.Name()) |
||||
|
} |
||||
|
} |
||||
|
return fileList |
||||
|
} |
||||
|
|
||||
|
func CopyFile(source string, target string) { |
||||
|
log.Infof("Copy file `%s` => `%s`", source, target) |
||||
|
if IsFileExist(target) { |
||||
|
log.Warningf("File `%s` will be overrided", target) |
||||
|
} |
||||
|
srcFile, err := os.Open(source) |
||||
|
if err != nil { |
||||
|
log.Panicf("Failed to open file -> %s", source) |
||||
|
} |
||||
|
dstFile, err := os.OpenFile(target, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) |
||||
|
if err != nil { |
||||
|
_ = srcFile.Close() |
||||
|
log.Panicf("Failed to open file -> %s", target) |
||||
|
} |
||||
|
_, err = io.Copy(dstFile, srcFile) |
||||
|
_ = srcFile.Close() |
||||
|
_ = dstFile.Close() |
||||
|
if err != nil { |
||||
|
log.Panicf("Failed to copy from `%s` to `%s`", source, target) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func DownloadFile(url string, file string) { |
||||
|
log.Debugf("File download `%s` => `%s`", url, file) |
||||
|
resp, err := http.Get(url) |
||||
|
if err != nil { |
||||
|
log.Errorf("Download `%s` error -> %v", url, err) |
||||
|
return |
||||
|
} |
||||
|
out, err := os.OpenFile(file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) |
||||
|
if err != nil { |
||||
|
_ = resp.Body.Close() |
||||
|
log.Panicf("Open `%s` error -> %v", file, err) |
||||
|
} |
||||
|
_, err = io.Copy(out, resp.Body) |
||||
|
_ = resp.Body.Close() |
||||
|
if err != nil { |
||||
|
log.Panicf("File `%s` save error -> %v", file, err) |
||||
|
} |
||||
|
log.Infof("Download success `%s` => `%s`", url, file) |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
package common |
||||
|
|
||||
|
import ( |
||||
|
"net" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
func isIP(ipAddr string, isCidr bool) bool { |
||||
|
if !isCidr { |
||||
|
return net.ParseIP(ipAddr) != nil |
||||
|
} |
||||
|
_, _, err := net.ParseCIDR(ipAddr) |
||||
|
return err == nil |
||||
|
} |
||||
|
|
||||
|
func IsIPv4(ipAddr string, isCidr bool) bool { |
||||
|
return isIP(ipAddr, isCidr) && strings.Contains(ipAddr, ".") |
||||
|
} |
||||
|
|
||||
|
func IsIPv6(ipAddr string, isCidr bool) bool { |
||||
|
return isIP(ipAddr, isCidr) && strings.Contains(ipAddr, ":") |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package common |
||||
|
|
||||
|
import ( |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
"os/exec" |
||||
|
"syscall" |
||||
|
) |
||||
|
|
||||
|
func RunCommand(command ...string) (int, string) { |
||||
|
log.Debugf("Running system command -> %v", command) |
||||
|
process := exec.Command(command[0], command[1:]...) |
||||
|
output, _ := process.CombinedOutput() |
||||
|
log.Debugf("Command %v -> \n%s", command, string(output)) |
||||
|
code := process.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
||||
|
if code != 0 { |
||||
|
log.Warningf("Command %v return code %d", command, code) |
||||
|
} |
||||
|
return code, string(output) |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package network |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
) |
||||
|
|
||||
|
func loadDns(dnsServer []string) { |
||||
|
if len(dnsServer) == 0 { |
||||
|
log.Info("Using system DNS server") |
||||
|
return |
||||
|
} |
||||
|
log.Infof("Setting up DNS server -> %v", dnsServer) |
||||
|
dnsConfig := "" |
||||
|
for _, address := range dnsServer { |
||||
|
dnsConfig += "nameserver " + address + "\n" |
||||
|
} |
||||
|
common.WriteFile("/etc/resolv.conf", dnsConfig, true) |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package network |
||||
|
|
||||
|
type Config struct { |
||||
|
IPv4 struct { |
||||
|
Address string |
||||
|
Gateway string |
||||
|
RouteTable int |
||||
|
TProxyPort int |
||||
|
} |
||||
|
V4RouteTable int |
||||
|
V6RouteTable int |
||||
|
V4TProxyPort int |
||||
|
V6TProxyPort int |
||||
|
} |
||||
|
|
||||
|
func Load() { |
||||
|
|
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package network |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
"regexp" |
||||
|
) |
||||
|
|
||||
|
func getV4Cidr() []string { |
||||
|
var v4Cidr []string |
||||
|
_, output := common.RunCommand("ip", "-4", "addr") |
||||
|
for _, temp := range regexp.MustCompile(`inet (\S+)`).FindAllStringSubmatch(output, -1) { |
||||
|
v4Cidr = append(v4Cidr, temp[1]) |
||||
|
} |
||||
|
return v4Cidr |
||||
|
} |
||||
|
|
||||
|
func getV6Cidr() []string { |
||||
|
var v6Cidr []string |
||||
|
_, output := common.RunCommand("ip", "-6", "addr") |
||||
|
for _, temp := range regexp.MustCompile(`inet6 (\S+)`).FindAllStringSubmatch(output, -1) { |
||||
|
v6Cidr = append(v6Cidr, temp[1]) |
||||
|
} |
||||
|
return v6Cidr |
||||
|
} |
||||
|
|
||||
|
func loadNetwork(v4Address string, v4Gateway string, v6Address string, v6Gateway string) { |
||||
|
log.Info("Enabled IP forward") |
||||
|
common.RunCommand("sysctl", "-w", "net.ipv4.ip_forward=1") |
||||
|
common.RunCommand("sysctl", "-w", "net.ipv6.conf.all.forwarding=1") |
||||
|
|
||||
|
log.Info("Flush system IP configure") |
||||
|
common.RunCommand("ip", "link", "set", "eth0", "down") |
||||
|
common.RunCommand("ip", "-4", "addr", "flush", "dev", "eth0") |
||||
|
common.RunCommand("ip", "-6", "addr", "flush", "dev", "eth0") |
||||
|
common.RunCommand("ip", "link", "set", "eth0", "down") |
||||
|
|
||||
|
log.Info("Setting up system IP configure") |
||||
|
if v4Address != "" { |
||||
|
common.RunCommand("ip", "-4", "addr", "add", v4Address, "dev", "eth0") |
||||
|
} |
||||
|
if v4Gateway != "" { |
||||
|
common.RunCommand("ip", "-4", "route", "add", "default", "via", v4Gateway) |
||||
|
} |
||||
|
if v6Address != "" { |
||||
|
common.RunCommand("ip", "-6", "addr", "add", v6Address, "dev", "eth0") |
||||
|
} |
||||
|
if v6Gateway != "" { |
||||
|
common.RunCommand("ip", "-6", "route", "add", "default", "via", v6Gateway) |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package network |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
type tproxyConfig struct { |
||||
|
routeTable int |
||||
|
tproxyPort int |
||||
|
bypassCidr []string |
||||
|
} |
||||
|
|
||||
|
func loadV4TProxy(config tproxyConfig) { |
||||
|
log.Info("Setting up TProxy of IPv4") |
||||
|
tableNum := strconv.Itoa(config.routeTable) |
||||
|
common.RunCommand("ip", "-4", "rule", "add", "fwmark", "1", "table", tableNum) |
||||
|
common.RunCommand("ip", "-4", "route", "add", "local", "0.0.0.0/0", "dev", "lo", "table", tableNum) |
||||
|
common.RunCommand("iptables", "-t", "mangle", "-N", "XPROXY") |
||||
|
log.Infof("Setting up IPv4 bypass CIDR -> %v", config.bypassCidr) |
||||
|
for _, cidr := range config.bypassCidr { |
||||
|
common.RunCommand("iptables", "-t", "mangle", "-A", "XPROXY", "-d", cidr, "-j", "RETURN") |
||||
|
} |
||||
|
common.RunCommand("iptables", "-t", "mangle", "-A", "XPROXY", |
||||
|
"-p", "tcp", "-j", "TPROXY", "--on-port", strconv.Itoa(config.tproxyPort), "--tproxy-mark", "1") |
||||
|
common.RunCommand("iptables", "-t", "mangle", "-A", "XPROXY", |
||||
|
"-p", "udp", "-j", "TPROXY", "--on-port", strconv.Itoa(config.tproxyPort), "--tproxy-mark", "1") |
||||
|
common.RunCommand("iptables", "-t", "mangle", "-A", "PREROUTING", "-j", "XPROXY") |
||||
|
} |
||||
|
|
||||
|
func loadV6TProxy(config tproxyConfig) { |
||||
|
log.Info("Setting up TProxy of IPv6") |
||||
|
tableNum := strconv.Itoa(config.routeTable) |
||||
|
common.RunCommand("ip", "-6", "rule", "add", "fwmark", "1", "table", tableNum) |
||||
|
common.RunCommand("ip", "-6", "route", "add", "local", "::/0", "dev", "lo", "table", tableNum) |
||||
|
common.RunCommand("ip6tables", "-t", "mangle", "-N", "XPROXY6") |
||||
|
log.Infof("Setting up IPv6 bypass CIDR -> %v", config.bypassCidr) |
||||
|
for _, cidr := range config.bypassCidr { |
||||
|
common.RunCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", "-d", cidr, "-j", "RETURN") |
||||
|
} |
||||
|
common.RunCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", |
||||
|
"-p", "tcp", "-j", "TPROXY", "--on-port", strconv.Itoa(config.tproxyPort), "--tproxy-mark", "1") |
||||
|
common.RunCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", |
||||
|
"-p", "udp", "-j", "TPROXY", "--on-port", strconv.Itoa(config.tproxyPort), "--tproxy-mark", "1") |
||||
|
common.RunCommand("ip6tables", "-t", "mangle", "-A", "PREROUTING", "-j", "XPROXY6") |
||||
|
} |
@ -0,0 +1,84 @@ |
|||||
|
package proxy |
||||
|
|
||||
|
var dnsConfig = `{ |
||||
|
"dns": { |
||||
|
"servers": [ |
||||
|
"localhost" |
||||
|
] |
||||
|
} |
||||
|
}` |
||||
|
|
||||
|
var routeConfig = `{ |
||||
|
"routing": { |
||||
|
"domainStrategy": "AsIs", |
||||
|
"rules": [ |
||||
|
{ |
||||
|
"type": "field", |
||||
|
"network": "tcp,udp", |
||||
|
"outboundTag": "node" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}` |
||||
|
|
||||
|
var outboundsConfig = `{ |
||||
|
"outbounds": [ |
||||
|
{ |
||||
|
"tag": "node", |
||||
|
"protocol": "freedom", |
||||
|
"settings": {} |
||||
|
} |
||||
|
] |
||||
|
}` |
||||
|
|
||||
|
func httpConfig(tag string, port int, sniff sniffObject) interface{} { |
||||
|
type empty struct{} |
||||
|
return inboundObject{ |
||||
|
Tag: tag, |
||||
|
Port: port, |
||||
|
Protocol: "http", |
||||
|
Settings: empty{}, |
||||
|
StreamSettings: empty{}, |
||||
|
Sniffing: sniff, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func socksConfig(tag string, port int, sniff sniffObject) interface{} { |
||||
|
type empty struct{} |
||||
|
type socksObject struct { |
||||
|
UDP bool `json:"udp"` |
||||
|
} |
||||
|
return inboundObject{ |
||||
|
Tag: tag, |
||||
|
Port: port, |
||||
|
Protocol: "socks", |
||||
|
Settings: socksObject{UDP: true}, |
||||
|
StreamSettings: empty{}, |
||||
|
Sniffing: sniff, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func tproxyConfig(tag string, port int, sniff sniffObject) interface{} { |
||||
|
type tproxyObject struct { |
||||
|
Network string `json:"network"` |
||||
|
FollowRedirect bool `json:"followRedirect"` |
||||
|
} |
||||
|
type tproxyStreamObject struct { |
||||
|
Sockopt struct { |
||||
|
Tproxy string `json:"tproxy"` |
||||
|
} `json:"sockopt"` |
||||
|
} |
||||
|
tproxyStream := tproxyStreamObject{} |
||||
|
tproxyStream.Sockopt.Tproxy = "tproxy" |
||||
|
return inboundObject{ |
||||
|
Tag: tag, |
||||
|
Port: port, |
||||
|
Protocol: "dokodemo-door", |
||||
|
Settings: tproxyObject{ |
||||
|
Network: "tcp,udp", |
||||
|
FollowRedirect: true, |
||||
|
}, |
||||
|
StreamSettings: tproxyStream, |
||||
|
Sniffing: sniff, |
||||
|
} |
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
package proxy |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/common" |
||||
|
"encoding/json" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
) |
||||
|
|
||||
|
func saveConfig(configDir string, caption string, content string, overwrite bool) { |
||||
|
filePath := configDir + "/" + caption + ".json" |
||||
|
common.WriteFile(filePath, content+"\n", overwrite) |
||||
|
} |
||||
|
|
||||
|
func jsonEncode(raw interface{}) string { |
||||
|
jsonOutput, _ := json.MarshalIndent(raw, "", " ") // json encode
|
||||
|
return string(jsonOutput) |
||||
|
} |
||||
|
|
||||
|
func loadLog(logLevel string, logDir string) string { |
||||
|
if logLevel != "debug" && logLevel != "info" && |
||||
|
logLevel != "warning" && logLevel != "error" && logLevel != "none" { |
||||
|
log.Warningf("Unknown log level -> %s", logLevel) |
||||
|
logLevel = "warning" // using `warning` as default
|
||||
|
} |
||||
|
return jsonEncode(logObject{ |
||||
|
Loglevel: logLevel, |
||||
|
Access: logDir + "/access.log", |
||||
|
Error: logDir + "/error.log", |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
func loadInbounds(config Config) string { |
||||
|
inbounds := inboundsObject{} |
||||
|
sniff := sniffObject{ |
||||
|
Enabled: config.Sniff, |
||||
|
RouteOnly: !config.Redirect, |
||||
|
DestOverride: []string{"http", "tls"}, |
||||
|
} |
||||
|
inbounds.Inbounds = append(inbounds.Inbounds, tproxyConfig("tproxy", config.V4TProxyPort, sniff)) |
||||
|
inbounds.Inbounds = append(inbounds.Inbounds, tproxyConfig("tproxy6", config.V6TProxyPort, sniff)) |
||||
|
for tag, port := range config.HttpInbounds { |
||||
|
inbounds.Inbounds = append(inbounds.Inbounds, httpConfig(tag, port, sniff)) |
||||
|
} |
||||
|
for tag, port := range config.SocksInbounds { |
||||
|
inbounds.Inbounds = append(inbounds.Inbounds, socksConfig(tag, port, sniff)) |
||||
|
} |
||||
|
for _, addon := range config.AddOnInbounds { |
||||
|
inbounds.Inbounds = append(inbounds.Inbounds, addon) |
||||
|
} |
||||
|
return jsonEncode(inbounds) |
||||
|
} |
||||
|
|
||||
|
func Load(configDir string, exposeDir string, config Config) { |
||||
|
common.CreateFolder(exposeDir + "/log") |
||||
|
common.CreateFolder(exposeDir + "/config") |
||||
|
common.CreateFolder(configDir) |
||||
|
saveConfig(exposeDir+"/config", "dns", dnsConfig, false) |
||||
|
saveConfig(exposeDir+"/config", "route", routeConfig, false) |
||||
|
saveConfig(exposeDir+"/config", "outbounds", outboundsConfig, false) |
||||
|
saveConfig(configDir, "inbounds", loadInbounds(config), true) |
||||
|
saveConfig(configDir, "log", loadLog(config.LogLevel, exposeDir+"/log"), true) |
||||
|
for _, configFile := range common.ListFiles(exposeDir+"/config", ".json") { |
||||
|
common.CopyFile(exposeDir+"/config/"+configFile, configDir+"/"+configFile) |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
package proxy |
||||
|
|
||||
|
type Config struct { |
||||
|
Sniff bool |
||||
|
Redirect bool |
||||
|
V4TProxyPort int |
||||
|
V6TProxyPort int |
||||
|
LogLevel string |
||||
|
HttpInbounds map[string]int |
||||
|
SocksInbounds map[string]int |
||||
|
AddOnInbounds []interface{} |
||||
|
} |
||||
|
|
||||
|
type logObject struct { |
||||
|
Loglevel string `json:"loglevel"` |
||||
|
Access string `json:"access"` |
||||
|
Error string `json:"error"` |
||||
|
} |
||||
|
|
||||
|
type inboundsObject struct { |
||||
|
Inbounds []interface{} `json:"inbounds"` |
||||
|
} |
||||
|
|
||||
|
type sniffObject struct { |
||||
|
Enabled bool `json:"enabled"` |
||||
|
RouteOnly bool `json:"routeOnly"` |
||||
|
DestOverride []string `json:"destOverride"` |
||||
|
} |
||||
|
|
||||
|
type inboundObject struct { |
||||
|
Tag string `json:"tag"` |
||||
|
Port int `json:"port"` |
||||
|
Protocol string `json:"protocol"` |
||||
|
Settings interface{} `json:"settings"` |
||||
|
StreamSettings interface{} `json:"streamSettings"` |
||||
|
Sniffing sniffObject `json:"sniffing"` |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"XProxy/cmd/network" |
||||
|
"fmt" |
||||
|
log "github.com/sirupsen/logrus" |
||||
|
) |
||||
|
|
||||
|
func main() { |
||||
|
log.SetLevel(log.DebugLevel) |
||||
|
|
||||
|
fmt.Println("xproxy start") |
||||
|
//common.CreateFolder("/tmp/test")
|
||||
|
//fmt.Println(common.IsFileExist("/tmp/1.jpg"))
|
||||
|
//fmt.Println(common.ListFiles("/xproxy/config", ".json"))
|
||||
|
net = network.Config{ |
||||
|
V4RouteTable: 12, |
||||
|
} |
||||
|
} |
@ -1,286 +0,0 @@ |
|||||
package main |
|
||||
|
|
||||
import ( |
|
||||
"encoding/json" |
|
||||
log "github.com/sirupsen/logrus" |
|
||||
"io" |
|
||||
"io/ioutil" |
|
||||
"net/http" |
|
||||
"os" |
|
||||
"os/exec" |
|
||||
"strings" |
|
||||
"syscall" |
|
||||
) |
|
||||
|
|
||||
var logConfig = `{ |
|
||||
"log": { |
|
||||
"loglevel": "${LEVEL}", |
|
||||
"access": "${DIR}/access.log", |
|
||||
"error": "${DIR}/error.log" |
|
||||
} |
|
||||
}` |
|
||||
|
|
||||
var dnsConfig = `{ |
|
||||
"dns": { |
|
||||
"servers": [ |
|
||||
"localhost" |
|
||||
] |
|
||||
} |
|
||||
}` |
|
||||
|
|
||||
var routeConfig = `{ |
|
||||
"routing": { |
|
||||
"domainStrategy": "AsIs", |
|
||||
"rules": [ |
|
||||
{ |
|
||||
"type": "field", |
|
||||
"network": "tcp,udp", |
|
||||
"outboundTag": "node" |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
}` |
|
||||
|
|
||||
var outboundsConfig = `{ |
|
||||
"outbounds": [ |
|
||||
{ |
|
||||
"tag": "node", |
|
||||
"protocol": "freedom", |
|
||||
"settings": {} |
|
||||
} |
|
||||
] |
|
||||
}` |
|
||||
|
|
||||
type inboundsSettings struct { |
|
||||
Inbounds []interface{} `json:"inbounds"` |
|
||||
} |
|
||||
|
|
||||
type sniffSettings struct { |
|
||||
Enabled bool `json:"enabled"` |
|
||||
RouteOnly bool `json:"routeOnly"` |
|
||||
DestOverride []string `json:"destOverride"` |
|
||||
} |
|
||||
|
|
||||
type inboundSettings struct { |
|
||||
Tag string `json:"tag"` |
|
||||
Port int `json:"port"` |
|
||||
Protocol string `json:"protocol"` |
|
||||
Settings interface{} `json:"settings"` |
|
||||
StreamSettings interface{} `json:"streamSettings"` |
|
||||
Sniffing sniffSettings `json:"sniffing"` |
|
||||
} |
|
||||
|
|
||||
func runCommand(command ...string) (int, string) { |
|
||||
log.Debugf("Running system command -> %v", command) |
|
||||
process := exec.Command(command[0], command[1:]...) |
|
||||
output, _ := process.CombinedOutput() |
|
||||
log.Debugf("Command %v -> \n%s", command, string(output)) |
|
||||
code := process.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
|
||||
if code != 0 { |
|
||||
log.Warningf("Command %v return code %d", command, code) |
|
||||
} |
|
||||
return code, string(output) |
|
||||
} |
|
||||
|
|
||||
func isFileExist(filePath string) bool { |
|
||||
s, err := os.Stat(filePath) |
|
||||
if err != nil { // file or folder not exist
|
|
||||
return false |
|
||||
} |
|
||||
return !s.IsDir() |
|
||||
} |
|
||||
|
|
||||
func createFolder(folderPath string) { |
|
||||
log.Debugf("Create folder -> %s", folderPath) |
|
||||
err := os.MkdirAll(folderPath, 0755) |
|
||||
if err != nil { |
|
||||
log.Panicf("Failed to create folder -> %s", folderPath) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func listFolder(folderPath string, suffix string) []string { |
|
||||
var fileList []string |
|
||||
files, err := ioutil.ReadDir(folderPath) |
|
||||
if err != nil { |
|
||||
log.Panicf("Failed to list folder -> %s", folderPath) |
|
||||
} |
|
||||
for _, file := range files { |
|
||||
if strings.HasSuffix(file.Name(), suffix) { |
|
||||
fileList = append(fileList, file.Name()) |
|
||||
} |
|
||||
} |
|
||||
return fileList |
|
||||
} |
|
||||
|
|
||||
func copyFile(source string, target string) { |
|
||||
log.Infof("Copy file `%s` => `%s`", source, target) |
|
||||
srcFile, err := os.Open(source) |
|
||||
if err != nil { |
|
||||
log.Panicf("Failed to open file -> %s", source) |
|
||||
} |
|
||||
dstFile, err := os.OpenFile(target, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) |
|
||||
if err != nil { |
|
||||
log.Panicf("Failed to open file -> %s", target) |
|
||||
} |
|
||||
_, err = io.Copy(dstFile, srcFile) |
|
||||
if err != nil { |
|
||||
log.Panicf("Failed to copy from `%s` to `%s`", source, target) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func downloadFile(url string, file string) bool { |
|
||||
log.Debugf("File download %s => %s", url, file) |
|
||||
resp, err := http.Get(url) |
|
||||
if err != nil { |
|
||||
log.Errorf("Download %s error -> %v", url, err) |
|
||||
return false |
|
||||
} |
|
||||
defer func(Body io.ReadCloser) { |
|
||||
_ = Body.Close() |
|
||||
}(resp.Body) |
|
||||
out, err := os.OpenFile(file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) |
|
||||
if err != nil { |
|
||||
_ = resp.Body.Close() |
|
||||
log.Panicf("Open %s error -> %v", file, err) |
|
||||
} |
|
||||
_, err = io.Copy(out, resp.Body) |
|
||||
_ = resp.Body.Close() |
|
||||
if err != nil { |
|
||||
log.Panicf("File %s save error -> %v", file, err) |
|
||||
} |
|
||||
log.Infof("Download success `%s` => `%s`", url, file) |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
func saveConfig(configDir string, caption string, content string, overwrite bool) { |
|
||||
filePath := configDir + "/" + caption + ".json" |
|
||||
if !overwrite && isFileExist(filePath) { // file exist and don't overwrite
|
|
||||
log.Debugf("Skip loading config -> %s", filePath) |
|
||||
return |
|
||||
} |
|
||||
log.Debugf("Loading %s -> \n%s", filePath, content) |
|
||||
err := os.WriteFile(filePath, []byte(content), 0644) |
|
||||
if err != nil { |
|
||||
log.Panicf("File %s -> %v", caption, err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadHttpConfig(tag string, port int, sniffObject sniffSettings) interface{} { |
|
||||
type empty struct{} |
|
||||
return inboundSettings{ |
|
||||
Tag: tag, |
|
||||
Port: port, |
|
||||
Protocol: "http", |
|
||||
Settings: empty{}, |
|
||||
StreamSettings: empty{}, |
|
||||
Sniffing: sniffObject, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadSocksConfig(tag string, port int, sniffObject sniffSettings) interface{} { |
|
||||
type empty struct{} |
|
||||
type socksSettings struct { |
|
||||
UDP bool `json:"udp"` |
|
||||
} |
|
||||
return inboundSettings{ |
|
||||
Tag: tag, |
|
||||
Port: port, |
|
||||
Protocol: "socks", |
|
||||
Settings: socksSettings{UDP: true}, |
|
||||
StreamSettings: empty{}, |
|
||||
Sniffing: sniffObject, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadTProxyConfig(tag string, port int, sniffObject sniffSettings) interface{} { |
|
||||
type tproxySettings struct { |
|
||||
Network string `json:"network"` |
|
||||
FollowRedirect bool `json:"followRedirect"` |
|
||||
} |
|
||||
type tproxyStreamSettings struct { |
|
||||
Sockopt struct { |
|
||||
Tproxy string `json:"tproxy"` |
|
||||
} `json:"sockopt"` |
|
||||
} |
|
||||
tproxyStream := tproxyStreamSettings{} |
|
||||
tproxyStream.Sockopt.Tproxy = "tproxy" |
|
||||
return inboundSettings{ |
|
||||
Tag: tag, |
|
||||
Port: port, |
|
||||
Protocol: "dokodemo-door", |
|
||||
Settings: tproxySettings{ |
|
||||
Network: "tcp,udp", |
|
||||
FollowRedirect: true, |
|
||||
}, |
|
||||
StreamSettings: tproxyStream, |
|
||||
Sniffing: sniffObject, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadProxy(configDir string, exposeDir string) { |
|
||||
createFolder(exposeDir + "/log") |
|
||||
createFolder(exposeDir + "/config") |
|
||||
createFolder(configDir) |
|
||||
saveConfig(exposeDir+"/config", "dns", dnsConfig+"\n", false) |
|
||||
saveConfig(exposeDir+"/config", "route", routeConfig+"\n", false) |
|
||||
saveConfig(exposeDir+"/config", "outbounds", outboundsConfig+"\n", false) |
|
||||
|
|
||||
logConfig = strings.ReplaceAll(logConfig, "${LEVEL}", logLevel) |
|
||||
logConfig = strings.ReplaceAll(logConfig, "${DIR}", exposeDir+"/log") |
|
||||
saveConfig(configDir, "log", logConfig+"\n", true) |
|
||||
|
|
||||
inbounds := inboundsSettings{} |
|
||||
sniff := sniffSettings{ |
|
||||
Enabled: enableSniff, |
|
||||
RouteOnly: !enableRedirect, |
|
||||
DestOverride: []string{"http", "tls"}, |
|
||||
} |
|
||||
inbounds.Inbounds = append(inbounds.Inbounds, loadTProxyConfig("tproxy", v4TProxyPort, sniff)) |
|
||||
inbounds.Inbounds = append(inbounds.Inbounds, loadTProxyConfig("tproxy6", v6TProxyPort, sniff)) |
|
||||
for tag, port := range httpInbounds { |
|
||||
inbounds.Inbounds = append(inbounds.Inbounds, loadHttpConfig(tag, port, sniff)) |
|
||||
} |
|
||||
for tag, port := range socksInbounds { |
|
||||
inbounds.Inbounds = append(inbounds.Inbounds, loadSocksConfig(tag, port, sniff)) |
|
||||
} |
|
||||
for _, addon := range addOnInbounds { |
|
||||
inbounds.Inbounds = append(inbounds.Inbounds, addon) |
|
||||
} |
|
||||
inboundsConfig, _ := json.MarshalIndent(inbounds, "", " ") // json encode
|
|
||||
saveConfig(configDir, "inbounds", string(inboundsConfig)+"\n", true) |
|
||||
|
|
||||
for _, configFile := range listFolder(exposeDir+"/config", ".json") { |
|
||||
if configFile == "log.json" || configFile == "inbounds.json" { |
|
||||
log.Warningf("Config file `%s` will be overrided", configFile) |
|
||||
} |
|
||||
copyFile(exposeDir+"/config/"+configFile, configDir+"/"+configFile) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func extractGeoFile(archivePath string, geoFile string, targetDir string) { |
|
||||
if isFileExist(targetDir + "/" + geoFile) { |
|
||||
log.Debugf("Asset %s exist -> skip extract", geoFile) |
|
||||
return |
|
||||
} |
|
||||
log.Infof("Extract asset file -> %s", targetDir+"/"+geoFile) |
|
||||
runCommand("tar", "xvf", archivePath, "./"+geoFile, "-C", targetDir) |
|
||||
} |
|
||||
|
|
||||
func loadGeoIp(assetDir string) { |
|
||||
createFolder(assetDir) |
|
||||
extractGeoFile(assetFile, "geoip.dat", assetDir) |
|
||||
} |
|
||||
|
|
||||
func loadGeoSite(assetDir string) { |
|
||||
createFolder(assetDir) |
|
||||
extractGeoFile(assetFile, "geosite.dat", assetDir) |
|
||||
} |
|
||||
|
|
||||
func updateAssets(assetDir string) { |
|
||||
if len(updateUrls) != 0 { |
|
||||
log.Info("Start update assets") |
|
||||
for file, url := range updateUrls { |
|
||||
downloadFile(url, assetDir+"/"+file) |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,98 +0,0 @@ |
|||||
package main |
|
||||
|
|
||||
import ( |
|
||||
log "github.com/sirupsen/logrus" |
|
||||
"os" |
|
||||
"regexp" |
|
||||
"strconv" |
|
||||
) |
|
||||
|
|
||||
func loadDns() { |
|
||||
if len(dnsServer) == 0 { |
|
||||
log.Info("Using system DNS server") |
|
||||
return |
|
||||
} |
|
||||
log.Infof("Setting up DNS server -> %v", dnsServer) |
|
||||
dnsContent := "" |
|
||||
for _, address := range dnsServer { |
|
||||
dnsContent += "nameserver " + address + "\n" |
|
||||
} |
|
||||
err := os.WriteFile("/etc/resolv.conf", []byte(dnsContent), 0644) |
|
||||
if err != nil { |
|
||||
log.Panic("Setting up DNS failed") |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadNetwork() { |
|
||||
log.Info("Enabled IP forward") |
|
||||
runCommand("sysctl", "-w", "net.ipv4.ip_forward=1") |
|
||||
runCommand("sysctl", "-w", "net.ipv6.conf.all.forwarding=1") |
|
||||
|
|
||||
log.Info("Flush system IP configure") |
|
||||
runCommand("ip", "link", "set", "eth0", "down") |
|
||||
runCommand("ip", "-4", "addr", "flush", "dev", "eth0") |
|
||||
runCommand("ip", "-6", "addr", "flush", "dev", "eth0") |
|
||||
runCommand("ip", "link", "set", "eth0", "down") |
|
||||
|
|
||||
log.Info("Setting up system IP configure") |
|
||||
if v4Address != "" { |
|
||||
runCommand("ip", "-4", "addr", "add", v4Address, "dev", "eth0") |
|
||||
} |
|
||||
if v4Gateway != "" { |
|
||||
runCommand("ip", "-4", "route", "add", "default", "via", v4Gateway) |
|
||||
} |
|
||||
if v6Address != "" { |
|
||||
runCommand("ip", "-6", "addr", "add", v6Address, "dev", "eth0") |
|
||||
} |
|
||||
if v6Gateway != "" { |
|
||||
runCommand("ip", "-6", "route", "add", "default", "via", v6Gateway) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func v4SysBypass() { |
|
||||
_, output := runCommand("ip", "-4", "addr") |
|
||||
for _, temp := range regexp.MustCompile(`inet (\S+)`).FindAllStringSubmatch(output, -1) { |
|
||||
v4Bypass = append(v4Bypass, temp[1]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func v6SysBypass() { |
|
||||
_, output := runCommand("ip", "-6", "addr") |
|
||||
for _, temp := range regexp.MustCompile(`inet6 (\S+)`).FindAllStringSubmatch(output, -1) { |
|
||||
v6Bypass = append(v6Bypass, temp[1]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func loadTProxy() { |
|
||||
log.Info("Setting up TProxy of IPv4") |
|
||||
v4TableNum := strconv.Itoa(v4RouteTable) |
|
||||
runCommand("ip", "-4", "rule", "add", "fwmark", "1", "table", v4TableNum) |
|
||||
runCommand("ip", "-4", "route", "add", "local", "0.0.0.0/0", "dev", "lo", "table", v4TableNum) |
|
||||
runCommand("iptables", "-t", "mangle", "-N", "XPROXY") |
|
||||
v4SysBypass() |
|
||||
log.Infof("Setting up IPv4 bypass CIDR -> %v", v4Bypass) |
|
||||
for _, cidr := range v4Bypass { |
|
||||
runCommand("iptables", "-t", "mangle", "-A", "XPROXY", "-d", cidr, "-j", "RETURN") |
|
||||
} |
|
||||
runCommand("iptables", "-t", "mangle", "-A", "XPROXY", "-p", "tcp", "-j", "TPROXY", |
|
||||
"--on-port", strconv.Itoa(v4TProxyPort), "--tproxy-mark", "1") |
|
||||
runCommand("iptables", "-t", "mangle", "-A", "XPROXY", "-p", "udp", "-j", "TPROXY", |
|
||||
"--on-port", strconv.Itoa(v4TProxyPort), "--tproxy-mark", "1") |
|
||||
runCommand("iptables", "-t", "mangle", "-A", "PREROUTING", "-j", "XPROXY") |
|
||||
|
|
||||
log.Info("Setting up TProxy of IPv6") |
|
||||
v6TableNum := strconv.Itoa(v6RouteTable) |
|
||||
runCommand("ip", "-6", "rule", "add", "fwmark", "1", "table", v6TableNum) |
|
||||
runCommand("ip", "-6", "route", "add", "local", "::/0", "dev", "lo", "table", v6TableNum) |
|
||||
runCommand("ip6tables", "-t", "mangle", "-N", "XPROXY6") |
|
||||
v6SysBypass() |
|
||||
log.Infof("Setting up IPv6 bypass CIDR -> %v", v6Bypass) |
|
||||
for _, cidr := range v6Bypass { |
|
||||
runCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", "-d", cidr, "-j", "RETURN") |
|
||||
} |
|
||||
runCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", "-p", "tcp", "-j", "TPROXY", |
|
||||
"--on-port", strconv.Itoa(v6TProxyPort), "--tproxy-mark", "1") |
|
||||
runCommand("ip6tables", "-t", "mangle", "-A", "XPROXY6", "-p", "udp", "-j", "TPROXY", |
|
||||
"--on-port", strconv.Itoa(v6TProxyPort), "--tproxy-mark", "1") |
|
||||
runCommand("ip6tables", "-t", "mangle", "-A", "PREROUTING", "-j", "XPROXY6") |
|
||||
} |
|
Loading…
Reference in new issue