github.com/bepass-org/wireguard-go@v1.0.4-rc2.0.20240304192354-ebce6572bc24/app/app.go (about) 1 package app 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "log" 8 "net" 9 "net/netip" 10 "os" 11 "path/filepath" 12 "time" 13 14 "github.com/bepass-org/wireguard-go/psiphon" 15 "github.com/bepass-org/wireguard-go/warp" 16 "github.com/bepass-org/wireguard-go/wiresocks" 17 ) 18 19 type WarpOptions struct { 20 LogLevel string 21 Bind netip.AddrPort 22 Endpoint string 23 License string 24 Psiphon *PsiphonOptions 25 Gool bool 26 Scan *ScanOptions 27 } 28 29 type PsiphonOptions struct { 30 Country string 31 } 32 33 type ScanOptions struct { 34 MaxRTT time.Duration 35 } 36 37 func RunWarp(ctx context.Context, opts WarpOptions) error { 38 if opts.Psiphon != nil && opts.Gool { 39 return errors.New("can't use psiphon and gool at the same time") 40 } 41 42 if opts.Psiphon != nil && opts.Psiphon.Country == "" { 43 return errors.New("must provide country for psiphon") 44 } 45 46 //create necessary file structures 47 if err := makeDirs(); err != nil { 48 return err 49 } 50 log.Println("'primary' and 'secondary' directories are ready") 51 52 // Change the current working directory to 'stuff' 53 if err := os.Chdir("stuff"); err != nil { 54 return fmt.Errorf("error changing to 'stuff' directory: %w", err) 55 } 56 log.Println("Changed working directory to 'stuff'") 57 58 //create identities 59 if err := createPrimaryAndSecondaryIdentities(opts.License); err != nil { 60 return err 61 } 62 63 //Decide Working Scenario 64 endpoints := []string{opts.Endpoint, opts.Endpoint} 65 66 if opts.Scan != nil { 67 var err error 68 endpoints, err = wiresocks.RunScan(ctx, opts.Scan.MaxRTT) 69 if err != nil { 70 return err 71 } 72 } 73 74 var warpErr error 75 switch { 76 case opts.Psiphon != nil: 77 // run primary warp on a random tcp port and run psiphon on bind address 78 warpErr = runWarpWithPsiphon(ctx, opts.Bind, endpoints, opts.Psiphon.Country, opts.LogLevel == "debug") 79 case opts.Gool: 80 // run warp in warp 81 warpErr = runWarpInWarp(ctx, opts.Bind, endpoints, opts.LogLevel == "debug") 82 default: 83 // just run primary warp on bindAddress 84 _, _, warpErr = runWarp(ctx, opts.Bind, endpoints, "./primary/wgcf-profile.ini", opts.LogLevel == "debug", true, true) 85 } 86 87 return warpErr 88 } 89 90 func runWarp(ctx context.Context, bind netip.AddrPort, endpoints []string, confPath string, verbose, startProxy bool, trick bool) (*wiresocks.VirtualTun, int, error) { 91 conf, err := wiresocks.ParseConfig(confPath, endpoints[0]) 92 if err != nil { 93 log.Println(err) 94 return nil, 0, err 95 } 96 97 if trick { 98 conf.Device.Trick = trick 99 } 100 101 tnet, err := wiresocks.StartWireguard(ctx, conf.Device, verbose) 102 if err != nil { 103 log.Println(err) 104 return nil, 0, err 105 } 106 107 if startProxy { 108 tnet.StartProxy(bind) 109 } 110 111 return tnet, conf.Device.MTU, nil 112 } 113 114 func runWarpWithPsiphon(ctx context.Context, bind netip.AddrPort, endpoints []string, country string, verbose bool) error { 115 // make a random bind address for warp 116 warpBindAddress, err := findFreePort("tcp") 117 if err != nil { 118 log.Println("There are no free tcp ports on Device!") 119 return err 120 } 121 122 _, _, err = runWarp(ctx, warpBindAddress, endpoints, "./primary/wgcf-profile.ini", verbose, true, true) 123 if err != nil { 124 return err 125 } 126 127 // run psiphon 128 err = psiphon.RunPsiphon(warpBindAddress.String(), bind.String(), country, ctx) 129 if err != nil { 130 log.Printf("unable to run psiphon %v", err) 131 return fmt.Errorf("unable to run psiphon %v", err) 132 } 133 134 log.Printf("Serving on %s", bind) 135 136 return nil 137 } 138 139 func runWarpInWarp(ctx context.Context, bind netip.AddrPort, endpoints []string, verbose bool) error { 140 // run secondary warp 141 vTUN, mtu, err := runWarp(ctx, netip.AddrPort{}, endpoints, "./secondary/wgcf-profile.ini", verbose, false, true) 142 if err != nil { 143 return err 144 } 145 146 // run virtual endpoint 147 virtualEndpointBindAddress, err := findFreePort("udp") 148 if err != nil { 149 log.Println("There are no free udp ports on Device!") 150 return err 151 } 152 addr := endpoints[1] 153 err = wiresocks.NewVtunUDPForwarder(virtualEndpointBindAddress.String(), addr, vTUN, mtu+100, ctx) 154 if err != nil { 155 log.Println(err) 156 return err 157 } 158 159 // run primary warp 160 _, _, err = runWarp(ctx, bind, []string{virtualEndpointBindAddress.String()}, "./primary/wgcf-profile.ini", verbose, true, false) 161 if err != nil { 162 return err 163 } 164 return nil 165 } 166 167 func findFreePort(network string) (netip.AddrPort, error) { 168 if network == "udp" { 169 addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") 170 if err != nil { 171 return netip.AddrPort{}, err 172 } 173 174 conn, err := net.ListenUDP("udp", addr) 175 if err != nil { 176 return netip.AddrPort{}, err 177 } 178 defer conn.Close() 179 180 return netip.MustParseAddrPort(conn.LocalAddr().String()), nil 181 } 182 // Listen on TCP port 0, which tells the OS to pick a free port. 183 listener, err := net.Listen(network, "127.0.0.1:0") 184 if err != nil { 185 return netip.AddrPort{}, err // Return error if unable to listen on a port 186 } 187 defer listener.Close() // Ensure the listener is closed when the function returns 188 189 // Get the port from the listener's address 190 return netip.MustParseAddrPort(listener.Addr().String()), nil 191 } 192 193 func createPrimaryAndSecondaryIdentities(license string) error { 194 // make primary identity 195 warp.UpdatePath("./primary") 196 if !warp.CheckProfileExists(license) { 197 err := warp.LoadOrCreateIdentity(license) 198 if err != nil { 199 log.Printf("error: %v", err) 200 return fmt.Errorf("error: %v", err) 201 } 202 } 203 // make secondary 204 warp.UpdatePath("./secondary") 205 if !warp.CheckProfileExists(license) { 206 err := warp.LoadOrCreateIdentity(license) 207 if err != nil { 208 log.Printf("error: %v", err) 209 return fmt.Errorf("error: %v", err) 210 } 211 } 212 return nil 213 } 214 215 func makeDirs() error { 216 stuffDir := "stuff" 217 primaryDir := "primary" 218 secondaryDir := "secondary" 219 220 // Check if 'stuff' directory exists, if not create it 221 if _, err := os.Stat(stuffDir); os.IsNotExist(err) { 222 if err := os.Mkdir(stuffDir, 0755); err != nil { 223 return fmt.Errorf("error creating 'stuff' directory: %w", err) 224 } 225 } 226 227 // Create 'primary' and 'secondary' directories if they don't exist 228 for _, dir := range []string{primaryDir, secondaryDir} { 229 if _, err := os.Stat(filepath.Join(stuffDir, dir)); os.IsNotExist(err) { 230 if err := os.Mkdir(filepath.Join(stuffDir, dir), 0755); err != nil { 231 return fmt.Errorf("error creating '%s' directory: %w", dir, err) 232 } 233 } 234 } 235 236 return nil 237 }