Files
T

193 lines
4.4 KiB
Go

package config
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/samosvalishe/free-turn-proxy/internal/uri"
)
// ClientJSON - JSON-схема конфигурации клиента (gomobile).
type ClientJSON struct {
Peer string `json:"peer"`
ClientID string `json:"clientId"`
SubURL string `json:"subUrl"`
Provider string `json:"provider"`
Routes bool `json:"routes"`
TURN turnJSON `json:"turn"`
Proxy proxyJSON `json:"proxy"`
VK vkJSON `json:"vk"`
Obf obfJSON `json:"obf"`
DNS dnsJSON `json:"dns"`
Log logJSON `json:"log"`
KCP kcpJSON `json:"kcp"`
Tunnel tunnelJSON `json:"tunnel"`
}
// tunnelJSON - параметры встроенного wg-quick туннеля.
type tunnelJSON struct {
Mode string `json:"mode"`
Config string `json:"config"`
MTU int `json:"mtu"`
}
type turnJSON struct {
N int `json:"n"`
Transport string `json:"transport"`
Host string `json:"host"`
Port string `json:"port"`
}
type proxyJSON struct {
Mode string `json:"mode"`
Listen string `json:"listen"`
}
type vkJSON struct {
Links []string `json:"links"`
StreamsPerCred int `json:"streamsPerCred"`
ManualCaptcha bool `json:"manualCaptcha"`
Platform string `json:"platform"`
}
type obfJSON struct {
Profile string `json:"profile"`
Key string `json:"key"`
TimingMs int `json:"timingMs"`
}
type dnsJSON struct {
Mode string `json:"mode"`
Servers []string `json:"servers"`
}
type logJSON struct {
Debug bool `json:"debug"`
}
// DefaultClientJSON возвращает дефолтную конфигурацию клиента в формате JSON.
func DefaultClientJSON() string {
b, err := json.MarshalIndent(defaultClientJSON(), "", " ")
if err != nil {
panic("config: default JSON must marshal: " + err.Error())
}
return string(b)
}
// PeekSubURLJSON извлекает subUrl из сырого JSON без полной валидации.
func PeekSubURLJSON(data []byte) string {
var peek struct {
SubURL string `json:"subUrl"`
}
if json.Unmarshal(data, &peek) != nil {
return ""
}
return peek.SubURL
}
// ParseClientJSON парсит JSON-конфигурацию клиента с опциональным overlayURI подписки.
func ParseClientJSON(data []byte, overlayURI string) (*Client, error) {
dto := defaultClientJSON()
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
if err := dec.Decode(&dto); err != nil {
return nil, fmt.Errorf("config json: %w", err)
}
r := dto.toRaw()
if overlayURI != "" {
u, err := uri.Parse(overlayURI)
if err != nil {
return nil, fmt.Errorf("failed to parse freeturn:// URI: %w", err)
}
r.applyURI(u)
}
c, err := assemble(r)
if err != nil {
return nil, err
}
if err := Validate(c); err != nil {
return nil, err
}
return c, nil
}
func defaultClientJSON() ClientJSON {
r := defaultRaw()
return ClientJSON{
Peer: r.Peer,
ClientID: r.ClientID,
SubURL: r.SubURL,
Provider: r.Provider,
Routes: r.Routes,
TURN: turnJSON{
N: r.N,
Transport: r.Transport,
Host: r.Turn,
Port: r.Port,
},
Proxy: proxyJSON{Mode: r.Mode, Listen: r.Listen},
KCP: kcpJSONFrom(r.KCP),
VK: vkJSON{
StreamsPerCred: r.StreamsPerCred,
ManualCaptcha: r.ManualCaptcha,
Platform: r.Platform,
},
Obf: obfJSON{
Profile: r.ObfProfile,
Key: r.ObfKey,
TimingMs: int(r.ObfTiming / time.Millisecond),
},
DNS: dnsJSON{Mode: r.DNSMode},
Log: logJSON{Debug: r.Debug},
Tunnel: tunnelJSON{
Mode: r.TunnelMode,
Config: r.TunnelConfig,
MTU: r.TunnelMTU,
},
}
}
func (j ClientJSON) toRaw() raw {
return raw{
Turn: j.TURN.Host,
Port: j.TURN.Port,
Listen: j.Proxy.Listen,
Peer: j.Peer,
Provider: j.Provider,
Links: strings.Join(j.VK.Links, ","),
N: j.TURN.N,
StreamsPerCred: j.VK.StreamsPerCred,
Transport: j.TURN.Transport,
Mode: j.Proxy.Mode,
ObfProfile: j.Obf.Profile,
ObfKey: j.Obf.Key,
ObfTiming: time.Duration(j.Obf.TimingMs) * time.Millisecond,
ManualCaptcha: j.VK.ManualCaptcha,
Platform: j.VK.Platform,
DNSMode: j.DNS.Mode,
DNSServers: strings.Join(j.DNS.Servers, ","),
Debug: j.Log.Debug,
ClientID: j.ClientID,
SubURL: j.SubURL,
Routes: j.Routes,
TunnelMode: j.Tunnel.Mode,
TunnelConfig: j.Tunnel.Config,
TunnelMTU: j.Tunnel.MTU,
KCP: j.KCP.profile(),
}
}