package main import ( "context" "errors" "fmt" "log" "mpbl3p/config" "mpbl3p/flags" "mpbl3p/tun" "os" "os/signal" "strconv" "syscall" ) const ( ENV_NC_TUN_FD = "NC_TUN_FD" ENV_NC_CONFIG_PATH = "NC_CONFIG_PATH" ) func main() { log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) if _, exists := os.LookupEnv(ENV_NC_TUN_FD); !exists { // we are the parent process // 1) process arguments // 2) validate config // 2) create a tun adapter // 3) spawn a child // 4) exit o, err := flags.ParseFlags() if err != nil { if errors.Is(err, flags.PrintedHelpErr) { return } panic(err) } log.Println("loading config...") c, err := config.LoadConfig(o.ConfigFile) if err != nil { log.Fatalf("error validating config: %s", err.Error()) return } log.Println("creating tun adapter...") t, err := tun.NewTun(o.Positional.InterfaceName, int(c.Host.MTU)) if err != nil { panic(err) } if o.Foreground { if err := os.Setenv(ENV_NC_TUN_FD, fmt.Sprintf("%d", t.File().Fd())); err != nil { panic(err) } if err := os.Setenv(ENV_NC_CONFIG_PATH, o.ConfigFile); err != nil { panic(err) } log.Println("switching to foreground") goto FOREGROUND } files := make([]*os.File, 4) files[0], _ = os.Open(os.DevNull) // stdin files[1], _ = os.Open(os.DevNull) // stderr files[2], _ = os.Open(os.DevNull) // stdout files[3] = t.File() env := os.Environ() env = append(env, fmt.Sprintf("%s=3", ENV_NC_TUN_FD)) env = append(env, fmt.Sprintf("%s=%s", ENV_NC_CONFIG_PATH, o.ConfigFile)) attr := os.ProcAttr{ Env: env, Files: files, } path, err := os.Executable() if err != nil { panic(err) } process, err := os.StartProcess( path, os.Args, &attr, ) if err != nil { panic(err) } pidFile, err := os.Create(o.PidFile) if err != nil { panic(err) } if _, err := fmt.Fprintf(pidFile, "%d", process.Pid); err != nil { panic(err) } _ = process.Release() return } // we are the child process // 1) recreate tun adapter from file descriptor // 2) launch proxy FOREGROUND: log.Println("loading config...") c, err := config.LoadConfig(os.Getenv(ENV_NC_CONFIG_PATH)) if err != nil { panic(err) } log.Println("connecting tun adapter...") tunFd, err := strconv.ParseUint(os.Getenv(ENV_NC_TUN_FD), 10, 64) if err != nil { panic(err) } t, err := tun.NewFromFile(uintptr(tunFd), int(c.Host.MTU)) if err != nil { panic(err) } defer func() { if err := t.Close(); err != nil { panic(err) } }() log.Println("building config...") ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, err := c.Build(ctx, t, t) if err != nil { panic(err) } log.Println("starting proxy...") p.Start() log.Println("proxy started") signals := make(chan os.Signal) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) <-signals log.Println("exiting...") }