github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/internal/app/app.go (about) 1 package app 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net" 8 "net/http" 9 "os" 10 "path/filepath" 11 "time" 12 13 "github.com/Asutorufa/yuhaiin/internal/appapi" 14 web "github.com/Asutorufa/yuhaiin/internal/http" 15 "github.com/Asutorufa/yuhaiin/internal/version" 16 "github.com/Asutorufa/yuhaiin/pkg/components/config" 17 "github.com/Asutorufa/yuhaiin/pkg/components/inbound" 18 "github.com/Asutorufa/yuhaiin/pkg/components/resolver" 19 "github.com/Asutorufa/yuhaiin/pkg/components/shunt" 20 "github.com/Asutorufa/yuhaiin/pkg/components/statistics" 21 "github.com/Asutorufa/yuhaiin/pkg/components/tools" 22 "github.com/Asutorufa/yuhaiin/pkg/log" 23 "github.com/Asutorufa/yuhaiin/pkg/net/dialer" 24 "github.com/Asutorufa/yuhaiin/pkg/net/netapi" 25 "github.com/Asutorufa/yuhaiin/pkg/net/proxy/direct" 26 "github.com/Asutorufa/yuhaiin/pkg/node" 27 pc "github.com/Asutorufa/yuhaiin/pkg/protos/config" 28 "github.com/Asutorufa/yuhaiin/pkg/sysproxy" 29 "github.com/Asutorufa/yuhaiin/pkg/utils/cache" 30 "go.etcd.io/bbolt" 31 32 _ "github.com/Asutorufa/yuhaiin/pkg/net/mux" 33 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/direct" 34 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/drop" 35 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/grpc" 36 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/http" 37 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/http2" 38 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/mixed" 39 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/quic" 40 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/reality" 41 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/reject" 42 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocks" 43 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocksr" 44 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/simple" 45 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/socks4a" 46 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/socks5" 47 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/tls" 48 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/trojan" 49 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/tun" 50 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/vless" 51 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/vmess" 52 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/websocket" 53 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/wireguard" 54 _ "github.com/Asutorufa/yuhaiin/pkg/net/proxy/yuubinsya" 55 ) 56 57 func AddComponent[T any](a *appapi.Start, name string, t T) T { 58 if z, ok := any(t).(config.Observer); ok { 59 a.Setting.AddObserver(z) 60 } 61 62 if z, ok := any(t).(io.Closer); ok { 63 a.AddCloser(name, z) 64 } 65 66 return t 67 } 68 69 func OpenBboltDB(path string) (*bbolt.DB, error) { 70 db, err := bbolt.Open(path, os.ModePerm, &bbolt.Options{Timeout: time.Second * 2}) 71 switch err { 72 case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch: 73 if err = os.Remove(path); err != nil { 74 return nil, fmt.Errorf("remove invalid cache file failed: %w", err) 75 } 76 log.Info("remove invalid cache file and create new one") 77 return bbolt.Open(path, os.ModePerm, &bbolt.Options{Timeout: time.Second}) 78 } 79 80 return db, err 81 } 82 83 func Start(opt appapi.Start) (_ *appapi.Components, err error) { 84 85 so := &opt 86 87 db, err := OpenBboltDB(PathGenerator.Cache(so.ConfigPath)) 88 if err != nil { 89 return nil, fmt.Errorf("init bbolt cache failed: %w", err) 90 } 91 so.AddCloser("bbolt_db", db) 92 93 httpListener, err := net.Listen("tcp", so.Host) 94 if err != nil { 95 return nil, err 96 } 97 so.AddCloser("http_listener", httpListener) 98 99 so.Setting.AddObserver(config.ObserverFunc(func(s *pc.Setting) { 100 log.Set(s.GetLogcat(), PathGenerator.Log(so.ConfigPath)) 101 })) 102 103 fmt.Println(version.Art) 104 log.Info("config", "path", so.ConfigPath, "grpc&http host", so.Host) 105 106 so.Setting.AddObserver(config.ObserverFunc(sysproxy.Update())) 107 so.Setting.AddObserver(config.ObserverFunc(func(s *pc.Setting) { dialer.DefaultInterfaceName = s.GetNetInterface() })) 108 109 // proxy access point/endpoint 110 node := node.NewNodes(PathGenerator.Node(so.ConfigPath)) 111 subscribe := node.Subscribe() 112 tag := node.Tag() 113 114 // make dns flow across all proxy chain 115 dynamicProxy := netapi.NewDynamicProxy(direct.Default) 116 117 // local,remote,bootstrap dns 118 dns := AddComponent(so, "resolver", resolver.NewResolver(dynamicProxy)) 119 // bypass dialer and dns request 120 st := AddComponent(so, "shunt", shunt.NewShunt(node.Outbound(), dns, opt.ProcessDumper)) 121 node.SetRuleTags(st.Tags) 122 // connections' statistic & flow data 123 stcs := AddComponent(so, "statistic", statistics.NewConnStore(cache.NewCache(db, "flow_data"), st)) 124 hosts := AddComponent(so, "hosts", resolver.NewHosts(stcs, st)) 125 // wrap dialer and dns resolver to fake ip, if use 126 fakedns := AddComponent(so, "fakedns", resolver.NewFakeDNS(hosts, hosts, cache.NewCache(db, "fakedns_cache"), cache.NewCache(db, "fakedns_cachev6"))) 127 // dns server/tun dns hijacking handler 128 dnsServer := AddComponent(so, "dnsServer", resolver.NewDNSServer(fakedns)) 129 // make dns flow across all proxy chain 130 dynamicProxy.Set(fakedns) 131 // inbound server 132 _ = AddComponent(so, "inbound_listener", 133 inbound.NewListener(dnsServer, fakedns)) 134 // tools 135 tools := tools.NewTools(fakedns, opt.Setting, st.Update) 136 mux := http.NewServeMux() 137 138 app := &appapi.Components{ 139 Start: so, 140 Mux: mux, 141 HttpListener: httpListener, 142 Tools: tools, 143 Node: node, 144 DB: db, 145 Subscribe: subscribe, 146 Connections: stcs, 147 Tag: tag, 148 } 149 150 // http page 151 web.Server(app) 152 // grpc server 153 app.RegisterGrpcService() 154 155 return app, nil 156 } 157 158 var PathGenerator = pathGenerator{} 159 160 type pathGenerator struct{} 161 162 func (p pathGenerator) Lock(dir string) string { return p.makeDir(filepath.Join(dir, "LOCK")) } 163 func (p pathGenerator) Node(dir string) string { return p.makeDir(filepath.Join(dir, "node.json")) } 164 func (p pathGenerator) Config(dir string) string { return p.makeDir(filepath.Join(dir, "config.json")) } 165 func (p pathGenerator) Log(dir string) string { 166 return p.makeDir(filepath.Join(dir, "log", "yuhaiin.log")) 167 } 168 func (pathGenerator) makeDir(s string) string { 169 if _, err := os.Stat(s); errors.Is(err, os.ErrNotExist) { 170 _ = os.MkdirAll(filepath.Dir(s), os.ModePerm) 171 } 172 173 return s 174 } 175 func (p pathGenerator) Cache(dir string) string { return p.makeDir(filepath.Join(dir, "cache")) }