mirror of
https://github.com/luscis/openlan.git
synced 2025-10-05 16:47:11 +08:00
fea: ceci name proxy support.
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/luscis/openlan/pkg/access"
|
||||
"github.com/luscis/openlan/pkg/config"
|
||||
"github.com/luscis/openlan/pkg/libol"
|
||||
"github.com/luscis/openlan/pkg/proxy"
|
||||
@@ -13,10 +12,17 @@ import (
|
||||
func main() {
|
||||
mode := "http"
|
||||
conf := ""
|
||||
nodate := false
|
||||
|
||||
flag.StringVar(&mode, "mode", "http", "Proxy mode for http, socks, tcp and name")
|
||||
flag.StringVar(&conf, "conf", "ceci.yaml", "The configuration file")
|
||||
flag.BoolVar(&nodate, "nodate", nodate, "Dont display message datetime")
|
||||
flag.Parse()
|
||||
|
||||
if nodate {
|
||||
libol.NoLogDate()
|
||||
}
|
||||
|
||||
if !(mode == "http" || mode == "socks" || mode == "tcp" || mode == "name") {
|
||||
libol.Warn("Ceci: not support mode:%s", mode)
|
||||
os.Exit(1)
|
||||
@@ -24,41 +30,35 @@ func main() {
|
||||
|
||||
libol.PreNotify()
|
||||
|
||||
var x proxy.Proxyer
|
||||
if mode == "name" {
|
||||
c := &config.Point{
|
||||
RequestAddr: true,
|
||||
Terminal: "off",
|
||||
Conf: conf,
|
||||
}
|
||||
c := &config.NameProxy{Conf: conf}
|
||||
if err := c.Initialize(); err != nil {
|
||||
return
|
||||
}
|
||||
p := access.NewPoint(c)
|
||||
p.Initialize()
|
||||
libol.Go(p.Start)
|
||||
x = proxy.NewNameProxy(c)
|
||||
} else if mode == "socks" {
|
||||
c := &config.SocksProxy{Conf: conf}
|
||||
if err := c.Initialize(); err != nil {
|
||||
return
|
||||
}
|
||||
p := proxy.NewSocksProxy(c)
|
||||
libol.Go(p.Start)
|
||||
x = proxy.NewSocksProxy(c)
|
||||
} else if mode == "tcp" {
|
||||
c := &config.TcpProxy{Conf: conf}
|
||||
if err := c.Initialize(); err != nil {
|
||||
return
|
||||
}
|
||||
p := proxy.NewTcpProxy(c)
|
||||
libol.Go(p.Start)
|
||||
x = proxy.NewTcpProxy(c)
|
||||
} else {
|
||||
c := &config.HttpProxy{Conf: conf}
|
||||
if err := c.Initialize(); err != nil {
|
||||
return
|
||||
}
|
||||
p := proxy.NewHttpProxy(c, nil)
|
||||
libol.Go(p.Start)
|
||||
x = proxy.NewHttpProxy(c, nil)
|
||||
}
|
||||
|
||||
libol.Go(x.Start)
|
||||
libol.SdNotify()
|
||||
libol.Wait()
|
||||
x.Stop()
|
||||
}
|
||||
|
@@ -1,25 +1,13 @@
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
connection: 2.2.2.1
|
||||
interface:
|
||||
address: 192.168.11.33/24
|
||||
username: test@vxlan
|
||||
password: fvph0ldhdvai
|
||||
crypt:
|
||||
secret: 477bb0c55edb
|
||||
forward:
|
||||
server: 192.168.11.1
|
||||
match:
|
||||
- 8.8.8.8
|
||||
- 8.8.4.4
|
||||
|
||||
listen: 127.0.0.1
|
||||
nameto: 114.114.114.114
|
||||
backends:
|
||||
- server: 192.168.11.10
|
||||
- server: 192.168.11.1
|
||||
nameto: 8.8.8.8
|
||||
match:
|
||||
- openai.com
|
||||
- chatgpt.com
|
||||
- server: 192.168.11.11
|
||||
- server: 192.168.11.2
|
||||
nameto: 8.8.8.8
|
||||
match:
|
||||
- google.com
|
||||
|
@@ -1,4 +0,0 @@
|
||||
protocol: tcp
|
||||
connection: who.openlan.net
|
||||
username: hi@default
|
||||
password: cb2ff088a34d
|
@@ -1,14 +0,0 @@
|
||||
|
||||
interface:
|
||||
address: 172.32.100.10/24
|
||||
connection: who.openlan.net
|
||||
username: hi@default
|
||||
password: 1f4ee82b5eb6
|
||||
protocol: tls
|
||||
crypt:
|
||||
algorithm: aes-256
|
||||
secret: 1f4ee82b5eb6
|
||||
cert:
|
||||
insecure: true
|
||||
|
||||
|
@@ -44,7 +44,7 @@ type Http struct {
|
||||
}
|
||||
|
||||
func (h *Http) Correct() {
|
||||
CorrectAddr(&h.Listen, 10000)
|
||||
SetListen(&h.Listen, 10000)
|
||||
if h.Public == "" {
|
||||
h.Public = VarDir("public")
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (h *Http) GetUrl() string {
|
||||
return "https://127.0.0.1:" + port
|
||||
}
|
||||
|
||||
func CorrectAddr(listen *string, port int) {
|
||||
func SetListen(listen *string, port int) {
|
||||
if *listen == "" {
|
||||
*listen = fmt.Sprintf("0.0.0.0:%d", port)
|
||||
return
|
||||
|
@@ -95,7 +95,7 @@ func (ap *Point) Correct() {
|
||||
ap.Network = strings.SplitN(ap.Username, "@", 2)[1]
|
||||
}
|
||||
}
|
||||
CorrectAddr(&ap.Connection, 10002)
|
||||
SetListen(&ap.Connection, 10002)
|
||||
if runtime.GOOS == "darwin" {
|
||||
ap.Interface.Provider = "tun"
|
||||
}
|
||||
|
@@ -234,3 +234,35 @@ func (p *Proxy) Save() {
|
||||
libol.Error("Proxy.Save %s %s", p.Conf, err)
|
||||
}
|
||||
}
|
||||
|
||||
type NameProxy struct {
|
||||
Conf string `json:"-" yaml:"-"`
|
||||
Listen string `json:"listen,omitempty"`
|
||||
Nameto string `json:"nameto,omitempty" yaml:"nameto,omitempty"`
|
||||
Metric int
|
||||
Backends ToForwards `json:"backends,omitempty" yaml:"backends,omitempty"`
|
||||
}
|
||||
|
||||
func (t *NameProxy) Initialize() error {
|
||||
libol.Info("NameProxy.Initialize %s", t.Conf)
|
||||
if err := t.Load(); err != nil {
|
||||
libol.Error("NameProxy.Initialize %s", err)
|
||||
return err
|
||||
}
|
||||
t.Correct()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NameProxy) Correct() {
|
||||
SetListen(&t.Listen, 53)
|
||||
if t.Metric == 0 {
|
||||
t.Metric = 300
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NameProxy) Load() error {
|
||||
if t.Conf == "" || libol.FileExist(t.Conf) != nil {
|
||||
return libol.NewErr("invalid configure file")
|
||||
}
|
||||
return libol.UnmarshalLoad(t, t.Conf)
|
||||
}
|
||||
|
@@ -114,7 +114,7 @@ func (s *Switch) Correct() {
|
||||
s.Alias = GetAlias()
|
||||
}
|
||||
|
||||
CorrectAddr(&s.Listen, 10002)
|
||||
SetListen(&s.Listen, 10002)
|
||||
if s.Http == nil {
|
||||
s.Http = &Http{}
|
||||
}
|
||||
|
@@ -122,8 +122,9 @@ func IpRouteAdd(name, prefix, nexthop string, opts ...string) ([]byte, error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
args := []string{
|
||||
"route", "address", prefix, "via", nexthop,
|
||||
"route", "replace", prefix, "via", nexthop,
|
||||
}
|
||||
args = append(args, opts...)
|
||||
return exec.Command("ip", args...).CombinedOutput()
|
||||
case "windows":
|
||||
args := []string{
|
||||
|
@@ -243,6 +243,14 @@ func (s *SubLogger) Fatal(format string, v ...interface{}) {
|
||||
s.logger.Write(FATAL, s.Fmt(format), v...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
func LogDate() {
|
||||
log.SetFlags(log.LstdFlags)
|
||||
}
|
||||
|
||||
func NoLogDate() {
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
func init() {
|
||||
LogDate()
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
@@ -125,26 +126,28 @@ func NewHttpProxy(cfg *co.HttpProxy, px Proxyer) *HttpProxy {
|
||||
requests: make(map[string]*HttpRecord),
|
||||
proxer: px,
|
||||
}
|
||||
|
||||
h.server = &http.Server{
|
||||
Addr: cfg.Listen,
|
||||
Handler: h,
|
||||
}
|
||||
user, pass := co.SplitSecret(cfg.Secret)
|
||||
if user != "" {
|
||||
h.pass[user] = pass
|
||||
h.out.Debug("HttpProxy: Auth user %s", user)
|
||||
}
|
||||
if cfg.SocksProxy != nil {
|
||||
h.socks = NewSocksProxy(cfg.SocksProxy)
|
||||
h.socks.server.SetBackends(h)
|
||||
}
|
||||
h.loadUrl()
|
||||
h.loadPass()
|
||||
|
||||
h.Initialize()
|
||||
return h
|
||||
}
|
||||
|
||||
func (t *HttpProxy) Initialize() {
|
||||
t.server = &http.Server{
|
||||
Addr: t.cfg.Listen,
|
||||
Handler: t,
|
||||
}
|
||||
user, pass := co.SplitSecret(t.cfg.Secret)
|
||||
if user != "" {
|
||||
t.pass[user] = pass
|
||||
t.out.Debug("HttpProxy: Auth user %s", user)
|
||||
}
|
||||
if t.cfg.SocksProxy != nil {
|
||||
t.socks = NewSocksProxy(t.cfg.SocksProxy)
|
||||
t.socks.server.SetBackends(t)
|
||||
}
|
||||
t.loadUrl()
|
||||
t.loadPass()
|
||||
}
|
||||
|
||||
func (t *HttpProxy) loadUrl() {
|
||||
t.api.HandleFunc("/", t.GetIndex).Methods("GET")
|
||||
t.api.HandleFunc("/api", t.GetApi).Methods("GET")
|
||||
@@ -609,14 +612,13 @@ func (t *HttpProxy) AddMatch(w http.ResponseWriter, r *http.Request) {
|
||||
backend := vars["backend"]
|
||||
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.cfg.AddMatch(domain, backend) > -1 {
|
||||
encodeYaml(w, "success")
|
||||
} else {
|
||||
encodeYaml(w, "failed")
|
||||
}
|
||||
t.save()
|
||||
t.lock.Unlock()
|
||||
t.Save()
|
||||
}
|
||||
|
||||
func (t *HttpProxy) AddUser(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -648,7 +650,10 @@ func (t *HttpProxy) DelUser(w http.ResponseWriter, r *http.Request) {
|
||||
t.savePass()
|
||||
}
|
||||
|
||||
func (t *HttpProxy) save() {
|
||||
func (t *HttpProxy) Save() {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.proxer == nil {
|
||||
t.cfg.Save()
|
||||
} else {
|
||||
@@ -663,14 +668,14 @@ func (t *HttpProxy) DelMatch(w http.ResponseWriter, r *http.Request) {
|
||||
backend := vars["backend"]
|
||||
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.cfg.DelMatch(domain, backend) > -1 {
|
||||
encodeYaml(w, "success")
|
||||
} else {
|
||||
encodeYaml(w, "failed")
|
||||
}
|
||||
t.save()
|
||||
t.lock.Unlock()
|
||||
|
||||
t.Save()
|
||||
}
|
||||
|
||||
func (t *HttpProxy) GetPac(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -718,3 +723,10 @@ func (t *HttpProxy) GetApi(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
encodeYaml(w, urls)
|
||||
}
|
||||
|
||||
func (t *HttpProxy) Stop() {
|
||||
if t.server != nil {
|
||||
t.server.Shutdown(context.Background())
|
||||
t.server = nil
|
||||
}
|
||||
}
|
||||
|
146
pkg/proxy/name.go
Executable file
146
pkg/proxy/name.go
Executable file
@@ -0,0 +1,146 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/luscis/openlan/pkg/config"
|
||||
"github.com/luscis/openlan/pkg/libol"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type NameProxy struct {
|
||||
listen string
|
||||
cfg *config.NameProxy
|
||||
server *dns.Server
|
||||
out *libol.SubLogger
|
||||
lock sync.RWMutex
|
||||
names map[string]string
|
||||
addrs map[string]string
|
||||
}
|
||||
|
||||
func NewNameProxy(cfg *config.NameProxy) *NameProxy {
|
||||
return &NameProxy{
|
||||
listen: cfg.Listen,
|
||||
cfg: cfg,
|
||||
out: libol.NewSubLogger(cfg.Listen),
|
||||
names: make(map[string]string),
|
||||
addrs: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NameProxy) Initialize() {
|
||||
}
|
||||
|
||||
func (n *NameProxy) Forward(name, addr, nexthop string) {
|
||||
opts := []string{"metric", fmt.Sprintf("%d", n.cfg.Metric)}
|
||||
if out, err := libol.IpRouteAdd("", addr, nexthop, opts...); err != nil {
|
||||
n.out.Warn("Access.Forward: %s %s: %s", addr, err, out)
|
||||
return
|
||||
}
|
||||
n.out.Info("NameProxy.Forward: %s <- %s via %s ", nexthop, name, addr)
|
||||
}
|
||||
|
||||
func (n *NameProxy) UpdateDNS(name, addr string) bool {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
|
||||
updated := false
|
||||
if _, ok := n.names[name]; !ok {
|
||||
n.names[name] = addr
|
||||
updated = true
|
||||
}
|
||||
|
||||
if _, ok := n.addrs[addr]; !ok {
|
||||
n.addrs[addr] = name
|
||||
updated = true
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func (n *NameProxy) FindBackend(r *dns.Msg) *config.ForwardTo {
|
||||
if len(r.Question) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
name := r.Question[0].Name
|
||||
n.out.Debug("NameProxy.FindBackend %s", name)
|
||||
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
|
||||
via := n.cfg.Backends.FindBackend(name)
|
||||
if via != nil {
|
||||
n.out.Debug("NameProxy.FindBackend %s via %s", name, via.Server)
|
||||
}
|
||||
return via
|
||||
}
|
||||
|
||||
func (n *NameProxy) handleDNS(conn dns.ResponseWriter, r *dns.Msg) {
|
||||
client := &dns.Client{
|
||||
ReadTimeout: 3 * time.Second,
|
||||
WriteTimeout: 3 * time.Second,
|
||||
Net: "udp",
|
||||
}
|
||||
nameto := n.cfg.Nameto
|
||||
|
||||
libol.Go(func() {
|
||||
via := n.FindBackend(r)
|
||||
|
||||
if via != nil {
|
||||
nameto = via.Nameto
|
||||
}
|
||||
|
||||
config.SetListen(&nameto, 53)
|
||||
if nameto == "0.0.0.0:53" || nameto == n.listen {
|
||||
n.out.Error("NameProxy.handleDNS nil(%s)", nameto)
|
||||
return
|
||||
}
|
||||
|
||||
n.out.Info("NameProxy.handleDNS %s <- %v via %s", nameto, r.Question, conn.RemoteAddr())
|
||||
resp, _, err := client.Exchange(r, nameto)
|
||||
if err != nil {
|
||||
n.out.Error("NameProxy.handleDNS %s: %v", r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if via != nil && via.Server != "" {
|
||||
for _, rr := range resp.Answer {
|
||||
if aa, ok := rr.(*dns.A); ok {
|
||||
name := aa.Hdr.Name
|
||||
addr := aa.A.String()
|
||||
if n.UpdateDNS(name, addr) {
|
||||
n.Forward(name, addr, via.Server)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := conn.WriteMsg(resp); err != nil {
|
||||
n.out.Error("NameProxy.handleDNS %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (n *NameProxy) Start() {
|
||||
dns.HandleFunc(".", n.handleDNS)
|
||||
n.server = &dns.Server{Addr: n.listen, Net: "udp"}
|
||||
n.out.Info("NameProxy.StartDNS on %s", n.listen)
|
||||
|
||||
if err := n.server.ListenAndServe(); err != nil {
|
||||
n.out.Error("NameProxy.StartDNS server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NameProxy) Stop() {
|
||||
if n.server != nil {
|
||||
n.server.Shutdown()
|
||||
n.server = nil
|
||||
}
|
||||
n.out.Info("NameProxy.Stop")
|
||||
}
|
||||
|
||||
func (n *NameProxy) Save() {
|
||||
}
|
@@ -22,7 +22,12 @@ func NewSocksProxy(cfg *config.SocksProxy) *SocksProxy {
|
||||
out: libol.NewSubLogger(cfg.Listen),
|
||||
}
|
||||
// Create a SOCKS5 server
|
||||
user, pass := co.SplitSecret(cfg.Secret)
|
||||
s.Initialize()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SocksProxy) Initialize() {
|
||||
user, pass := co.SplitSecret(s.cfg.Secret)
|
||||
authMethods := make([]socks5.Authenticator, 0, 2)
|
||||
if user != "" {
|
||||
author := socks5.UserPassAuthenticator{
|
||||
@@ -34,11 +39,11 @@ func NewSocksProxy(cfg *config.SocksProxy) *SocksProxy {
|
||||
s.out.Debug("SocksProxy: Auth user %s", user)
|
||||
}
|
||||
conf := &socks5.Config{
|
||||
Backends: cfg.Backends,
|
||||
Backends: s.cfg.Backends,
|
||||
AuthMethods: authMethods,
|
||||
Logger: s.out,
|
||||
}
|
||||
crt := cfg.Cert
|
||||
crt := s.cfg.Cert
|
||||
if crt != nil && crt.KeyFile != "" {
|
||||
conf.TlsConfig = &tls.Config{
|
||||
Certificates: crt.GetCertificates(),
|
||||
@@ -47,10 +52,8 @@ func NewSocksProxy(cfg *config.SocksProxy) *SocksProxy {
|
||||
server, err := socks5.New(conf)
|
||||
if err != nil {
|
||||
s.out.Error("NewSocksProxy %s", err)
|
||||
return nil
|
||||
}
|
||||
s.server = server
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SocksProxy) Start() {
|
||||
@@ -79,3 +82,12 @@ func (s *SocksProxy) Start() {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SocksProxy) Stop() {
|
||||
if s.server != nil {
|
||||
s.server = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocksProxy) Save() {
|
||||
}
|
||||
|
@@ -25,6 +25,9 @@ func NewTcpProxy(cfg *config.TcpProxy) *TcpProxy {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpProxy) Initialize() {
|
||||
}
|
||||
|
||||
func (t *TcpProxy) tunnel(src net.Conn, dst net.Conn) {
|
||||
defer dst.Close()
|
||||
defer src.Close()
|
||||
@@ -106,8 +109,11 @@ func (t *TcpProxy) Start() {
|
||||
|
||||
func (t *TcpProxy) Stop() {
|
||||
if t.listener != nil {
|
||||
_ = t.listener.Close()
|
||||
}
|
||||
t.out.Info("TcpProxy.Stop")
|
||||
t.listener.Close()
|
||||
t.listener = nil
|
||||
}
|
||||
t.out.Info("TcpProxy.Stop")
|
||||
}
|
||||
|
||||
func (t *TcpProxy) Save() {
|
||||
}
|
||||
|
@@ -8,13 +8,12 @@ type Point struct {
|
||||
Alias string `json:"alias"`
|
||||
Protocol string `json:"protocol"`
|
||||
Remote string `json:"remote"`
|
||||
Switch string `json:"switch,omitempty"`
|
||||
Device string `json:"device"`
|
||||
RxBytes uint64 `json:"rxBytes"`
|
||||
TxBytes uint64 `json:"txBytes"`
|
||||
RxBytes uint64 `json:"rxbytes"`
|
||||
TxBytes uint64 `json:"txbytes"`
|
||||
ErrPkt uint64 `json:"errors"`
|
||||
State string `json:"state"`
|
||||
AliveTime int64 `json:"aliveTime"`
|
||||
AliveTime int64 `json:"alivetime"`
|
||||
System string `json:"system"`
|
||||
Address string `json:"address"`
|
||||
Names map[string]string `json:"names"`
|
||||
|
Reference in New Issue
Block a user