diff --git a/vpn/tun_windows.go b/vpn/tun_windows.go index a70cb8f28d60d..52778a8a9d08b 100644 --- a/vpn/tun_windows.go +++ b/vpn/tun_windows.go @@ -25,7 +25,12 @@ import ( "github.com/coder/retry" ) -const tunName = "Coder" +const ( + tunName = "Coder" + tunGUID = "{0ed1515d-04a4-4c46-abae-11ad07cf0e6d}" + + wintunDLL = "wintun.dll" +) func GetNetworkingStack(t *Tunnel, _ *StartRequest, logger slog.Logger) (NetworkStack, error) { // Initialize COM process-wide so Tailscale can make calls to the windows @@ -44,12 +49,35 @@ func GetNetworkingStack(t *Tunnel, _ *StartRequest, logger slog.Logger) (Network // Set the name and GUID for the TUN interface. tun.WintunTunnelType = tunName - guid, err := windows.GUIDFromString("{0ed1515d-04a4-4c46-abae-11ad07cf0e6d}") + guid, err := windows.GUIDFromString(tunGUID) if err != nil { - panic(err) + return NetworkStack{}, xerrors.Errorf("could not parse GUID %q: %w", tunGUID, err) } tun.WintunStaticRequestedGUID = &guid + // Ensure wintun.dll is available, and fail early if it's not to avoid + // hanging for 5 minutes in tstunNewWithWindowsRetries. + // + // First, we call wintun.Version() to make the wintun package attempt to + // load wintun.dll. This allows the wintun package to set the logging + // callback in the DLL before we load it ourselves. + _ = wintun.Version() + + // Then, we try to load wintun.dll ourselves so we get a better error + // message if there was a problem. This call matches the wintun package, so + // we're loading it in the same way. + // + // Note: this leaks the handle to wintun.dll, but since it's already loaded + // it wouldn't be freed anyways. + const ( + LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200 + LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 + ) + _, err = windows.LoadLibraryEx(wintunDLL, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32) + if err != nil { + return NetworkStack{}, xerrors.Errorf("could not load %q, it should be in the same directory as the executable (in Coder Desktop, this should have been installed automatically): %w", wintunDLL, err) + } + tunDev, tunName, err := tstunNewWithWindowsRetries(tailnet.Logger(logger.Named("net.tun.device")), tunName) if err != nil { return NetworkStack{}, xerrors.Errorf("create tun device: %w", err)