func startSocksServer(addr string, quit chan bool) (ln net.Listener, err error) { ln, err = net.Listen("tcp", addr) if err != nil { log.Printf("error in listen %s:%s", addr, err) return } ssrv := gosocks.NewBasicServer(addr, socksTimeout) go func() { err := ssrv.Serve(ln) if err != nil { log.Printf("error in serve Socks: %s", err) } // notify other threads to quit close(quit) }() return }
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") }