예제 #1
0
func (c *fireflyClient) handleSignals() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	go func() {
		s := <-ch
		switch s {
		case syscall.SIGHUP:
			utils.RotateLog(c.options.logFilename, c.logFile)
		case syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
			log.Printf("Got signal \"%s\", exiting...", s)
			c.exit(nil)
		}
	}()
}
예제 #2
0
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)
}
예제 #3
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")
}