dll.go raw

   1  //go:build windows
   2  
   3  /* SPDX-License-Identifier: MIT
   4   *
   5   * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
   6   */
   7  
   8  package wintun
   9  
  10  import (
  11  	"fmt"
  12  	"sync"
  13  	"sync/atomic"
  14  	"unsafe"
  15  
  16  	"golang.org/x/sys/windows"
  17  )
  18  
  19  func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
  20  	return &lazyDLL{Name: name, onLoad: onLoad}
  21  }
  22  
  23  func (d *lazyDLL) NewProc(name string) *lazyProc {
  24  	return &lazyProc{dll: d, Name: name}
  25  }
  26  
  27  type lazyProc struct {
  28  	Name string
  29  	mu   sync.Mutex
  30  	dll  *lazyDLL
  31  	addr uintptr
  32  }
  33  
  34  func (p *lazyProc) Find() error {
  35  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
  36  		return nil
  37  	}
  38  	p.mu.Lock()
  39  	defer p.mu.Unlock()
  40  	if p.addr != 0 {
  41  		return nil
  42  	}
  43  
  44  	err := p.dll.Load()
  45  	if err != nil {
  46  		return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err)
  47  	}
  48  	addr, err := p.nameToAddr()
  49  	if err != nil {
  50  		return fmt.Errorf("Error getting %v address: %w", p.Name, err)
  51  	}
  52  
  53  	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
  54  	return nil
  55  }
  56  
  57  func (p *lazyProc) Addr() uintptr {
  58  	err := p.Find()
  59  	if err != nil {
  60  		panic(err)
  61  	}
  62  	return p.addr
  63  }
  64  
  65  type lazyDLL struct {
  66  	Name   string
  67  	mu     sync.Mutex
  68  	module windows.Handle
  69  	onLoad func(d *lazyDLL)
  70  }
  71  
  72  func (d *lazyDLL) Load() error {
  73  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
  74  		return nil
  75  	}
  76  	d.mu.Lock()
  77  	defer d.mu.Unlock()
  78  	if d.module != 0 {
  79  		return nil
  80  	}
  81  
  82  	const (
  83  		LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
  84  		LOAD_LIBRARY_SEARCH_SYSTEM32        = 0x00000800
  85  	)
  86  	module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
  87  	if err != nil {
  88  		return fmt.Errorf("Unable to load library: %w", err)
  89  	}
  90  
  91  	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
  92  	if d.onLoad != nil {
  93  		d.onLoad(d)
  94  	}
  95  	return nil
  96  }
  97  
  98  func (p *lazyProc) nameToAddr() (uintptr, error) {
  99  	return windows.GetProcAddress(p.dll.module, p.Name)
 100  }
 101  
 102  // Version returns the version of the Wintun DLL.
 103  func Version() string {
 104  	if modwintun.Load() != nil {
 105  		return "unknown"
 106  	}
 107  	resInfo, err := windows.FindResource(modwintun.module, windows.ResourceID(1), windows.RT_VERSION)
 108  	if err != nil {
 109  		return "unknown"
 110  	}
 111  	data, err := windows.LoadResourceData(modwintun.module, resInfo)
 112  	if err != nil {
 113  		return "unknown"
 114  	}
 115  
 116  	var fixedInfo *windows.VS_FIXEDFILEINFO
 117  	fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
 118  	err = windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen)
 119  	if err != nil {
 120  		return "unknown"
 121  	}
 122  	version := fmt.Sprintf("%d.%d", (fixedInfo.FileVersionMS>>16)&0xff, (fixedInfo.FileVersionMS>>0)&0xff)
 123  	if nextNibble := (fixedInfo.FileVersionLS >> 16) & 0xff; nextNibble != 0 {
 124  		version += fmt.Sprintf(".%d", nextNibble)
 125  	}
 126  	if nextNibble := (fixedInfo.FileVersionLS >> 0) & 0xff; nextNibble != 0 {
 127  		version += fmt.Sprintf(".%d", nextNibble)
 128  	}
 129  	return version
 130  }
 131