github.com/metacubex/sing-tun@v0.2.7-0.20240512075008-89e7c6208eec/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  }