init
Some checks failed
Build and Release / release (push) Failing after 26s

This commit is contained in:
2025-12-12 17:37:34 +07:00
commit 9d769ed08c
32 changed files with 895 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
name: Build and Release
run-name: ${{ gitea.actor }} build 🚀
on:
push:
branches:
- master
jobs:
release:
runs-on: ubuntu-latest
container:
image: azenkain/go-node:latest
steps:
- uses: actions/checkout@v4
- name: Download Go dependencies
run: go mod download
- name: Build for Windows
run: GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" .
- name: Grant execute permissions
run: |
chmod +x ./script/release-uploader
- name: Upload release
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: script/release-uploader -token=$REPO_TOKEN -release-url="https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/Firefly_Go_Proxy/releases" -files="firefly-go-proxy.exe"

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
.history
.vscode
*.exe
*.pem
*.log
*.crt

18
LICENSE Normal file
View File

@@ -0,0 +1,18 @@
MIT License
Copyright (c) 2025 Firefly-Shelter
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

14
Makefile Normal file
View File

@@ -0,0 +1,14 @@
build:
@echo Building windows binary...
set GOOS=windows&& set GOARCH=amd64&& set CGO_ENABLED=0&& go build -trimpath -ldflags="-s -w" .
@echo Done!
build_ico:
@echo Building application icon...
magick logo.jpg -define icon:auto-resize=256,128,64,48,32,16 ./logo.ico
@echo Done!
set_logo:
@echo Embedding application icon...
go-winres simply --icon ./logo.ico
@echo Done!

81
README.md Normal file
View File

@@ -0,0 +1,81 @@
# Firefly Go Proxy
A lightweight HTTP/HTTPS proxy server with domain redirection and request blocking capabilities. This tool is designed to help with local development and testing by intercepting and modifying HTTP/HTTPS traffic.
## Features
- HTTP/HTTPS proxy with MITM support
- Domain-based request redirection
- URL pattern blocking
- Automatic certificate management
- Cross-platform support (Windows, macOS, Linux)
- System proxy configuration
## Installation
### Prerequisites
- Go 1.22 or higher
- Git
### Building from source
```bash
cd firefly-go-proxy
go build
```
## Usage
### Basic usage
```bash
./firefly-proxy [flags] //linux|macos
./firefly-proxy.exe [flags] //windows
```
### Available Flags
- `-r`: Redirect target host (default: "127.0.0.1:21000")
- `-b`: Comma-separated list of blocked ports
- `-e`: Path to an executable to run with admin privileges
### Examples
1. Start proxy with default settings:
```bash
./firefly-proxy //linux|macos
./firefly-proxy.exe //windows
```
2. Redirect traffic to a different host:
```bash
./firefly-proxy -r 192.168.1.100:8080 //linux|macos
./firefly-proxy.exe -r 192.168.1.100:8080 //windows
```
3. Block specific ports:
```bash
./firefly-proxy -b "80,443,8080" //linux|macos
./firefly-proxy.exe -b "80,443,8080" //windows
```
4. Run an executable with admin privileges:
```bash
./firefly-proxy -e "/path/to/your/executable" //linux|macos
./firefly-proxy.exe -e "/path/to/your/executable" //windows
```
## How it works
The proxy intercepts HTTP/HTTPS traffic and can:
- Redirect requests based on domain names
- Block specific URLs or patterns
- Handle SSL/TLS connections with custom CA certificates
- Automatically configure system proxy settings
## License
MIT License

37
cache.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"crypto/tls"
"sync"
)
type CertStorage struct {
certs map[string]*tls.Certificate
mtx sync.RWMutex
}
func (cs *CertStorage) Fetch(hostname string, gen func() (*tls.Certificate, error)) (*tls.Certificate, error) {
cs.mtx.RLock()
cert, ok := cs.certs[hostname]
cs.mtx.RUnlock()
if ok {
return cert, nil
}
cert, err := gen()
if err != nil {
return nil, err
}
cs.mtx.Lock()
cs.certs[hostname] = cert
cs.mtx.Unlock()
return cert, nil
}
func NewCertStorage() *CertStorage {
return &CertStorage{
certs: make(map[string]*tls.Certificate),
}
}

