Updates tailscale/corp#553 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>main
parent
85c3d17b3c
commit
dbb4c246fa
@ -0,0 +1,131 @@ |
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package monitor |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"sync" |
||||
"syscall" |
||||
"time" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
"tailscale.com/types/logger" |
||||
) |
||||
|
||||
var ( |
||||
iphlpapi = syscall.NewLazyDLL("iphlpapi.dll") |
||||
notifyAddrChangeProc = iphlpapi.NewProc("NotifyAddrChange") |
||||
notifyRouteChangeProc = iphlpapi.NewProc("NotifyRouteChange") |
||||
) |
||||
|
||||
type unspecifiedMessage struct{} |
||||
|
||||
func (unspecifiedMessage) ignore() bool { return false } |
||||
|
||||
type winMon struct { |
||||
ctx context.Context |
||||
cancel context.CancelFunc |
||||
|
||||
logf logger.Logf |
||||
|
||||
mu sync.Mutex |
||||
event windows.Handle |
||||
} |
||||
|
||||
func newOSMon(logf logger.Logf) (osMon, error) { |
||||
ctx, cancel := context.WithCancel(context.Background()) |
||||
return &winMon{ |
||||
logf: logf, |
||||
ctx: ctx, |
||||
cancel: cancel, |
||||
}, nil |
||||
} |
||||
|
||||
func (m *winMon) Close() error { |
||||
m.cancel() |
||||
|
||||
m.mu.Lock() |
||||
defer m.mu.Unlock() |
||||
if h := m.event; h != 0 { |
||||
// Wake up any reader blocked in Receive.
|
||||
windows.SetEvent(h) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
var errClosed = errors.New("closed") |
||||
|
||||
func (m *winMon) Receive() (message, error) { |
||||
if m.ctx.Err() != nil { |
||||
return nil, errClosed |
||||
} |
||||
|
||||
var o windows.Overlapped |
||||
h, err := windows.CreateEvent(nil, 1 /* true*/, 0 /* unsignaled */, nil /* no name */) |
||||
if err != nil { |
||||
m.logf("CreateEvent: %v", err) |
||||
return nil, err |
||||
} |
||||
defer windows.CloseHandle(h) |
||||
|
||||
m.mu.Lock() |
||||
m.event = h |
||||
m.mu.Unlock() |
||||
|
||||
o.HEvent = h |
||||
|
||||
err = notifyAddrChange(&h, &o) |
||||
if err != nil { |
||||
m.logf("notifyAddrChange: %v", err) |
||||
return nil, err |
||||
} |
||||
err = notifyRouteChange(&h, &o) |
||||
if err != nil { |
||||
m.logf("notifyRouteChange: %v", err) |
||||
return nil, err |
||||
} |
||||
|
||||
t0 := time.Now() |
||||
evt, err := windows.WaitForSingleObject(o.HEvent, windows.INFINITE) |
||||
if m.ctx.Err() != nil { |
||||
return nil, errClosed |
||||
} |
||||
if err != nil { |
||||
m.logf("notifyRouteChange: %v", err) |
||||
return nil, err |
||||
} |
||||
d := time.Since(t0) |
||||
m.logf("got windows change event after %v: %+v", d, evt) |
||||
|
||||
m.mu.Lock() |
||||
m.event = 0 |
||||
m.mu.Unlock() |
||||
|
||||
return unspecifiedMessage{}, nil |
||||
} |
||||
|
||||
func notifyAddrChange(h *windows.Handle, o *windows.Overlapped) error { |
||||
return callNotifyProc(notifyAddrChangeProc, h, o) |
||||
} |
||||
|
||||
func notifyRouteChange(h *windows.Handle, o *windows.Overlapped) error { |
||||
return callNotifyProc(notifyAddrChangeProc, h, o) |
||||
} |
||||
|
||||
func callNotifyProc(p *syscall.LazyProc, h *windows.Handle, o *windows.Overlapped) error { |
||||
r1, _, e1 := p.Call(uintptr(unsafe.Pointer(h)), uintptr(unsafe.Pointer(o))) |
||||
expect := uintptr(0) |
||||
if h != nil || o != nil { |
||||
const ERROR_IO_PENDING = 997 |
||||
expect = ERROR_IO_PENDING |
||||
} |
||||
if r1 == expect { |
||||
return nil |
||||
} |
||||
return e1 |
||||
} |
||||
Loading…
Reference in new issue