Files
openlan/pkg/config/proxy.go
2025-03-20 20:21:37 +08:00

290 lines
6.8 KiB
Go
Executable File

package config
import (
"flag"
"fmt"
"os"
"path"
"regexp"
"github.com/luscis/openlan/pkg/libol"
)
type ShadowProxy struct {
Server string `json:"server,omitempty"`
Key string `json:"key,omitempty"`
Cipher string `json:"cipher,omitempty"`
Password string `json:"password,omitempty"`
Plugin string `json:"plugin,omitempty"`
PluginOpts string `json:"pluginOpts,omitempty"`
Protocol string `json:"protocol,omitempty"`
}
type ForwardSocks struct {
Server string `json:"server,omitempty"`
}
type HttpForward struct {
Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`
Server string `json:"server,omitempty" yaml:"server,omitempty"`
Insecure bool `json:"insecure,omitempty" yaml:"insecure,omitempty"`
Match []string `json:"match,omitempty" yaml:"match,omitempty"`
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
Socks ForwardSocks `json:"socks,omitempty" yaml:"socks,omitempty"`
}
func (f *HttpForward) SocksAddr() string {
if f.Socks.Server != "" {
return f.Socks.Server
}
return f.Server
}
type HttpBackends []*HttpForward
func (h HttpBackends) isMatch(value string, rules []string) bool {
if len(rules) == 0 {
return true
}
for _, rule := range rules {
pattern := fmt.Sprintf(`(^|\.)%s(:\d+)?$`, regexp.QuoteMeta(rule))
re := regexp.MustCompile(pattern)
if re.MatchString(value) {
return true
}
}
return false
}
func (h HttpBackends) FindBackend(host string) *HttpForward {
for _, via := range h {
if via == nil {
continue
}
if via.Server == "" && via.Socks.Server == "" {
continue
}
if h.isMatch(host, via.Match) {
return via
}
}
return nil
}
type FindBackend interface {
FindBackend(host string) *HttpForward
}
type SocksProxy struct {
Conf string `json:"-" yaml:"-"`
Listen string `json:"listen,omitempty" yaml:"listen,omitempty"`
Auth *Password `json:"auth,omitempty" yaml:"auth,omitempty"`
Backends HttpBackends `json:"backends,omitempty" yaml:"backends,omitempty"`
}
func (s *SocksProxy) Initialize() error {
libol.Info("SocksProxy.Initialize %s", s.Conf)
if err := s.Load(); err != nil {
libol.Error("SocksProxy.Initialize %s", err)
return err
}
return nil
}
func (s *SocksProxy) Load() error {
if s.Conf == "" || libol.FileExist(s.Conf) != nil {
return libol.NewErr("invalid configure file")
}
return libol.UnmarshalLoad(s, s.Conf)
}
type HttpSocks struct {
Listen string `json:"listen,omitempty"`
}
type HttpProxy struct {
Conf string `json:"-" yaml:"-"`
ConfDir string `json:"-" yaml:"-"`
Listen string `json:"listen,omitempty"`
Auth *Password `json:"auth,omitempty" yaml:"auth,omitempty"`
Cert *Cert `json:"cert,omitempty" yaml:"cert,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty"`
CaCert string `json:"cacert,omitempty" yaml:"cacert,omitempty"`
Backends HttpBackends `json:"backends,omitempty" yaml:"backends,omitempty"`
Socks *HttpSocks `json:"socks,omitempty" yaml:"socks,omitempty"`
SocksProxy *SocksProxy `json:"-" yaml:"-"`
}
func (h *HttpProxy) Initialize() error {
if h.ConfDir == "" {
h.ConfDir = path.Dir(os.Args[0])
}
libol.Info("HttpProxy.Initialize %s", h.Conf)
if err := h.Load(); err != nil {
libol.Error("HttpProxy.Initialize %s", err)
return err
}
h.Correct()
return nil
}
func (h *HttpProxy) Load() error {
if h.Conf == "" || libol.FileExist(h.Conf) != nil {
return libol.NewErr("invalid configure file")
}
return libol.UnmarshalLoad(h, h.Conf)
}
func (h *HttpProxy) Correct() {
if h.Cert != nil {
h.Cert.Correct()
}
if h.Password == "" {
h.Password = h.Listen + ".pass"
}
h.Password = path.Join(h.ConfDir, h.Password)
if h.CaCert == "" {
h.CaCert = "ca.crt"
}
h.CaCert = path.Join(h.ConfDir, h.CaCert)
if h.Socks != nil {
h.SocksProxy = &SocksProxy{
Listen: h.Socks.Listen,
}
}
}
func (h *HttpProxy) FindMatch(domain string, to *HttpForward) int {
for i, rule := range to.Match {
if rule == domain {
return i
}
}
return -1
}
func (h *HttpProxy) FindBackend(remote string) *HttpForward {
for _, to := range h.Backends {
if to.Server == remote {
return to
}
}
return nil
}
func (h *HttpProxy) AddMatch(domain, remote string) int {
to := h.FindBackend(remote)
if to == nil {
return -1
}
index := h.FindMatch(domain, to)
if index == -1 {
to.Match = append(to.Match, domain)
}
return 0
}
func (h *HttpProxy) DelMatch(domain, remote string) int {
to := h.FindBackend(remote)
if to == nil {
return -1
}
index := h.FindMatch(domain, to)
if index > -1 {
to.Match = append(to.Match[:index], to.Match[index+1:]...)
}
return index
}
func (h *HttpProxy) Save() {
if h.Conf == "" {
return
}
if err := libol.MarshalSave(&h, h.Conf, true); err != nil {
libol.Error("Proxy.Save %s %s", h.Conf, err)
}
}
type TcpProxy struct {
Conf string `json:"-" yaml:"-"`
Listen string `json:"listen,omitempty"`
Target []string `json:"target,omitempty"`
}
func (t *TcpProxy) Initialize() error {
libol.Info("TcpProxy.Initialize %s", t.Conf)
if err := t.Load(); err != nil {
libol.Error("TcpProxy.Initialize %s", err)
return err
}
return nil
}
func (t *TcpProxy) Load() error {
if t.Conf == "" || libol.FileExist(t.Conf) != nil {
return libol.NewErr("invalid configure file")
}
return libol.UnmarshalLoad(t, t.Conf)
}
type Proxy struct {
Conf string `json:"-" yaml:"-"`
ConfDir string `json:"-" yaml:"-"`
Log Log `json:"log"`
Socks []*SocksProxy `json:"socks,omitempty" yaml:"socks,omitempty"`
Http []*HttpProxy `json:"http,omitempty" yaml:"http,omitempty"`
Tcp []*TcpProxy `json:"tcp,omitempty" yaml:"tcp,omitempty"`
Shadow []*ShadowProxy `json:"shadow,omitempty" yaml:"shadow,omitempty"`
PProf string `json:"pprof,omitempty" yaml:"pprof,omitempty"`
}
func NewProxy() *Proxy {
p := &Proxy{}
p.Parse()
p.Initialize()
return p
}
func (p *Proxy) Parse() {
flag.StringVar(&p.Log.File, "log:file", "", "Configure log file")
flag.StringVar(&p.Conf, "conf", "", "The configure file")
flag.StringVar(&p.PProf, "prof", "", "Http listen for CPU prof")
flag.IntVar(&p.Log.Verbose, "log:level", 20, "Configure log level")
flag.Parse()
}
func (p *Proxy) Initialize() {
if p.Conf == "" {
p.Conf = path.Dir(os.Args[0]) + "/" + "proxy.json"
}
if p.ConfDir == "" {
p.ConfDir = path.Dir(p.Conf)
}
if err := p.Load(); err != nil {
libol.Error("Proxy.Initialize %s", err)
}
p.Correct()
libol.Debug("Proxy.Initialize %v", p)
}
func (p *Proxy) Correct() {
for _, h := range p.Http {
h.ConfDir = p.ConfDir
h.Correct()
}
p.Log.Correct()
}
func (p *Proxy) Load() error {
return libol.UnmarshalLoad(p, p.Conf)
}
func (p *Proxy) Save() {
if err := libol.MarshalSave(&p, p.Conf, true); err != nil {
libol.Error("Proxy.Save %s %s", p.Conf, err)
}
}