29
cert.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"crypto/tls"
"os"
"path/filepath"
"github.com/elazarl/goproxy"
)
const caCertName = "firefly-go-proxy-ca.crt"
func setupCertificate() (*tls.Certificate, error) {
if _, err := os.Stat(caCertName); os.IsNotExist(err) {
if err := os.WriteFile(caCertName, goproxy.GoproxyCa.Certificate[0], 0644); err != nil {
return nil, err
}
}
absPath, err := filepath.Abs(caCertName)
if err != nil {
return nil, err
}
if err := installCA(absPath); err != nil {
return nil, err
}
return &goproxy.GoproxyCa, nil
}

25
cert_darwin.go Normal file
View File

@@ -0,0 +1,25 @@
//go:build darwin
// +build darwin
package main
import (
"os/exec"
)
func installCA(absPath string) error {
cmd := exec.Command(
"security",
"add-trusted-cert",
"-d",
"-r", "trustRoot",
"-k", "/Library/Keychains/System.keychain",
absPath,
)
if err := cmd.Run(); err != nil {
return err
}
return nil
}

62
cert_linux.go Normal file
View File

@@ -0,0 +1,62 @@
//go:build linux
// +build linux
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
func installCA(absPath string) error {
// Detect distro
data, err := os.ReadFile("/etc/os-release")
if err != nil {
return fmt.Errorf("cannot detect distro: %v", err)
}
content := string(data)
// Debian/Ubuntu/Kali
if strings.Contains(content, "ID=debian") ||
strings.Contains(content, "ID=ubuntu") ||
strings.Contains(content, "ID=kali") {
destDir := "/usr/local/share/ca-certificates"
if err := os.MkdirAll(destDir, 0755); err != nil {
return fmt.Errorf("failed to create cert dir: %v", err)
}
filename := filepath.Base(absPath)
destPath := filepath.Join(destDir, filename)
inputData, err := os.ReadFile(absPath)
if err != nil {
return fmt.Errorf("failed to read source file: %v", err)
}
if err := os.WriteFile(destPath, inputData, 0644); err != nil {
return fmt.Errorf("failed to write cert file to system: %v", err)
}
fmt.Printf("Updating certificates for Debian/Ubuntu...\n")
cmd := exec.Command("update-ca-certificates")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// Arch / Manjaro
if strings.Contains(content, "ID=arch") ||
strings.Contains(content, "ID=manjaro") {
cmd := exec.Command("trust", "anchor", "--store", absPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
return fmt.Errorf("unsupported Linux distribution")
}

7
cert_other.go Normal file
View File

@@ -0,0 +1,7 @@
//go:build !windows && !darwin && !linux
package main
func installCA(certPath string) error {
return nil
}

17
cert_windows.go Normal file
View File

@@ -0,0 +1,17 @@
//go:build windows
// +build windows
package main
import (
"os/exec"
)
func installCA(absPath string) error {
cmd := exec.Command("certutil", "-addstore", "-user", "root", absPath)
if err := cmd.Run(); err != nil {
return err
}
return nil
}

62
config.go Normal file
View File

@@ -0,0 +1,62 @@
package main
var RedirectDomains = []string{
".hoyoverse.com",
".mihoyo.com",
".yuanshen.com",
".bhsr.com",
".starrails.com",
".juequling.com",
".zenlesszonezero.com",
".bh3.com",
".honkaiimpact3.com",
".mob.com",
".hyg.com",
}
var AlwaysIgnoreDomains = []string{
"autopatchcn.yuanshen.com",
"autopatchhk.yuanshen.com",
"autopatchcn.juequling.com",
"autopatchos.zenlesszonezero.com",
"autopatchcn.bhsr.com",
"autopatchos.starrails.com",
}
var BlockUrls = []string{
"/data_abtest_api/config/experiment/list",
"/common/hkrpg_global/announcement/api/getAlertPic",
"/common/hkrpg_global/announcement/api/getAlertAnn",
"/hkrpg_global/combo/red_dot/list",
"/sdk/upload",
"/sdk/dataUpload",
"/common/h5log/log/batch",
"/crash/dataUpload",
"/crashdump/dataUpload",
"/client/event/dataUpload",
"/log",
"/asm/dataUpload",
"/sophon/dataUpload",
"/apm/dataUpload",
"/2g/dataUpload",
"/v1/firelog/legacy/log",
"/h5/upload",
"/_ts",
"/perf/config/verify",
"/ptolemaios_api/api/reportStrategyData",
"/combo/box/api/config/sdk/combo",
"/hkrpg_global/combo/granter/api/compareProtocolVersion",
"/admin/mi18n",
"/combo/box/api/config/sw/precache",
"/hkrpg_global/mdk/agreement/api/getAgreementInfos",
"/device-fp/api/getExtList",
"/admin/mi18n/plat_os/m09291531181441/m09291531181441-version.json",
"/admin/mi18n/plat_oversea/m2020030410/m2020030410-version.json",
}
var ForceRedirectOnUrlContains = []string{
"/query_dispatch",
"/query_gateway",
"/query_region_list",
"/query_cur_region",
}

16
go.mod Normal file
View File

@@ -0,0 +1,16 @@
module firefly-go-proxy
go 1.25.4
require (
github.com/elazarl/goproxy v1.7.2
github.com/rs/zerolog v1.34.0
golang.org/x/sys v0.39.0
)
require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/text v0.31.0 // indirect
)

32
go.sum Normal file
View File

@@ -0,0 +1,32 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

17
log.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"os"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func init() {
output := zerolog.ConsoleWriter{
Out: os.Stdout,
PartsOrder: []string{"level", "message"},
}
log.Logger = zerolog.New(output).With().Logger()
}

BIN
logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

BIN
logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

156
main.go Normal file
View File

@@ -0,0 +1,156 @@
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/elazarl/goproxy"
zlog "github.com/rs/zerolog/log"
)
var ENV_CONFIG = make([]string, 0)
func main() {
redirectHost := flag.String("r", "127.0.0.1:21000", "redirect target host")
blockedStr := flag.String("b", "", "comma separated list of blocked ports")
exePath := flag.String("e", "", "path to the executable")
flag.Parse()
blockedPorts := parseBlockedPorts(*blockedStr)
port := findFreePort(blockedPorts)
if port == "-1" {
zlog.Error().Str("port", port).Msg("No free port available")
return
}
cert, err := setupCertificate()
if err != nil {
zlog.Error().Err(err).Msg("Failed setup certificate")
return
}
addr := ":" + port
proxyAddr := "127.0.0.1"
defer func() {
if r := recover(); r != nil {
zlog.Error().
Interface("panic", r).
Msg("Unexpected panic, resetting system proxy")
setProxy(false, "", "")
}
}()
setProxy(true, proxyAddr, port)
customCaMitm := &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(cert)}
var customAlwaysMitm goproxy.FuncHttpsHandler = func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
domain := cleanHost(host)
if matchDomain(domain, RedirectDomains) {
return customCaMitm, host
}
return goproxy.OkConnect, host
}
proxy := goproxy.NewProxyHttpServer()
proxy.Logger = log.New(io.Discard, "", 0)
proxy.Tr = &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConns: 1000,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
DisableCompression: false,
}
proxy.CertStore = NewCertStorage()
proxy.OnRequest().HandleConnect(customAlwaysMitm)
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
host := req.URL.Hostname()
path := req.URL.Path
if matchDomain(host, AlwaysIgnoreDomains) {
return req, nil
}
if matchDomain(host, RedirectDomains) {
if matchURL(path, BlockUrls) {
full := req.URL.String()
zlog.Warn().Str("url", full).Msg("Blocked URL")
return req, goproxy.NewResponse(
req,
goproxy.ContentTypeText,
http.StatusNotFound,
`{\n"message": "blocked by proxy",\n,"success": false,\n"retcode": -1\n}`,
)
}
full := req.URL.String()
if matchURL(full, ForceRedirectOnUrlContains) {
zlog.Info().Str("Url", full).Msg("Force redirect")
req.URL.Scheme = "http"
req.URL.Host = *redirectHost
return req, nil
}
zlog.Info().Str("Host", host).Msg("Redirect domain")
req.URL.Scheme = "http"
req.URL.Host = *redirectHost
return req, nil
}
return req, nil
})
srv := &http.Server{
Addr: addr,
Handler: proxy,
ReadTimeout: 10 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20,
}
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
if *exePath != "" && exists(*exePath) {
go func() {
time.Sleep(1 * time.Second)
err := runWithAdmin(*exePath, ENV_CONFIG)
if err != nil {
zlog.Error().Err(err).Msg("Failed to start exe as admin")
} else {
zlog.Info().Str("ExePath", *exePath).Msg("Started exe as admin")
}
}()
}
go func() {
zlog.Info().
Str("ProxyAddress", proxyAddr).
Str("RedirectTo", *redirectHost).
Str("BlockedPorts", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(blockedPorts)), ","), "[]")).
Str("ExePath", *exePath).
Msg("Proxy started")
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
zlog.Fatal().Err(err).Msg("ListenAndServe failed")
}
}()
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
zlog.Error().Err(err).Msg("Server shutdown error")
}
setProxy(false, "", "")
}

