136 lines
3.0 KiB
Go
136 lines
3.0 KiB
Go
|
|
// Package mobile provides a Go mobile binding for the TG WS Proxy.
|
||
|
|
package mobile
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
"net"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
|
||
|
|
"github.com/Flowseal/tg-ws-proxy/internal/config"
|
||
|
|
"github.com/Flowseal/tg-ws-proxy/internal/proxy"
|
||
|
|
)
|
||
|
|
|
||
|
|
var server *proxy.Server
|
||
|
|
var cancel context.CancelFunc
|
||
|
|
|
||
|
|
// Start starts the proxy server with the given configuration.
|
||
|
|
// Returns "OK" on success or an error message.
|
||
|
|
func Start(host string, port int, dcIP string, verbose bool) string {
|
||
|
|
cfg := config.DefaultConfig()
|
||
|
|
cfg.Host = host
|
||
|
|
cfg.Port = port
|
||
|
|
if dcIP != "" {
|
||
|
|
cfg.DCIP = parseDCIP(dcIP)
|
||
|
|
}
|
||
|
|
cfg.Verbose = verbose
|
||
|
|
|
||
|
|
// Setup logging to file
|
||
|
|
logDir := getLogDir()
|
||
|
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||
|
|
return fmt.Sprintf("Failed to create log dir: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
logFile := filepath.Join(logDir, "proxy.log")
|
||
|
|
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Sprintf("Failed to open log file: %v", err)
|
||
|
|
}
|
||
|
|
log.SetOutput(f)
|
||
|
|
log.SetFlags(log.Ldate | log.Ltime)
|
||
|
|
|
||
|
|
var ctx context.Context
|
||
|
|
ctx, cancel = context.WithCancel(context.Background())
|
||
|
|
|
||
|
|
server = proxy.NewServer(cfg)
|
||
|
|
if err := server.Start(ctx); err != nil {
|
||
|
|
cancel()
|
||
|
|
return fmt.Sprintf("Failed to start proxy: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return "OK"
|
||
|
|
}
|
||
|
|
|
||
|
|
// Stop stops the proxy server.
|
||
|
|
func Stop() string {
|
||
|
|
if cancel != nil {
|
||
|
|
cancel()
|
||
|
|
}
|
||
|
|
if server != nil {
|
||
|
|
server.Stop()
|
||
|
|
}
|
||
|
|
return "OK"
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetStatus returns the current proxy status.
|
||
|
|
func GetStatus() string {
|
||
|
|
if server == nil {
|
||
|
|
return "Not running"
|
||
|
|
}
|
||
|
|
stats := server.GetStats()
|
||
|
|
return fmt.Sprintf("Connections: %d | WS: %d | TCP: %d | Bytes Up: %d | Bytes Down: %d",
|
||
|
|
stats.ConnectionsTotal,
|
||
|
|
stats.ConnectionsWS,
|
||
|
|
stats.ConnectionsTCP,
|
||
|
|
stats.BytesUp,
|
||
|
|
stats.BytesDown)
|
||
|
|
}
|
||
|
|
|
||
|
|
// parseDCIP parses DC IP configuration string.
|
||
|
|
func parseDCIP(s string) []string {
|
||
|
|
if s == "" {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
result := []string{}
|
||
|
|
for _, part := range split(s, ",") {
|
||
|
|
trimmed := trim(part)
|
||
|
|
if trimmed != "" {
|
||
|
|
result = append(result, trimmed)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// getLogDir returns the log directory for Android.
|
||
|
|
func getLogDir() string {
|
||
|
|
// On Android, use app-specific directory
|
||
|
|
if dataDir := os.Getenv("ANDROID_DATA"); dataDir != "" {
|
||
|
|
return filepath.Join(dataDir, "tg-ws-proxy")
|
||
|
|
}
|
||
|
|
// Fallback to temp directory
|
||
|
|
return os.TempDir()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Helper functions for string manipulation (avoiding strings package issues with gomobile)
|
||
|
|
func split(s, sep string) []string {
|
||
|
|
result := []string{}
|
||
|
|
start := 0
|
||
|
|
for i := 0; i <= len(s)-len(sep); i++ {
|
||
|
|
if s[i:i+len(sep)] == sep {
|
||
|
|
result = append(result, s[start:i])
|
||
|
|
start = i + len(sep)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
result = append(result, s[start:])
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
func trim(s string) string {
|
||
|
|
start := 0
|
||
|
|
end := len(s)
|
||
|
|
for start < end && (s[start] == ' ' || s[start] == '\t' || s[start] == '\n' || s[start] == '\r') {
|
||
|
|
start++
|
||
|
|
}
|
||
|
|
for end > start && (s[end-1] == ' ' || s[end-1] == '\t' || s[end-1] == '\n' || s[end-1] == '\r') {
|
||
|
|
end--
|
||
|
|
}
|
||
|
|
return s[start:end]
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dummy function to use net package (required for SOCKS5)
|
||
|
|
func init() {
|
||
|
|
_ = net.Dial
|
||
|
|
}
|