Files
euphoria-windows/interfacewatcher.go
2025-02-24 09:00:40 +01:00

158 lines
4.2 KiB
Go

/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
* Copyright (C) 2021 Mozilla Foundation. All Rights Reserved.
*/
package main
import (
"log"
"sync"
"golang.org/x/sys/windows"
"github.com/amnezia-vpn/euphoria/conn"
"github.com/amnezia-vpn/euphoria/tun"
"github.com/amnezia-vpn/euphoria-windows/conf"
"github.com/amnezia-vpn/euphoria-windows/services"
"github.com/amnezia-vpn/euphoria-windows/tunnel/firewall"
"github.com/amnezia-vpn/euphoria-windows/tunnel/winipcfg"
)
type interfaceWatcherError struct {
serviceError services.Error
err error
}
type interfaceWatcherEvent struct {
luid winipcfg.LUID
family winipcfg.AddressFamily
}
type interfaceWatcher struct {
errors chan interfaceWatcherError
binder conn.BindSocketToInterface
conf *conf.Config
tun *tun.NativeTun
setupMutex sync.Mutex
interfaceChangeCallback winipcfg.ChangeCallback
changeCallbacks4 []winipcfg.ChangeCallback
changeCallbacks6 []winipcfg.ChangeCallback
storedEvents []interfaceWatcherEvent
}
func (iw *interfaceWatcher) setup(family winipcfg.AddressFamily) {
var changeCallbacks *[]winipcfg.ChangeCallback
var ipversion string
if family == windows.AF_INET {
changeCallbacks = &iw.changeCallbacks4
ipversion = "v4"
} else if family == windows.AF_INET6 {
changeCallbacks = &iw.changeCallbacks6
ipversion = "v6"
} else {
return
}
if len(*changeCallbacks) != 0 {
for _, cb := range *changeCallbacks {
cb.Unregister()
}
*changeCallbacks = nil
}
var err error
log.Printf("Setting device %s addresses", ipversion)
err = configureInterface(family, iw.conf, iw.tun)
if err != nil {
iw.errors <- interfaceWatcherError{services.ErrorSetNetConfig, err}
return
}
}
func watchInterface() (*interfaceWatcher, error) {
iw := &interfaceWatcher{
errors: make(chan interfaceWatcherError, 2),
}
var err error
iw.interfaceChangeCallback, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
iw.setupMutex.Lock()
defer iw.setupMutex.Unlock()
if notificationType != winipcfg.MibAddInstance {
return
}
if iw.tun == nil {
iw.storedEvents = append(iw.storedEvents, interfaceWatcherEvent{iface.InterfaceLUID, iface.Family})
return
}
if iface.InterfaceLUID != winipcfg.LUID(iw.tun.LUID()) {
return
}
iw.setup(iface.Family)
})
if err != nil {
return nil, err
}
return iw, nil
}
func (iw *interfaceWatcher) Configure(binder conn.BindSocketToInterface, conf *conf.Config, tun *tun.NativeTun) {
iw.setupMutex.Lock()
defer iw.setupMutex.Unlock()
iw.binder, iw.conf, iw.tun = binder, conf, tun
for _, event := range iw.storedEvents {
if event.luid == winipcfg.LUID(iw.tun.LUID()) {
iw.setup(event.family)
}
}
iw.storedEvents = nil
}
func (iw *interfaceWatcher) Destroy() {
iw.setupMutex.Lock()
changeCallbacks4 := iw.changeCallbacks4
changeCallbacks6 := iw.changeCallbacks6
interfaceChangeCallback := iw.interfaceChangeCallback
tun := iw.tun
iw.setupMutex.Unlock()
if interfaceChangeCallback != nil {
interfaceChangeCallback.Unregister()
}
for _, cb := range changeCallbacks4 {
cb.Unregister()
}
for _, cb := range changeCallbacks6 {
cb.Unregister()
}
iw.setupMutex.Lock()
if interfaceChangeCallback == iw.interfaceChangeCallback {
iw.interfaceChangeCallback = nil
}
for len(changeCallbacks4) > 0 && len(iw.changeCallbacks4) > 0 {
iw.changeCallbacks4 = iw.changeCallbacks4[1:]
changeCallbacks4 = changeCallbacks4[1:]
}
for len(changeCallbacks6) > 0 && len(iw.changeCallbacks6) > 0 {
iw.changeCallbacks6 = iw.changeCallbacks6[1:]
changeCallbacks6 = changeCallbacks6[1:]
}
firewall.DisableFirewall()
if tun != nil && iw.tun == tun {
// It seems that the Windows networking stack doesn't like it when we destroy interfaces that have active
// routes, so to be certain, just remove everything before destroying.
luid := winipcfg.LUID(tun.LUID())
luid.FlushRoutes(windows.AF_INET)
luid.FlushIPAddresses(windows.AF_INET)
luid.FlushDNS(windows.AF_INET)
luid.FlushRoutes(windows.AF_INET6)
luid.FlushIPAddresses(windows.AF_INET6)
luid.FlushDNS(windows.AF_INET6)
}
iw.setupMutex.Unlock()
}