BIN
rsrc_windows_386.syso Normal file

Binary file not shown.

BIN
rsrc_windows_amd64.syso Normal file

Binary file not shown.

7
run_admin.go Normal file
View File

@@ -0,0 +1,7 @@
//go:build !windows && !darwin && !linux
package main
func runWithAdmin(exePath string, env []string) error {
return nil
}

20
run_admin_darwin.go Normal file
View File

@@ -0,0 +1,20 @@
//go:build darwin
// +build darwin
package main
import (
"fmt"
"os"
"os/exec"
"strings"
)
func runWithAdmin(exePath string, env []string) error {
escaped := strings.ReplaceAll(exePath, `"`, `\"`)
script := fmt.Sprintf(`do shell script "%s" with administrator privileges`, escaped)
cmd := exec.Command("osascript", "-e", script)
cmd.Env = append(os.Environ(), env...)
return cmd.Start()
}

15
run_admin_linux.go Normal file
View File

@@ -0,0 +1,15 @@
//go:build linux
// +build linux
package main
import (
"os"
"os/exec"
)
func runWithAdmin(exePath string, env []string) error {
cmd := exec.Command("pkexec", exePath)
cmd.Env = append(os.Environ(), env...)
return cmd.Start()
}

15
run_admin_windows.go Normal file
View File

