repo_description: 一款轻量级、高性能、功能强大的内网穿透代理服务器。支持tcp、udp、socks5、http等几乎所有流量转发，可用来访问内网网站、本地支付接口调试、ssh访问、远程桌面，内网dns解析、内网socks5代理等等……，并带有功能强大的web管理端。a lightweight, high-performance, powerful intranet penetration proxy server, with a powerful web management terminal.
repo_topics: dns firewall go gzip http https nat nps snnapy socks socks5 ssh tcp tunnel udp
code: package main
  
  import (
  	"flag"
  	"log"
  	"os"
  	"os/exec"
  	"path/filepath"
  	"runtime"
  	"strings"
  	"sync"
  
  	"ehang.io/nps/lib/file"
  	"ehang.io/nps/lib/install"
  	"ehang.io/nps/lib/version"
  	"ehang.io/nps/server"
  	"ehang.io/nps/server/connection"
  	"ehang.io/nps/server/tool"
  	"ehang.io/nps/web/routers"
  
  	"ehang.io/nps/lib/common"
  	"ehang.io/nps/lib/crypt"
  	"ehang.io/nps/lib/daemon"
  	"github.com/astaxie/beego"
  	"github.com/astaxie/beego/logs"
  
  	"github.com/kardianos/service"
  )
  
  var (
  	level string
  	ver   = flag.Bool("version", false, "show current version")
  )
  
  func main() {
  	flag.Parse()
  	// init log
  	if *ver {
  		common.PrintVersion()
  		return
  	}
  	if err := beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")); err != nil {
  		log.Fatalln("load config file error", err.Error())
  	}
  	common.InitPProfFromFile()
  	if level = beego.AppConfig.String("log_level"); level == "" {
  		level = "7"
  	}
  	logs.Reset()
  	logs.EnableFuncCallDepth(true)
  	logs.SetLogFuncCallDepth(3)
  	logPath := beego.AppConfig.String("log_path")
  	if logPath == "" {
  		logPath = common.GetLogPath()
  	}
  	if common.IsWindows() {
  		logPath = strings.Replace(logPath, "\\", "\\\\", -1)
  	}
  	// init service
  	options := make(service.KeyValue)
  	svcConfig := &service.Config{
  		Name:        "Nps",
  		DisplayName: "nps内网穿透代理服务器",
  		Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发，支持内网http代理、内网socks5代理，同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理，集成多用户模式。",
  		Option:      options,
  	}
  	svcConfig.Arguments = append(svcConfig.Arguments, "service")
  	if len(os.Args) > 1 && os.Args[1] == "service" {
  		_ = logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+logPath+`","daily":false,"maxlines":100000,"color":true}`)
  	} else {
  		_ = logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
  	}
  	if !common.IsWindows() {
  		svcConfig.Dependencies = []string{
  			"Requires=network.target",
  			"After=network-online.target syslog.target"}
  		svcConfig.Option["SystemdScript"] = install.SystemdScript
  		svcConfig.Option["SysvScript"] = install.SysvScript
  	}
  	prg := &nps{}
  	prg.exit = make(chan struct{})
  	s, err := service.New(prg, svcConfig)
  	if err != nil {
  		logs.Error(err, "service function disabled")
  		run()
  		// run without service
  		wg := sync.WaitGroup{}
  		wg.Add(1)
  		wg.Wait()
  		return
  	}
  	if len(os.Args) > 1 && os.Args[1] != "service" {
  		switch os.Args[1] {
  		case "reload":
  			daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
  			return
  		case "install":
  			// uninstall before
  			_ = service.Control(s, "stop")
  			_ = service.Control(s, "uninstall")
  
  			binPath := install.InstallNps()
  			svcConfig.Executable = binPath
  			s, err := service.New(prg, svcConfig)
  			if err != nil {
  				logs.Error(err)
  				return
  			}
  			err = service.Control(s, os.Args[1])
  			if err != nil {
  				logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
  			}
  			if service.Platform() == "unix-systemv" {
  				logs.Info("unix-systemv service")
  				confPath := "/etc/init.d/" + svcConfig.Name
  				os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name)
  				os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name)
  			}
  			return
  		case "start", "restart", "stop":
  			if service.Platform() == "unix-systemv" {
  				logs.Info("unix-systemv service")
  				cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1])
  				err := cmd.Run()
  				if err != nil {
  					logs.Error(err)
  				}
  				return
  			}
  			err := service.Control(s, os.Args[1])
  			if err != nil {
  				logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
  			}
  			return
  		case "uninstall":
  			err := service.Control(s, os.Args[1])
  			if err != nil {
  				logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
  			}
  			if service.Platform() == "unix-systemv" {
  				logs.Info("unix-systemv service")
  				os.Remove("/etc/rc.d/S90" + svcConfig.Name)
  				os.Remove("/etc/rc.d/K02" + svcConfig.Name)
  			}
  			return
  		case "update":
  			install.UpdateNps()
  			return
  		default:
  			logs.Error("command is not support")
  			return
  		}
  	}
  	_ = s.Run()
  }
  
  type nps struct {
  	exit chan struct{}
  }
  
  func (p *nps) Start(s service.Service) error {
  	_, _ = s.Status()
  	go p.run()
  	return nil
  }
  func (p *nps) Stop(s service.Service) error {
  	_, _ = s.Status()
  	close(p.exit)
  	if service.Interactive() {
  		os.Exit(0)
  	}
  	return nil
  }
  
  func (p *nps) run() error {
  	defer func() {
  		if err := recover(); err != nil {
  			const size = 64 << 10
  			buf := make([]byte, size)
  			buf = buf[:runtime.Stack(buf, false)]
  			logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf))
  		}
  	}()
  	run()
  	select {
  	case <-p.exit:
  		logs.Warning("stop...")
  	}
  	return nil
  }
  
  func run() {
  	routers.Init()
  	task := &file.Tunnel{
  		Mode: "webServer",
  	}
  	bridgePort, err := beego.AppConfig.Int("bridge_port")
  	if err != nil {
  		logs.Error("Getting bridge_port error", err)
  		os.Exit(0)
  	}
  	logs.Info("the version of server is %s ,allow client core version to be %s", version.VERSION, version.GetVersion())
  	connection.InitConnectionService()
  	//crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key"))
  	crypt.InitTls()
  	tool.InitAllowPort()
  	tool.StartSystemInfo()
  	timeout, err := beego.AppConfig.Int("disconnect_timeout")
  	if err != nil {
  		timeout = 60
  	}
  	go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"), timeout)
  }
  