fix(vpn): fail early if wintun.dll is not present (cherry-pick #16707) (#16738)

Cherry-picked fix(vpn): fail early if wintun.dll is not present (#16707)

Prevents the VPN startup from hanging for 5 minutes due to a startup
backoff if `wintun.dll` cannot be loaded.

Because the `wintun` package doesn't expose an easy `Load() error`
method for us, the only way for us to force it to load (without unwanted
side effects) is through `wintun.Version()` which doesn't return an
error message.

So, we call that function so the `wintun` package loads the DLL and
configures the logging properly, then we try to load the DLL ourselves.
`LoadLibraryEx` will not load the library multiple times and returns a
reference to the existing library.

Closes https://github.com/coder/coder-desktop-windows/issues/24

Co-authored-by: Dean Sheather <dean@deansheather.com>
This commit is contained in:
gcp-cherry-pick-bot[bot]
2025-02-28 12:47:59 +05:00
committed by GitHub
parent 34740bc242
commit 780b2714ff

View File

@ -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)