@@ -0,0 +1,15 @@
//go:build windows
// +build windows
package main
import (
"os"
"os/exec"
)
func runWithAdmin(exePath string, env []string) error {
cmd := exec.Command("powershell", "Start-Process", exePath, "-Verb", "runAs")
cmd.Env = append(os.Environ(), env...)
return cmd.Start()
}

13
script/README_Note.md Normal file
View File

@@ -0,0 +1,13 @@
# Changelog
## First release
### Added
- Initial release of Firefly Go Proxy
- Basic HTTP/HTTPS proxy functionality
- Domain-based request redirection
- URL pattern blocking
- Cross-platform support (Windows, macOS, Linux)
- System proxy configuration for all platforms
- Automatic certificate management
- Support for running executables with admin privileges

BIN
script/release-uploader Normal file

Binary file not shown.

5
script/release.json Normal file
View File

@@ -0,0 +1,5 @@
{
"tag": "1.0-01",
"title": "PreBuild Version 1.0 - 01"
}

7
system_proxy.go Normal file
View File

@@ -0,0 +1,7 @@
//go:build !windows && !darwin && !linux
package main
func setProxy(enable bool, host string, port string) error {
return nil
}

68
system_proxy_darwin.go Normal file
View File

@@ -0,0 +1,68 @@
//go:build darwin
// +build darwin
package main
import (
"fmt"
"os/exec"
"strings"
)
func parseNetworkServices(out string) []string {
lines := strings.Split(out, "\n")
var result []string
for _, line := range lines {
if strings.Contains(line, "(Hardware Port:") {
start := strings.Index(line, "Hardware Port: ") + len("Hardware Port: ")
end := strings.Index(line[start:], ",")
if end > 0 {
result = append(result, line[start:start+end])
}
}
}
return result
}
func contains(arr []string, v string) bool {
for _, x := range arr {
if x == v {
return true
}
}
return false
}
func setProxy(enable bool, host string, port string) error {
out, err := exec.Command("networksetup", "-listnetworkserviceorder").CombinedOutput()
if err != nil {
return err
}
services := parseNetworkServices(string(out))
active := ""
if contains(services, "Wi-Fi") {
active = "Wi-Fi"
} else if contains(services, "Ethernet") {
active = "Ethernet"
} else {
if len(services) == 0 {
return fmt.Errorf("no network services found")
}
active = services[0]
}
if enable {
exec.Command("networksetup", "-setwebproxy", active, host, port).Run()
exec.Command("networksetup", "-setsecurewebproxy", active, host, port).Run()
exec.Command("networksetup", "-setwebproxystate", active, "on").Run()
exec.Command("networksetup", "-setsecurewebproxystate", active, "on").Run()
} else {
exec.Command("networksetup", "-setwebproxystate", active, "off").Run()
exec.Command("networksetup", "-setsecurewebproxystate", active, "off").Run()
}
return nil
}

