github.com/apernet/sing-tun@v0.2.6-0.20240323130332-b9f6511036ad/internal/winipcfg/interface_change_handler.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package winipcfg 7 8 import ( 9 "sync" 10 11 "golang.org/x/sys/windows" 12 ) 13 14 // InterfaceChangeCallback structure allows interface change callback handling. 15 type InterfaceChangeCallback struct { 16 cb func(notificationType MibNotificationType, iface *MibIPInterfaceRow) 17 wait sync.WaitGroup 18 } 19 20 var ( 21 interfaceChangeAddRemoveMutex = sync.Mutex{} 22 interfaceChangeMutex = sync.Mutex{} 23 interfaceChangeCallbacks = make(map[*InterfaceChangeCallback]bool) 24 interfaceChangeHandle = windows.Handle(0) 25 ) 26 27 // RegisterInterfaceChangeCallback registers a new InterfaceChangeCallback. If this particular callback is already 28 // registered, the function will silently return. Returned InterfaceChangeCallback.Unregister method should be used 29 // to unregister. 30 func RegisterInterfaceChangeCallback(callback func(notificationType MibNotificationType, iface *MibIPInterfaceRow)) (*InterfaceChangeCallback, error) { 31 s := &InterfaceChangeCallback{cb: callback} 32 33 interfaceChangeAddRemoveMutex.Lock() 34 defer interfaceChangeAddRemoveMutex.Unlock() 35 36 interfaceChangeMutex.Lock() 37 defer interfaceChangeMutex.Unlock() 38 39 interfaceChangeCallbacks[s] = true 40 41 if interfaceChangeHandle == 0 { 42 err := notifyIPInterfaceChange(windows.AF_UNSPEC, windows.NewCallback(interfaceChanged), 0, false, &interfaceChangeHandle) 43 if err != nil { 44 delete(interfaceChangeCallbacks, s) 45 interfaceChangeHandle = 0 46 return nil, err 47 } 48 } 49 50 return s, nil 51 } 52 53 // Unregister unregisters the callback. 54 func (callback *InterfaceChangeCallback) Unregister() error { 55 interfaceChangeAddRemoveMutex.Lock() 56 defer interfaceChangeAddRemoveMutex.Unlock() 57 58 interfaceChangeMutex.Lock() 59 delete(interfaceChangeCallbacks, callback) 60 removeIt := len(interfaceChangeCallbacks) == 0 && interfaceChangeHandle != 0 61 interfaceChangeMutex.Unlock() 62 63 callback.wait.Wait() 64 65 if removeIt { 66 err := cancelMibChangeNotify2(interfaceChangeHandle) 67 if err != nil { 68 return err 69 } 70 interfaceChangeHandle = 0 71 } 72 73 return nil 74 } 75 76 func interfaceChanged(callerContext uintptr, row *MibIPInterfaceRow, notificationType MibNotificationType) uintptr { 77 rowCopy := *row 78 interfaceChangeMutex.Lock() 79 for cb := range interfaceChangeCallbacks { 80 cb.wait.Add(1) 81 go func(cb *InterfaceChangeCallback) { 82 cb.cb(notificationType, &rowCopy) 83 cb.wait.Done() 84 }(cb) 85 } 86 interfaceChangeMutex.Unlock() 87 return 0 88 }