func (c *fireflyClient) _main() { flag.StringVar(&c.options.localSocksAddr, "local-socks-addr", "127.0.0.1:38250", "SOCKS proxy address") flag.StringVar(&c.options.localHTTPAddr, "local-http-addr", "127.0.0.1:38251", "HTTP proxy address") flag.StringVar(&c.options.localUIAddr, "local-ui-addr", "127.0.0.1:38252", "Web UI address, use random local address when specified address is not available") flag.BoolVar(&c.options.tunnellingAll, "tunnelling-all", false, "whether tunnelling all traffic") flag.StringVar(&c.options.logFilename, "logfile", "", "file to record log") flag.StringVar(&c.options.pidFilename, "pidfile", "", "file to save process id") flag.StringVar(&c.options.landingPage, "landing-page", "https://gofirefly.org/page/", "") flag.StringVar(&c.options.updatePubKey, "update-pubkey-file", "", "PEM encoded RSA public key file, use embedded public key if not specified") flag.StringVar(&c.options.updatePubKey, "update-cacerts", "", "trusted CA certificates for update, use embedded cacerts if not specified") flag.StringVar(&c.options.updateURL, "update-url", "https://update.gofirefly.org/update", "url for auto-update") flag.StringVar(&c.options.trackingID, "tracking-id", "UA-76209591-1", "Google Analytics tracking ID") flag.Parse() var err error c.fs, err = tarfs.New(Resources, "") if err != nil { log.Printf("FATAL: fail to load embedded resources: %s", err) os.Exit(1) } c.appData, err = utils.OpenAppData("firefly") if err != nil { log.Printf("WARNING: unable to load/store customized settings: %s", err) } // initiate log file c.logFile = utils.RotateLog(c.options.logFilename, nil) if c.options.logFilename != "" && c.logFile == nil { log.Printf("WARNING: fail to initiate log file") } // listen SOCKS localSocksAddr := c.options.localSocksAddr c.socksListener, err = net.Listen("tcp", localSocksAddr) if err != nil { log.Printf("FATAL: fail to listen on SOCKS proxy address %s: %s", localSocksAddr, err) os.Exit(1) } // listen HTTP localHTTPAddr := c.options.localHTTPAddr c.httpListener, err = net.Listen("tcp", localHTTPAddr) if err != nil { log.Printf("FATAL: fail to listen on HTTP/S proxy address %s: %s", localHTTPAddr, err) os.Exit(1) } // start tunnel client c.tunnelListener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0}) if err != nil { log.Printf("FATAL: fail to listen on tunnel client (SOCKS): %s", err) os.Exit(1) } handler := &tunnelHandler{ caCerts: c.loadCaCerts(), appData: c.appData, ch: make(chan *tunnelRequest), quit: make(chan bool), auth: sockstun.NewTunnelAnonymousAuthenticator(), } err = handler.loadTunnelPeers(c.fs) if err != nil { log.Printf("FATAL: fail to load tunnel peers") os.Exit(1) } go handler.run() c.tunnelProxy = gosocks.NewServer( c.tunnelListener.Addr().String(), 5*time.Minute, handler, // let handler's authenticator to process SOCKS authentication nil, ) go func() { err := c.tunnelProxy.Serve(c.tunnelListener) if err != nil { log.Printf("FATAL: error to serve tunnel client (SOCKS): %s", err) } c.exit(err) }() tunnelProxyAddr := c.tunnelListener.Addr().String() log.Printf("tunnel proxy (SOCKS) listens on %s", tunnelProxyAddr) // start SOCKS proxy domains := c.loadTunnellingDomains() c.socksHandler = &relayHandler{ basic: &gosocks.BasicSocksHandler{}, embeddedTunnellingDomains: domains, customTunnellingDomains: c.customTunnellingDomains(), tunnellingAll: c.isTunnellingAll(domains), nextHop: tunnelProxyAddr, } c.socksProxy = gosocks.NewServer( localSocksAddr, 5*time.Minute, c.socksHandler, &gosocks.AnonymousServerAuthenticator{}, ) go func() { err := c.socksProxy.Serve(c.socksListener) if err != nil { log.Printf("FATAL: error to serve SOCKS proxy: %s", err) } c.exit(err) }() log.Printf("SOCKS proxy listens on %s", c.options.localSocksAddr) // start HTTP proxy socksDialer := &gosocks.SocksDialer{ Timeout: 5 * time.Minute, Auth: &gosocks.AnonymousClientAuthenticator{}, } http2Socks := chain.GoproxySocksChain{ Chain: chain.HTTPSocksChain{ SocksDialer: socksDialer, SocksAddr: localSocksAddr, }, } c.httpProxy = goproxy.NewProxyHttpServer() c.httpProxy.OnRequest().DoFunc(http2Socks.HTTP) c.httpProxy.OnRequest().HandleConnectFunc(http2Socks.HTTPS) go func() { err := http.Serve(c.httpListener, c.httpProxy) if err != nil { log.Printf("FATAL: error to serve HTTP/S proxy: %s", err) } c.exit(err) }() log.Printf("HTTP/S proxy listens on %s", localHTTPAddr) // i18n c.configureI18n() // start web based UI uiListener, err := net.Listen("tcp", c.options.localUIAddr) if err != nil { log.Printf("fail to listen on specified UI (HTTP) address: %s", err) log.Printf("try to use random local address") uiListener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0}) if err != nil { log.Fatalf("FATAL: fail to listen on UI (HTTP) address: %s", err) } } c.ui = startUI(c, uiListener) // see ui.go go c.uiCommandProc() // set PAC icon, err := c.fs.Get("icons/24.ico") if err != nil { log.Fatalf("Unable to load icon for PAC: %s", err) } err = promptPrivilegeEscalation(icon) if err != nil { log.Fatalf("Unable to escalate priviledge for setting PAC: %s", err) } pacURL := c.ui.handle(pacFilename(), pacHandler(c.httpListener.Addr().String())) enablePAC(pacURL) c.addExitFunc(disablePAC) // systray c.addExitFunc(systray.Quit) c.configureSystray() // clean exit with signals go c.handleSignals() // pid file utils.SavePid(c.options.pidFilename) // open starting pages if c.openSettingsPage() { c.ui.show() } if c.openLandingPage() { if c.openSettingsPage() { // wait to avoid launching new browser window time.Sleep(3 * time.Second) } c.ui.open(c.options.landingPage) } // updater if !c.stopAutoUpdate() { c.startUpdater() } // state, report without using proxy c.state = newState(c.uuid(), c.options.trackingID, nil) go c.state.run() c.state.event("client", "launch") c.waitForExit() os.Exit(0) }
func main() { var opts serverOptions flag.StringVar(&opts.httpAddr, "http-addr", ":80", "http server address") flag.StringVar(&opts.httpsAddr, "https-addr", "", "https server address") flag.StringVar(&opts.certFile, "cert-file", "", "https certificate") flag.StringVar(&opts.keyFile, "key-file", "", "https key file") flag.StringVar(&opts.localSocksAddr, "local-socks-addr", "127.0.0.1:10800", "SOCKS server address") flag.StringVar(&opts.logFilename, "logfile", "", "file to record log") flag.StringVar(&opts.pidFilename, "pidfile", "", "file to save process id") flag.Parse() // initiate log file logFile := utils.RotateLog(opts.logFilename, nil) if opts.logFilename != "" && logFile == nil { log.Printf("WARNING: fail to initiate log file") } // a channel to receive quit signal from server daemons quit := make(chan bool) // start SOCKS server socksListener, err := net.Listen("tcp", opts.localSocksAddr) if err != nil { log.Fatalf("FATAL: fail to listen on SOCKS address %s: %s", opts.localSocksAddr, err) } socksServer := gosocks.NewBasicServer(opts.localSocksAddr, 5*time.Minute) go func() { err := socksServer.Serve(socksListener) if err != nil { log.Printf("FATAL: error to serve SOCKS: %s", err) } close(quit) }() log.Printf("SOCKS server listens on %s", opts.localSocksAddr) // start tunnel server httpListener, err := net.Listen("tcp", opts.httpAddr) if err != nil { log.Fatalf("FATAL: fail to listen on HTTP address %s: %s", opts.httpAddr, err) } connHandler := &tunnelConnHandler{ socksAddr: opts.localSocksAddr, socksTimeout: socksServer.GetTimeout(), socksAuth: sockstun.NewTunnelAnonymousAuthenticator(), } tunnelHandler := httptran.NewPollServerHandler(connHandler.serveWithMux) tunnelHandler.Run() httpServer := &http.Server{ Addr: opts.httpAddr, Handler: tunnelHandler, ReadTimeout: 10 * time.Minute, WriteTimeout: 10 * time.Minute, } go httpServer.Serve(httpListener) log.Printf("HTTP server listens on %s", opts.httpAddr) if opts.httpsAddr != "" && opts.certFile != "" && opts.keyFile != "" { cert, err := tls.LoadX509KeyPair(opts.certFile, opts.keyFile) if err != nil { log.Fatalf("FATAL: fail to load X509 keypair: %s", err) } httpsListener, err := tls.Listen("tcp", opts.httpsAddr, &tls.Config{ Certificates: []tls.Certificate{cert}, }) if err != nil { log.Fatalf("FATAL: fail to listen on HTTPS address %s: %s", opts.httpsAddr, err) } httpsServer := &http.Server{ Addr: opts.httpsAddr, Handler: tunnelHandler, ReadTimeout: 10 * time.Minute, WriteTimeout: 10 * time.Minute, } go httpsServer.Serve(httpsListener) log.Printf("HTTPS server listens on %s", opts.httpsAddr) defer httpsListener.Close() } // pidfile and clean up utils.SavePid(opts.pidFilename) defer socksListener.Close() defer httpListener.Close() // wait for control/quit signals c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) loop: for { select { case <-quit: log.Printf("quit signal received") break loop case s := <-c: switch s { case syscall.SIGINT, syscall.SIGTERM: break loop case syscall.SIGHUP: logFile = utils.RotateLog(opts.logFilename, logFile) } } } log.Printf("done") }