24
system_proxy_linux.go Normal file
View File

@@ -0,0 +1,24 @@
//go:build linux
// +build linux
package main
import "fmt"
func setProxy(enable bool, host string, port string) error {
httpProxy1 := fmt.Sprintf("HTTP_PROXY=http://%s:%s", host, port)
httpProxy2 := fmt.Sprintf("http_proxy=http://%s:%s", host, port)
ENV_CONFIG = append(ENV_CONFIG, httpProxy1, httpProxy2)
httpsProxy1 := fmt.Sprintf("HTTPS_PROXY=http://%s:%s", host, port)
httpsProxy2 := fmt.Sprintf("https_proxy=http://%s:%s", host, port)
ENV_CONFIG = append(ENV_CONFIG, httpsProxy1, httpsProxy2)
if enable {
ENV_CONFIG = make([]string, 0)
}
return nil
}

43
system_proxy_windows.go Normal file
View File

@@ -0,0 +1,43 @@
//go:build windows
// +build windows
package main
import (
"fmt"
"syscall"
"golang.org/x/sys/windows/registry"
)
func setProxy(enable bool, host string, port string) error {
k, _, err := registry.CreateKey(
registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\Internet Settings`,
registry.SET_VALUE,
)
if err != nil {
return err
}
if enable {
k.SetDWordValue("ProxyEnable", 1)
addr := fmt.Sprintf("%s:%s", host, port)
val := fmt.Sprintf("http=%s;https=%s", addr, addr)
k.SetStringValue("ProxyServer", val)
} else {
k.SetDWordValue("ProxyEnable", 0)
}
k.Close()
d := syscall.NewLazyDLL("wininet.dll")
o := d.NewProc("InternetSetOptionW")
o.Call(0, 39, 0, 0)
o.Call(0, 37, 0, 0)
return nil
}

67
utils.go Normal file
View File

@@ -0,0 +1,67 @@
package main
import (
"net"
"os"
"slices"
"strconv"
"strings"
)
func matchDomain(host string, list []string) bool {
for _, d := range list {
if strings.HasSuffix(host, d) {
return true
}
}
return false
}
func matchURL(url string, list []string) bool {
for _, u := range list {
if strings.HasPrefix(url, u) {
return true
}
}
return false
}
func cleanHost(h string) string {
if idx := strings.LastIndex(h, ":"); idx != -1 {
return h[:idx]
}
return h
}
func findFreePort(blocked []int) string {
for {
ln, err := net.Listen("tcp", ":0")
if err != nil {
return "-1"
}
port := ln.Addr().(*net.TCPAddr).Port
ln.Close()
if !slices.Contains(blocked, port) {
return strconv.Itoa(port)
}
}
}
func parseBlockedPorts(s string) []int {
var ports []int
if s == "" {
return ports
}
for _, p := range strings.Split(s, ",") {
if port, err := strconv.Atoi(strings.TrimSpace(p)); err == nil {
ports = append(ports, port)
}
}
return ports
}
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}