mirror of
https://github.com/KusakabeShi/EtherGuard-VPN.git
synced 2024-11-21 23:03:08 +01:00
delete unused part(tun/allow_ip)
This commit is contained in:
parent
7801b56b41
commit
1fa36f77ac
@ -1,311 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"net"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type parentIndirection struct {
|
||||
parentBit **trieEntry
|
||||
parentBitType uint8
|
||||
}
|
||||
|
||||
type trieEntry struct {
|
||||
peer *Peer
|
||||
child [2]*trieEntry
|
||||
parent parentIndirection
|
||||
cidr uint8
|
||||
bitAtByte uint8
|
||||
bitAtShift uint8
|
||||
bits net.IP
|
||||
perPeerElem *list.Element
|
||||
}
|
||||
|
||||
func isLittleEndian() bool {
|
||||
one := uint32(1)
|
||||
return *(*byte)(unsafe.Pointer(&one)) != 0
|
||||
}
|
||||
|
||||
func swapU32(i uint32) uint32 {
|
||||
if !isLittleEndian() {
|
||||
return i
|
||||
}
|
||||
|
||||
return bits.ReverseBytes32(i)
|
||||
}
|
||||
|
||||
func swapU64(i uint64) uint64 {
|
||||
if !isLittleEndian() {
|
||||
return i
|
||||
}
|
||||
|
||||
return bits.ReverseBytes64(i)
|
||||
}
|
||||
|
||||
func commonBits(ip1 net.IP, ip2 net.IP) uint8 {
|
||||
size := len(ip1)
|
||||
if size == net.IPv4len {
|
||||
a := (*uint32)(unsafe.Pointer(&ip1[0]))
|
||||
b := (*uint32)(unsafe.Pointer(&ip2[0]))
|
||||
x := *a ^ *b
|
||||
return uint8(bits.LeadingZeros32(swapU32(x)))
|
||||
} else if size == net.IPv6len {
|
||||
a := (*uint64)(unsafe.Pointer(&ip1[0]))
|
||||
b := (*uint64)(unsafe.Pointer(&ip2[0]))
|
||||
x := *a ^ *b
|
||||
if x != 0 {
|
||||
return uint8(bits.LeadingZeros64(swapU64(x)))
|
||||
}
|
||||
a = (*uint64)(unsafe.Pointer(&ip1[8]))
|
||||
b = (*uint64)(unsafe.Pointer(&ip2[8]))
|
||||
x = *a ^ *b
|
||||
return 64 + uint8(bits.LeadingZeros64(swapU64(x)))
|
||||
} else {
|
||||
panic("Wrong size bit string")
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) addToPeerEntries() {
|
||||
node.perPeerElem = node.peer.trieEntries.PushBack(node)
|
||||
}
|
||||
|
||||
func (node *trieEntry) removeFromPeerEntries() {
|
||||
if node.perPeerElem != nil {
|
||||
node.peer.trieEntries.Remove(node.perPeerElem)
|
||||
node.perPeerElem = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) choose(ip net.IP) byte {
|
||||
return (ip[node.bitAtByte] >> node.bitAtShift) & 1
|
||||
}
|
||||
|
||||
func (node *trieEntry) maskSelf() {
|
||||
mask := net.CIDRMask(int(node.cidr), len(node.bits)*8)
|
||||
for i := 0; i < len(mask); i++ {
|
||||
node.bits[i] &= mask[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) zeroizePointers() {
|
||||
// Make the garbage collector's life slightly easier
|
||||
node.peer = nil
|
||||
node.child[0] = nil
|
||||
node.child[1] = nil
|
||||
node.parent.parentBit = nil
|
||||
}
|
||||
|
||||
func (node *trieEntry) nodePlacement(ip net.IP, cidr uint8) (parent *trieEntry, exact bool) {
|
||||
for node != nil && node.cidr <= cidr && commonBits(node.bits, ip) >= node.cidr {
|
||||
parent = node
|
||||
if parent.cidr == cidr {
|
||||
exact = true
|
||||
return
|
||||
}
|
||||
bit := node.choose(ip)
|
||||
node = node.child[bit]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (trie parentIndirection) insert(ip net.IP, cidr uint8, peer *Peer) {
|
||||
if *trie.parentBit == nil {
|
||||
node := &trieEntry{
|
||||
peer: peer,
|
||||
parent: trie,
|
||||
bits: ip,
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
node.maskSelf()
|
||||
node.addToPeerEntries()
|
||||
*trie.parentBit = node
|
||||
return
|
||||
}
|
||||
node, exact := (*trie.parentBit).nodePlacement(ip, cidr)
|
||||
if exact {
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = peer
|
||||
node.addToPeerEntries()
|
||||
return
|
||||
}
|
||||
|
||||
newNode := &trieEntry{
|
||||
peer: peer,
|
||||
bits: ip,
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
newNode.maskSelf()
|
||||
newNode.addToPeerEntries()
|
||||
|
||||
var down *trieEntry
|
||||
if node == nil {
|
||||
down = *trie.parentBit
|
||||
} else {
|
||||
bit := node.choose(ip)
|
||||
down = node.child[bit]
|
||||
if down == nil {
|
||||
newNode.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = newNode
|
||||
return
|
||||
}
|
||||
}
|
||||
common := commonBits(down.bits, ip)
|
||||
if common < cidr {
|
||||
cidr = common
|
||||
}
|
||||
parent := node
|
||||
|
||||
if newNode.cidr == cidr {
|
||||
bit := newNode.choose(down.bits)
|
||||
down.parent = parentIndirection{&newNode.child[bit], bit}
|
||||
newNode.child[bit] = down
|
||||
if parent == nil {
|
||||
newNode.parent = trie
|
||||
*trie.parentBit = newNode
|
||||
} else {
|
||||
bit := parent.choose(newNode.bits)
|
||||
newNode.parent = parentIndirection{&parent.child[bit], bit}
|
||||
parent.child[bit] = newNode
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
node = &trieEntry{
|
||||
bits: append([]byte{}, newNode.bits...),
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
node.maskSelf()
|
||||
|
||||
bit := node.choose(down.bits)
|
||||
down.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = down
|
||||
bit = node.choose(newNode.bits)
|
||||
newNode.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = newNode
|
||||
if parent == nil {
|
||||
node.parent = trie
|
||||
*trie.parentBit = node
|
||||
} else {
|
||||
bit := parent.choose(node.bits)
|
||||
node.parent = parentIndirection{&parent.child[bit], bit}
|
||||
parent.child[bit] = node
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) lookup(ip net.IP) *Peer {
|
||||
var found *Peer
|
||||
size := uint8(len(ip))
|
||||
for node != nil && commonBits(node.bits, ip) >= node.cidr {
|
||||
if node.peer != nil {
|
||||
found = node.peer
|
||||
}
|
||||
if node.bitAtByte == size {
|
||||
break
|
||||
}
|
||||
bit := node.choose(ip)
|
||||
node = node.child[bit]
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
type AllowedIPs struct {
|
||||
IPv4 *trieEntry
|
||||
IPv6 *trieEntry
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(ip net.IP, cidr uint8) bool) {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = elem.Next() {
|
||||
node := elem.Value.(*trieEntry)
|
||||
if !cb(node.bits, node.cidr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
|
||||
var next *list.Element
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||
next = elem.Next()
|
||||
node := elem.Value.(*trieEntry)
|
||||
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
continue
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Insert(ip net.IP, cidr uint8, peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
|
||||
switch len(ip) {
|
||||
case net.IPv6len:
|
||||
parentIndirection{&table.IPv6, 2}.insert(ip, cidr, peer)
|
||||
case net.IPv4len:
|
||||
parentIndirection{&table.IPv4, 2}.insert(ip, cidr, peer)
|
||||
default:
|
||||
panic(errors.New("inserting unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Lookup(address []byte) *Peer {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
switch len(address) {
|
||||
case net.IPv6len:
|
||||
return table.IPv6.lookup(address)
|
||||
case net.IPv4len:
|
||||
return table.IPv4.lookup(address)
|
||||
default:
|
||||
panic(errors.New("looking up unknown address type"))
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
NumberOfPeers = 100
|
||||
NumberOfPeerRemovals = 4
|
||||
NumberOfAddresses = 250
|
||||
NumberOfTests = 10000
|
||||
)
|
||||
|
||||
type SlowNode struct {
|
||||
peer *Peer
|
||||
cidr uint8
|
||||
bits []byte
|
||||
}
|
||||
|
||||
type SlowRouter []*SlowNode
|
||||
|
||||
func (r SlowRouter) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
|
||||
func (r SlowRouter) Less(i, j int) bool {
|
||||
return r[i].cidr > r[j].cidr
|
||||
}
|
||||
|
||||
func (r SlowRouter) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
|
||||
func (r SlowRouter) Insert(addr []byte, cidr uint8, peer *Peer) SlowRouter {
|
||||
for _, t := range r {
|
||||
if t.cidr == cidr && commonBits(t.bits, addr) >= cidr {
|
||||
t.peer = peer
|
||||
t.bits = addr
|
||||
return r
|
||||
}
|
||||
}
|
||||
r = append(r, &SlowNode{
|
||||
cidr: cidr,
|
||||
bits: addr,
|
||||
peer: peer,
|
||||
})
|
||||
sort.Sort(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r SlowRouter) Lookup(addr []byte) *Peer {
|
||||
for _, t := range r {
|
||||
common := commonBits(t.bits, addr)
|
||||
if common >= t.cidr {
|
||||
return t.peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r SlowRouter) RemoveByPeer(peer *Peer) SlowRouter {
|
||||
n := 0
|
||||
for _, x := range r {
|
||||
if x.peer != peer {
|
||||
r[n] = x
|
||||
n++
|
||||
}
|
||||
}
|
||||
return r[:n]
|
||||
}
|
||||
|
||||
func TestTrieRandom(t *testing.T) {
|
||||
var slow4, slow6 SlowRouter
|
||||
var peers []*Peer
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
rand.Seed(1)
|
||||
|
||||
for n := 0; n < NumberOfPeers; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
}
|
||||
|
||||
for n := 0; n < NumberOfAddresses; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
cidr := uint8(rand.Intn(32) + 1)
|
||||
index := rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(addr4[:], cidr, peers[index])
|
||||
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
cidr = uint8(rand.Intn(128) + 1)
|
||||
index = rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(addr6[:], cidr, peers[index])
|
||||
slow6 = slow6.Insert(addr6[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
var p int
|
||||
for p = 0; ; p++ {
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
peer1 := slow4.Lookup(addr4[:])
|
||||
peer2 := allowedIPs.Lookup(addr4[:])
|
||||
if peer1 != peer2 {
|
||||
t.Errorf("Trie did not match naive implementation, for %v: want %p, got %p", net.IP(addr4[:]), peer1, peer2)
|
||||
}
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
peer1 = slow6.Lookup(addr6[:])
|
||||
peer2 = allowedIPs.Lookup(addr6[:])
|
||||
if peer1 != peer2 {
|
||||
t.Errorf("Trie did not match naive implementation, for %v: want %p, got %p", net.IP(addr6[:]), peer1, peer2)
|
||||
}
|
||||
}
|
||||
if p >= len(peers) || p >= NumberOfPeerRemovals {
|
||||
break
|
||||
}
|
||||
allowedIPs.RemoveByPeer(peers[p])
|
||||
slow4 = slow4.RemoveByPeer(peers[p])
|
||||
slow6 = slow6.RemoveByPeer(peers[p])
|
||||
}
|
||||
for ; p < len(peers); p++ {
|
||||
allowedIPs.RemoveByPeer(peers[p])
|
||||
}
|
||||
|
||||
if allowedIPs.IPv4 != nil || allowedIPs.IPv6 != nil {
|
||||
t.Error("Failed to remove all nodes from trie by peer")
|
||||
}
|
||||
}
|
@ -1,247 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testPairCommonBits struct {
|
||||
s1 []byte
|
||||
s2 []byte
|
||||
match uint8
|
||||
}
|
||||
|
||||
func TestCommonBits(t *testing.T) {
|
||||
|
||||
tests := []testPairCommonBits{
|
||||
{s1: []byte{1, 4, 53, 128}, s2: []byte{0, 0, 0, 0}, match: 7},
|
||||
{s1: []byte{0, 4, 53, 128}, s2: []byte{0, 0, 0, 0}, match: 13},
|
||||
{s1: []byte{0, 4, 53, 253}, s2: []byte{0, 4, 53, 252}, match: 31},
|
||||
{s1: []byte{192, 168, 1, 1}, s2: []byte{192, 169, 1, 1}, match: 15},
|
||||
{s1: []byte{65, 168, 1, 1}, s2: []byte{192, 169, 1, 1}, match: 0},
|
||||
}
|
||||
|
||||
for _, p := range tests {
|
||||
v := commonBits(p.s1, p.s2)
|
||||
if v != p.match {
|
||||
t.Error(
|
||||
"For slice", p.s1, p.s2,
|
||||
"expected match", p.match,
|
||||
",but got", v,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkTrie(peerNumber int, addressNumber int, addressLength int, b *testing.B) {
|
||||
var trie *trieEntry
|
||||
var peers []*Peer
|
||||
root := parentIndirection{&trie, 2}
|
||||
|
||||
rand.Seed(1)
|
||||
|
||||
const AddressLength = 4
|
||||
|
||||
for n := 0; n < peerNumber; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
}
|
||||
|
||||
for n := 0; n < addressNumber; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint8(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % peerNumber
|
||||
root.insert(addr[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
trie.lookup(addr[:])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTrieIPv4Peers100Addresses1000(b *testing.B) {
|
||||
benchmarkTrie(100, 1000, net.IPv4len, b)
|
||||
}
|
||||
|
||||
func BenchmarkTrieIPv4Peers10Addresses10(b *testing.B) {
|
||||
benchmarkTrie(10, 10, net.IPv4len, b)
|
||||
}
|
||||
|
||||
func BenchmarkTrieIPv6Peers100Addresses1000(b *testing.B) {
|
||||
benchmarkTrie(100, 1000, net.IPv6len, b)
|
||||
}
|
||||
|
||||
func BenchmarkTrieIPv6Peers10Addresses10(b *testing.B) {
|
||||
benchmarkTrie(10, 10, net.IPv6len, b)
|
||||
}
|
||||
|
||||
/* Test ported from kernel implementation:
|
||||
* selftest/allowedips.h
|
||||
*/
|
||||
func TestTrieIPv4(t *testing.T) {
|
||||
a := &Peer{}
|
||||
b := &Peer{}
|
||||
c := &Peer{}
|
||||
d := &Peer{}
|
||||
e := &Peer{}
|
||||
g := &Peer{}
|
||||
h := &Peer{}
|
||||
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
insert := func(peer *Peer, a, b, c, d byte, cidr uint8) {
|
||||
allowedIPs.Insert([]byte{a, b, c, d}, cidr, peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p != peer {
|
||||
t.Error("Assert EQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
assertNEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p == peer {
|
||||
t.Error("Assert NEQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
insert(a, 192, 168, 4, 0, 24)
|
||||
insert(b, 192, 168, 4, 4, 32)
|
||||
insert(c, 192, 168, 0, 0, 16)
|
||||
insert(d, 192, 95, 5, 64, 27)
|
||||
insert(c, 192, 95, 5, 65, 27)
|
||||
insert(e, 0, 0, 0, 0, 0)
|
||||
insert(g, 64, 15, 112, 0, 20)
|
||||
insert(h, 64, 15, 123, 211, 25)
|
||||
insert(a, 10, 0, 0, 0, 25)
|
||||
insert(b, 10, 0, 0, 128, 25)
|
||||
insert(a, 10, 1, 0, 0, 30)
|
||||
insert(b, 10, 1, 0, 4, 30)
|
||||
insert(c, 10, 1, 0, 8, 29)
|
||||
insert(d, 10, 1, 0, 16, 29)
|
||||
|
||||
assertEQ(a, 192, 168, 4, 20)
|
||||
assertEQ(a, 192, 168, 4, 0)
|
||||
assertEQ(b, 192, 168, 4, 4)
|
||||
assertEQ(c, 192, 168, 200, 182)
|
||||
assertEQ(c, 192, 95, 5, 68)
|
||||
assertEQ(e, 192, 95, 5, 96)
|
||||
assertEQ(g, 64, 15, 116, 26)
|
||||
assertEQ(g, 64, 15, 127, 3)
|
||||
|
||||
insert(a, 1, 0, 0, 0, 32)
|
||||
insert(a, 64, 0, 0, 0, 32)
|
||||
insert(a, 128, 0, 0, 0, 32)
|
||||
insert(a, 192, 0, 0, 0, 32)
|
||||
insert(a, 255, 0, 0, 0, 32)
|
||||
|
||||
assertEQ(a, 1, 0, 0, 0)
|
||||
assertEQ(a, 64, 0, 0, 0)
|
||||
assertEQ(a, 128, 0, 0, 0)
|
||||
assertEQ(a, 192, 0, 0, 0)
|
||||
assertEQ(a, 255, 0, 0, 0)
|
||||
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 1, 0, 0, 0)
|
||||
assertNEQ(a, 64, 0, 0, 0)
|
||||
assertNEQ(a, 128, 0, 0, 0)
|
||||
assertNEQ(a, 192, 0, 0, 0)
|
||||
assertNEQ(a, 255, 0, 0, 0)
|
||||
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
allowedIPs.RemoveByPeer(b)
|
||||
allowedIPs.RemoveByPeer(c)
|
||||
allowedIPs.RemoveByPeer(d)
|
||||
allowedIPs.RemoveByPeer(e)
|
||||
allowedIPs.RemoveByPeer(g)
|
||||
allowedIPs.RemoveByPeer(h)
|
||||
if allowedIPs.IPv4 != nil || allowedIPs.IPv6 != nil {
|
||||
t.Error("Expected removing all the peers to empty trie, but it did not")
|
||||
}
|
||||
|
||||
insert(a, 192, 168, 0, 0, 16)
|
||||
insert(a, 192, 168, 0, 0, 24)
|
||||
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 192, 168, 0, 1)
|
||||
}
|
||||
|
||||
/* Test ported from kernel implementation:
|
||||
* selftest/allowedips.h
|
||||
*/
|
||||
func TestTrieIPv6(t *testing.T) {
|
||||
a := &Peer{}
|
||||
b := &Peer{}
|
||||
c := &Peer{}
|
||||
d := &Peer{}
|
||||
e := &Peer{}
|
||||
f := &Peer{}
|
||||
g := &Peer{}
|
||||
h := &Peer{}
|
||||
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
expand := func(a uint32) []byte {
|
||||
var out [4]byte
|
||||
out[0] = byte(a >> 24 & 0xff)
|
||||
out[1] = byte(a >> 16 & 0xff)
|
||||
out[2] = byte(a >> 8 & 0xff)
|
||||
out[3] = byte(a & 0xff)
|
||||
return out[:]
|
||||
}
|
||||
|
||||
insert := func(peer *Peer, a, b, c, d uint32, cidr uint8) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
allowedIPs.Insert(addr, cidr, peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
p := allowedIPs.Lookup(addr)
|
||||
if p != peer {
|
||||
t.Error("Assert EQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128)
|
||||
insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
|
||||
insert(e, 0, 0, 0, 0, 0)
|
||||
insert(f, 0, 0, 0, 0, 0)
|
||||
insert(g, 0x24046800, 0, 0, 0, 32)
|
||||
insert(h, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef, 64)
|
||||
insert(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef, 128)
|
||||
insert(c, 0x24446800, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
insert(b, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
|
||||
assertEQ(d, 0x26075300, 0x60006b00, 0, 0xc05f0543)
|
||||
assertEQ(c, 0x26075300, 0x60006b00, 0, 0xc02e01ee)
|
||||
assertEQ(f, 0x26075300, 0x60006b01, 0, 0)
|
||||
assertEQ(g, 0x24046800, 0x40040806, 0, 0x1006)
|
||||
assertEQ(g, 0x24046800, 0x40040806, 0x1234, 0x5678)
|
||||
assertEQ(f, 0x240467ff, 0x40040806, 0x1234, 0x5678)
|
||||
assertEQ(f, 0x24046801, 0x40040806, 0x1234, 0x5678)
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0x1234, 0x5678)
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0, 0)
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
|
||||
assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef)
|
||||
}
|
@ -1,407 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn/bindtest"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/tuntest"
|
||||
)
|
||||
|
||||
// uapiCfg returns a string that contains cfg formatted use with IpcSet.
|
||||
// cfg is a series of alternating key/value strings.
|
||||
// uapiCfg exists because editors and humans like to insert
|
||||
// whitespace into configs, which can cause failures, some of which are silent.
|
||||
// For example, a leading blank newline causes the remainder
|
||||
// of the config to be silently ignored.
|
||||
func uapiCfg(cfg ...string) string {
|
||||
if len(cfg)%2 != 0 {
|
||||
panic("odd number of args to uapiReader")
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
for i, s := range cfg {
|
||||
buf.WriteString(s)
|
||||
sep := byte('\n')
|
||||
if i%2 == 0 {
|
||||
sep = '='
|
||||
}
|
||||
buf.WriteByte(sep)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// genConfigs generates a pair of configs that connect to each other.
|
||||
// The configs use distinct, probably-usable ports.
|
||||
func genConfigs(tb testing.TB) (cfgs [2]string, endpointCfgs [2]string) {
|
||||
var key1, key2 NoisePrivateKey
|
||||
_, err := rand.Read(key1[:])
|
||||
if err != nil {
|
||||
tb.Errorf("unable to generate private key random bytes: %v", err)
|
||||
}
|
||||
_, err = rand.Read(key2[:])
|
||||
if err != nil {
|
||||
tb.Errorf("unable to generate private key random bytes: %v", err)
|
||||
}
|
||||
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
||||
|
||||
cfgs[0] = uapiCfg(
|
||||
"private_key", hex.EncodeToString(key1[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
"public_key", hex.EncodeToString(pub2[:]),
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.2/32",
|
||||
)
|
||||
endpointCfgs[0] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub2[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
)
|
||||
cfgs[1] = uapiCfg(
|
||||
"private_key", hex.EncodeToString(key2[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
"public_key", hex.EncodeToString(pub1[:]),
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.1/32",
|
||||
)
|
||||
endpointCfgs[1] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub1[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// A testPair is a pair of testPeers.
|
||||
type testPair [2]testPeer
|
||||
|
||||
// A testPeer is a peer used for testing.
|
||||
type testPeer struct {
|
||||
tun *tuntest.ChannelTUN
|
||||
dev *Device
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
type SendDirection bool
|
||||
|
||||
const (
|
||||
Ping SendDirection = true
|
||||
Pong SendDirection = false
|
||||
)
|
||||
|
||||
func (d SendDirection) String() string {
|
||||
if d == Ping {
|
||||
return "ping"
|
||||
}
|
||||
return "pong"
|
||||
}
|
||||
|
||||
func (pair *testPair) Send(tb testing.TB, ping SendDirection, done chan struct{}) {
|
||||
tb.Helper()
|
||||
p0, p1 := pair[0], pair[1]
|
||||
if !ping {
|
||||
// pong is the new ping
|
||||
p0, p1 = p1, p0
|
||||
}
|
||||
msg := tuntest.Ping(p0.ip, p1.ip)
|
||||
p1.tun.Outbound <- msg
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
defer timer.Stop()
|
||||
var err error
|
||||
select {
|
||||
case msgRecv := <-p0.tun.Inbound:
|
||||
if !bytes.Equal(msg, msgRecv) {
|
||||
err = fmt.Errorf("%s did not transit correctly", ping)
|
||||
}
|
||||
case <-timer.C:
|
||||
err = fmt.Errorf("%s did not transit", ping)
|
||||
case <-done:
|
||||
}
|
||||
if err != nil {
|
||||
// The error may have occurred because the test is done.
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
// Real error.
|
||||
tb.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// genTestPair creates a testPair.
|
||||
func genTestPair(tb testing.TB, realSocket bool) (pair testPair) {
|
||||
cfg, endpointCfg := genConfigs(tb)
|
||||
var binds [2]conn.Bind
|
||||
if realSocket {
|
||||
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
|
||||
} else {
|
||||
binds = bindtest.NewChannelBinds()
|
||||
}
|
||||
// Bring up a ChannelTun for each config.
|
||||
for i := range pair {
|
||||
p := &pair[i]
|
||||
p.tun = tuntest.NewChannelTUN()
|
||||
p.ip = net.IPv4(1, 0, 0, byte(i+1))
|
||||
level := LogLevelVerbose
|
||||
if _, ok := tb.(*testing.B); ok && !testing.Verbose() {
|
||||
level = LogLevelError
|
||||
}
|
||||
p.dev = NewDevice(p.tun.TUN(), binds[i], NewLogger(level, fmt.Sprintf("dev%d: ", i)))
|
||||
if err := p.dev.IpcSet(cfg[i]); err != nil {
|
||||
tb.Errorf("failed to configure device %d: %v", i, err)
|
||||
p.dev.Close()
|
||||
continue
|
||||
}
|
||||
if err := p.dev.Up(); err != nil {
|
||||
tb.Errorf("failed to bring up device %d: %v", i, err)
|
||||
p.dev.Close()
|
||||
continue
|
||||
}
|
||||
endpointCfg[i^1] = fmt.Sprintf(endpointCfg[i^1], p.dev.net.port)
|
||||
}
|
||||
for i := range pair {
|
||||
p := &pair[i]
|
||||
if err := p.dev.IpcSet(endpointCfg[i]); err != nil {
|
||||
tb.Errorf("failed to configure device endpoint %d: %v", i, err)
|
||||
p.dev.Close()
|
||||
continue
|
||||
}
|
||||
// The device is ready. Close it when the test completes.
|
||||
tb.Cleanup(p.dev.Close)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestTwoDevicePing(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
pair := genTestPair(t, true)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
pair.Send(t, Ping, nil)
|
||||
})
|
||||
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
||||
pair.Send(t, Pong, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpDown(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
const itrials = 50
|
||||
const otrials = 10
|
||||
|
||||
for n := 0; n < otrials; n++ {
|
||||
pair := genTestPair(t, false)
|
||||
for i := range pair {
|
||||
for k := range pair[i].dev.peers.keyMap {
|
||||
pair[i].dev.IpcSet(fmt.Sprintf("public_key=%s\npersistent_keepalive_interval=1\n", hex.EncodeToString(k[:])))
|
||||
}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(pair))
|
||||
for i := range pair {
|
||||
go func(d *Device) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < itrials; i++ {
|
||||
if err := d.Up(); err != nil {
|
||||
t.Errorf("failed up bring up device: %v", err)
|
||||
}
|
||||
time.Sleep(time.Duration(rand.Intn(int(time.Nanosecond * (0x10000 - 1)))))
|
||||
if err := d.Down(); err != nil {
|
||||
t.Errorf("failed to bring down device: %v", err)
|
||||
}
|
||||
time.Sleep(time.Duration(rand.Intn(int(time.Nanosecond * (0x10000 - 1)))))
|
||||
}
|
||||
}(pair[i].dev)
|
||||
}
|
||||
wg.Wait()
|
||||
for i := range pair {
|
||||
pair[i].dev.Up()
|
||||
pair[i].dev.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestConcurrencySafety does other things concurrently with tunnel use.
|
||||
// It is intended to be used with the race detector to catch data races.
|
||||
func TestConcurrencySafety(t *testing.T) {
|
||||
pair := genTestPair(t, true)
|
||||
done := make(chan struct{})
|
||||
|
||||
const warmupIters = 10
|
||||
var warmup sync.WaitGroup
|
||||
warmup.Add(warmupIters)
|
||||
go func() {
|
||||
// Send data continuously back and forth until we're done.
|
||||
// Note that we may continue to attempt to send data
|
||||
// even after done is closed.
|
||||
i := warmupIters
|
||||
for ping := Ping; ; ping = !ping {
|
||||
pair.Send(t, ping, done)
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
if i > 0 {
|
||||
warmup.Done()
|
||||
i--
|
||||
}
|
||||
}
|
||||
}()
|
||||
warmup.Wait()
|
||||
|
||||
applyCfg := func(cfg string) {
|
||||
err := pair[0].dev.IpcSet(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Change persistent_keepalive_interval concurrently with tunnel use.
|
||||
t.Run("persistentKeepaliveInterval", func(t *testing.T) {
|
||||
var pub NoisePublicKey
|
||||
for key := range pair[0].dev.peers.keyMap {
|
||||
pub = key
|
||||
break
|
||||
}
|
||||
cfg := uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub[:]),
|
||||
"persistent_keepalive_interval", "1",
|
||||
)
|
||||
for i := 0; i < 1000; i++ {
|
||||
applyCfg(cfg)
|
||||
}
|
||||
})
|
||||
|
||||
// Change private keys concurrently with tunnel use.
|
||||
t.Run("privateKey", func(t *testing.T) {
|
||||
bad := uapiCfg("private_key", "7777777777777777777777777777777777777777777777777777777777777777")
|
||||
good := uapiCfg("private_key", hex.EncodeToString(pair[0].dev.staticIdentity.privateKey[:]))
|
||||
// Set iters to a large number like 1000 to flush out data races quickly.
|
||||
// Don't leave it large. That can cause logical races
|
||||
// in which the handshake is interleaved with key changes
|
||||
// such that the private key appears to be unchanging but
|
||||
// other state gets reset, which can cause handshake failures like
|
||||
// "Received packet with invalid mac1".
|
||||
const iters = 1
|
||||
for i := 0; i < iters; i++ {
|
||||
applyCfg(bad)
|
||||
applyCfg(good)
|
||||
}
|
||||
})
|
||||
|
||||
close(done)
|
||||
}
|
||||
|
||||
func BenchmarkLatency(b *testing.B) {
|
||||
pair := genTestPair(b, true)
|
||||
|
||||
// Establish a connection.
|
||||
pair.Send(b, Ping, nil)
|
||||
pair.Send(b, Pong, nil)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pair.Send(b, Ping, nil)
|
||||
pair.Send(b, Pong, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkThroughput(b *testing.B) {
|
||||
pair := genTestPair(b, true)
|
||||
|
||||
// Establish a connection.
|
||||
pair.Send(b, Ping, nil)
|
||||
pair.Send(b, Pong, nil)
|
||||
|
||||
// Measure how long it takes to receive b.N packets,
|
||||
// starting when we receive the first packet.
|
||||
var recv uint64
|
||||
var elapsed time.Duration
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var start time.Time
|
||||
for {
|
||||
<-pair[0].tun.Inbound
|
||||
new := atomic.AddUint64(&recv, 1)
|
||||
if new == 1 {
|
||||
start = time.Now()
|
||||
}
|
||||
// Careful! Don't change this to else if; b.N can be equal to 1.
|
||||
if new == uint64(b.N) {
|
||||
elapsed = time.Since(start)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Send packets as fast as we can until we've received enough.
|
||||
ping := tuntest.Ping(pair[0].ip, pair[1].ip)
|
||||
pingc := pair[1].tun.Outbound
|
||||
var sent uint64
|
||||
for atomic.LoadUint64(&recv) != uint64(b.N) {
|
||||
sent++
|
||||
pingc <- ping
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
b.ReportMetric(float64(elapsed)/float64(b.N), "ns/op")
|
||||
b.ReportMetric(1-float64(b.N)/float64(sent), "packet-loss")
|
||||
}
|
||||
|
||||
func BenchmarkUAPIGet(b *testing.B) {
|
||||
pair := genTestPair(b, true)
|
||||
pair.Send(b, Ping, nil)
|
||||
pair.Send(b, Pong, nil)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pair[0].dev.IpcGetOperation(io.Discard)
|
||||
}
|
||||
}
|
||||
|
||||
func goroutineLeakCheck(t *testing.T) {
|
||||
goroutines := func() (int, []byte) {
|
||||
p := pprof.Lookup("goroutine")
|
||||
b := new(bytes.Buffer)
|
||||
p.WriteTo(b, 1)
|
||||
return p.Count(), b.Bytes()
|
||||
}
|
||||
|
||||
startGoroutines, startStacks := goroutines()
|
||||
t.Cleanup(func() {
|
||||
if t.Failed() {
|
||||
return
|
||||
}
|
||||
// Give goroutines time to exit, if they need it.
|
||||
for i := 0; i < 10000; i++ {
|
||||
if runtime.NumGoroutine() <= startGoroutines {
|
||||
return
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
endGoroutines, endStacks := goroutines()
|
||||
t.Logf("starting stacks:\n%s\n", startStacks)
|
||||
t.Logf("ending stacks:\n%s\n", endStacks)
|
||||
t.Fatalf("expected %d goroutines, got %d, leak?", startGoroutines, endGoroutines)
|
||||
})
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/tuntest"
|
||||
)
|
||||
|
||||
func TestCurveWrappers(t *testing.T) {
|
||||
sk1, err := newPrivateKey()
|
||||
assertNil(t, err)
|
||||
|
||||
sk2, err := newPrivateKey()
|
||||
assertNil(t, err)
|
||||
|
||||
pk1 := sk1.publicKey()
|
||||
pk2 := sk2.publicKey()
|
||||
|
||||
ss1 := sk1.sharedSecret(pk2)
|
||||
ss2 := sk2.sharedSecret(pk1)
|
||||
|
||||
if ss1 != ss2 {
|
||||
t.Fatal("Failed to compute shared secet")
|
||||
}
|
||||
}
|
||||
|
||||
func randDevice(t *testing.T) *Device {
|
||||
sk, err := newPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tun := tuntest.NewChannelTUN()
|
||||
logger := NewLogger(LogLevelError, "")
|
||||
device := NewDevice(tun.TUN(), conn.NewDefaultBind(), logger)
|
||||
device.SetPrivateKey(sk)
|
||||
return device
|
||||
}
|
||||
|
||||
func assertNil(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, a, b []byte) {
|
||||
if !bytes.Equal(a, b) {
|
||||
t.Fatal(a, "!=", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoiseHandshake(t *testing.T) {
|
||||
dev1 := randDevice(t)
|
||||
dev2 := randDevice(t)
|
||||
|
||||
defer dev1.Close()
|
||||
defer dev2.Close()
|
||||
|
||||
peer1, err := dev2.NewPeer(dev1.staticIdentity.privateKey.publicKey())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer2, err := dev1.NewPeer(dev2.staticIdentity.privateKey.publicKey())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertEqual(
|
||||
t,
|
||||
peer1.handshake.precomputedStaticStatic[:],
|
||||
peer2.handshake.precomputedStaticStatic[:],
|
||||
)
|
||||
|
||||
/* simulate handshake */
|
||||
|
||||
// initiation message
|
||||
|
||||
t.Log("exchange initiation message")
|
||||
|
||||
msg1, err := dev1.CreateMessageInitiation(peer2)
|
||||
assertNil(t, err)
|
||||
|
||||
packet := make([]byte, 0, 256)
|
||||
writer := bytes.NewBuffer(packet)
|
||||
err = binary.Write(writer, binary.LittleEndian, msg1)
|
||||
assertNil(t, err)
|
||||
peer := dev2.ConsumeMessageInitiation(msg1)
|
||||
if peer == nil {
|
||||
t.Fatal("handshake failed at initiation message")
|
||||
}
|
||||
|
||||
assertEqual(
|
||||
t,
|
||||
peer1.handshake.chainKey[:],
|
||||
peer2.handshake.chainKey[:],
|
||||
)
|
||||
|
||||
assertEqual(
|
||||
t,
|
||||
peer1.handshake.hash[:],
|
||||
peer2.handshake.hash[:],
|
||||
)
|
||||
|
||||
// response message
|
||||
|
||||
t.Log("exchange response message")
|
||||
|
||||
msg2, err := dev2.CreateMessageResponse(peer1)
|
||||
assertNil(t, err)
|
||||
|
||||
peer = dev1.ConsumeMessageResponse(msg2)
|
||||
if peer == nil {
|
||||
t.Fatal("handshake failed at response message")
|
||||
}
|
||||
|
||||
assertEqual(
|
||||
t,
|
||||
peer1.handshake.chainKey[:],
|
||||
peer2.handshake.chainKey[:],
|
||||
)
|
||||
|
||||
assertEqual(
|
||||
t,
|
||||
peer1.handshake.hash[:],
|
||||
peer2.handshake.hash[:],
|
||||
)
|
||||
|
||||
// key pairs
|
||||
|
||||
t.Log("deriving keys")
|
||||
|
||||
err = peer1.BeginSymmetricSession()
|
||||
if err != nil {
|
||||
t.Fatal("failed to derive keypair for peer 1", err)
|
||||
}
|
||||
|
||||
err = peer2.BeginSymmetricSession()
|
||||
if err != nil {
|
||||
t.Fatal("failed to derive keypair for peer 2", err)
|
||||
}
|
||||
|
||||
key1 := peer1.keypairs.loadNext()
|
||||
key2 := peer2.keypairs.current
|
||||
|
||||
// encrypting / decryption test
|
||||
|
||||
t.Log("test key pairs")
|
||||
|
||||
func() {
|
||||
testMsg := []byte("wireguard test message 1")
|
||||
var err error
|
||||
var out []byte
|
||||
var nonce [12]byte
|
||||
out = key1.send.Seal(out, nonce[:], testMsg, nil)
|
||||
out, err = key2.receive.Open(out[:0], nonce[:], out, nil)
|
||||
assertNil(t, err)
|
||||
assertEqual(t, out, testMsg)
|
||||
}()
|
||||
|
||||
func() {
|
||||
testMsg := []byte("wireguard test message 2")
|
||||
var err error
|
||||
var out []byte
|
||||
var nonce [12]byte
|
||||
out = key2.send.Seal(out, nonce[:], testMsg, nil)
|
||||
out, err = key1.receive.Open(out[:0], nonce[:], out, nil)
|
||||
assertNil(t, err)
|
||||
assertEqual(t, out, testMsg)
|
||||
}()
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun"
|
||||
)
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
func (device *Device) RoutineTUNEventReader() {
|
||||
device.log.Verbosef("Routine: event worker - started")
|
||||
|
||||
for event := range device.tun.device.Events() {
|
||||
if event&tun.EventMTUUpdate != 0 {
|
||||
mtu, err := device.tun.device.MTU()
|
||||
if err != nil {
|
||||
device.log.Errorf("Failed to load updated MTU of device: %v", err)
|
||||
continue
|
||||
}
|
||||
if mtu < 0 {
|
||||
device.log.Errorf("MTU not updated to negative value: %v", mtu)
|
||||
continue
|
||||
}
|
||||
var tooLarge string
|
||||
if mtu > MaxContentSize {
|
||||
tooLarge = fmt.Sprintf(" (too large, capped at %v)", MaxContentSize)
|
||||
mtu = MaxContentSize
|
||||
}
|
||||
old := atomic.SwapInt32(&device.tun.mtu, int32(mtu))
|
||||
if int(old) != mtu {
|
||||
device.log.Verbosef("MTU updated: %v%s", mtu, tooLarge)
|
||||
}
|
||||
}
|
||||
|
||||
if event&tun.EventUp != 0 {
|
||||
device.log.Verbosef("Interface up requested")
|
||||
device.Up()
|
||||
}
|
||||
|
||||
if event&tun.EventDown != 0 {
|
||||
device.log.Verbosef("Interface down requested")
|
||||
device.Down()
|
||||
}
|
||||
}
|
||||
|
||||
device.log.Verbosef("Routine: event worker - stopped")
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatting(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("unable to walk %s: %v", path, err)
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() || filepath.Ext(path) != ".go" {
|
||||
return nil
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(path string) {
|
||||
defer wg.Done()
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("unable to read %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
src = bytes.ReplaceAll(src, []byte{'\r', '\n'}, []byte{'\n'})
|
||||
}
|
||||
formatted, err := format.Source(src)
|
||||
if err != nil {
|
||||
t.Errorf("unable to format %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(src, formatted) {
|
||||
t.Errorf("unformatted code: %s", path)
|
||||
}
|
||||
}(path)
|
||||
return nil
|
||||
})
|
||||
wg.Wait()
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/device"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/ipc"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
ExitSetupSuccess = 0
|
||||
ExitSetupFailed = 1
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
interfaceName := os.Args[1]
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real WireGuard for Windows client, the repo you want is <https://git.zx2c4.com/wireguard-windows/>, which includes this code as a module.")
|
||||
|
||||
logger := device.NewLogger(
|
||||
device.LogLevelVerbose,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
||||
|
||||
tun, err := tun.CreateTUN(interfaceName, 0)
|
||||
if err == nil {
|
||||
realInterfaceName, err2 := tun.Name()
|
||||
if err2 == nil {
|
||||
interfaceName = realInterfaceName
|
||||
}
|
||||
} else {
|
||||
logger.Errorf("Failed to create TUN device: %v", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
device := device.NewDevice(tun, conn.NewDefaultBind(), logger)
|
||||
err = device.Up()
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to bring up device: %v", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
logger.Verbosef("Device started")
|
||||
|
||||
uapi, err := ipc.UAPIListen(interfaceName)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to listen on uapi socket: %v", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
errs := make(chan error)
|
||||
term := make(chan os.Signal, 1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
logger.Verbosef("UAPI listener started")
|
||||
|
||||
// wait for program to terminate
|
||||
|
||||
signal.Notify(term, os.Interrupt)
|
||||
signal.Notify(term, os.Kill)
|
||||
signal.Notify(term, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-term:
|
||||
case <-errs:
|
||||
case <-device.Wait():
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
uapi.Close()
|
||||
device.Close()
|
||||
|
||||
logger.Verbosef("Shutting down")
|
||||
}
|
425
tests/netns.sh
425
tests/netns.sh
@ -1,425 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
|
||||
# This script tests the below topology:
|
||||
#
|
||||
# ┌─────────────────────┐ ┌──────────────────────────────────┐ ┌─────────────────────┐
|
||||
# │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │
|
||||
# │ │ │ │ │ │
|
||||
# │┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐│
|
||||
# ││ wg1 │───────────┼───┼────────────│ lo │────────────┼───┼───────────│ wg2 ││
|
||||
# │├────────┴──────────┐│ │ ┌───────┴────────┴────────┐ │ │┌──────────┴────────┤│
|
||||
# ││192.168.241.1/24 ││ │ │(ns1) (ns2) │ │ ││192.168.241.2/24 ││
|
||||
# ││fd00::1/24 ││ │ │127.0.0.1:1 127.0.0.1:2│ │ ││fd00::2/24 ││
|
||||
# │└───────────────────┘│ │ │[::]:1 [::]:2 │ │ │└───────────────────┘│
|
||||
# └─────────────────────┘ │ └─────────────────────────┘ │ └─────────────────────┘
|
||||
# └──────────────────────────────────┘
|
||||
#
|
||||
# After the topology is prepared we run a series of TCP/UDP iperf3 tests between the
|
||||
# wireguard peers in $ns1 and $ns2. Note that $ns0 is the endpoint for the wg1
|
||||
# interfaces in $ns1 and $ns2. See https://www.wireguard.com/netns/ for further
|
||||
# details on how this is accomplished.
|
||||
|
||||
# This code is ported to the WireGuard-Go directly from the kernel project.
|
||||
#
|
||||
# Please ensure that you have installed the newest version of the WireGuard
|
||||
# tools from the WireGuard project and before running these tests as:
|
||||
#
|
||||
# ./netns.sh <path to wireguard-go>
|
||||
|
||||
set -e
|
||||
|
||||
exec 3>&1
|
||||
export WG_HIDE_KEYS=never
|
||||
netns0="wg-test-$$-0"
|
||||
netns1="wg-test-$$-1"
|
||||
netns2="wg-test-$$-2"
|
||||
program=$1
|
||||
export LOG_LEVEL="verbose"
|
||||
|
||||
pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; }
|
||||
pp() { pretty "" "$*"; "$@"; }
|
||||
maybe_exec() { if [[ $BASHPID -eq $$ ]]; then "$@"; else exec "$@"; fi; }
|
||||
n0() { pretty 0 "$*"; maybe_exec ip netns exec $netns0 "$@"; }
|
||||
n1() { pretty 1 "$*"; maybe_exec ip netns exec $netns1 "$@"; }
|
||||
n2() { pretty 2 "$*"; maybe_exec ip netns exec $netns2 "$@"; }
|
||||
ip0() { pretty 0 "ip $*"; ip -n $netns0 "$@"; }
|
||||
ip1() { pretty 1 "ip $*"; ip -n $netns1 "$@"; }
|
||||
ip2() { pretty 2 "ip $*"; ip -n $netns2 "$@"; }
|
||||
sleep() { read -t "$1" -N 0 || true; }
|
||||
waitiperf() { pretty "${1//*-}" "wait for iperf:5201"; while [[ $(ss -N "$1" -tlp 'sport = 5201') != *iperf3* ]]; do sleep 0.1; done; }
|
||||
waitncatudp() { pretty "${1//*-}" "wait for udp:1111"; while [[ $(ss -N "$1" -ulp 'sport = 1111') != *ncat* ]]; do sleep 0.1; done; }
|
||||
waitiface() { pretty "${1//*-}" "wait for $2 to come up"; ip netns exec "$1" bash -c "while [[ \$(< \"/sys/class/net/$2/operstate\") != up ]]; do read -t .1 -N 0 || true; done;"; }
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
exec 2>/dev/null
|
||||
printf "$orig_message_cost" > /proc/sys/net/core/message_cost
|
||||
ip0 link del dev wg1
|
||||
ip1 link del dev wg1
|
||||
ip2 link del dev wg1
|
||||
local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)"
|
||||
[[ -n $to_kill ]] && kill $to_kill
|
||||
pp ip netns del $netns1
|
||||
pp ip netns del $netns2
|
||||
pp ip netns del $netns0
|
||||
exit
|
||||
}
|
||||
|
||||
orig_message_cost="$(< /proc/sys/net/core/message_cost)"
|
||||
trap cleanup EXIT
|
||||
printf 0 > /proc/sys/net/core/message_cost
|
||||
|
||||
ip netns del $netns0 2>/dev/null || true
|
||||
ip netns del $netns1 2>/dev/null || true
|
||||
ip netns del $netns2 2>/dev/null || true
|
||||
pp ip netns add $netns0
|
||||
pp ip netns add $netns1
|
||||
pp ip netns add $netns2
|
||||
ip0 link set up dev lo
|
||||
|
||||
# ip0 link add dev wg1 type wireguard
|
||||
n0 $program wg1
|
||||
ip0 link set wg1 netns $netns1
|
||||
|
||||
# ip0 link add dev wg1 type wireguard
|
||||
n0 $program wg2
|
||||
ip0 link set wg2 netns $netns2
|
||||
|
||||
key1="$(pp wg genkey)"
|
||||
key2="$(pp wg genkey)"
|
||||
pub1="$(pp wg pubkey <<<"$key1")"
|
||||
pub2="$(pp wg pubkey <<<"$key2")"
|
||||
psk="$(pp wg genpsk)"
|
||||
[[ -n $key1 && -n $key2 && -n $psk ]]
|
||||
|
||||
configure_peers() {
|
||||
|
||||
ip1 addr add 192.168.241.1/24 dev wg1
|
||||
ip1 addr add fd00::1/24 dev wg1
|
||||
|
||||
ip2 addr add 192.168.241.2/24 dev wg2
|
||||
ip2 addr add fd00::2/24 dev wg2
|
||||
|
||||
n0 wg set wg1 \
|
||||
private-key <(echo "$key1") \
|
||||
listen-port 10000 \
|
||||
peer "$pub2" \
|
||||
preshared-key <(echo "$psk") \
|
||||
allowed-ips 192.168.241.2/32,fd00::2/128
|
||||
n0 wg set wg2 \
|
||||
private-key <(echo "$key2") \
|
||||
listen-port 20000 \
|
||||
peer "$pub1" \
|
||||
preshared-key <(echo "$psk") \
|
||||
allowed-ips 192.168.241.1/32,fd00::1/128
|
||||
|
||||
n0 wg showconf wg1
|
||||
n0 wg showconf wg2
|
||||
|
||||
ip1 link set up dev wg1
|
||||
ip2 link set up dev wg2
|
||||
sleep 1
|
||||
}
|
||||
configure_peers
|
||||
|
||||
tests() {
|
||||
# Ping over IPv4
|
||||
n2 ping -c 10 -f -W 1 192.168.241.1
|
||||
n1 ping -c 10 -f -W 1 192.168.241.2
|
||||
|
||||
# Ping over IPv6
|
||||
n2 ping6 -c 10 -f -W 1 fd00::1
|
||||
n1 ping6 -c 10 -f -W 1 fd00::2
|
||||
|
||||
# TCP over IPv4
|
||||
n2 iperf3 -s -1 -B 192.168.241.2 &
|
||||
waitiperf $netns2
|
||||
n1 iperf3 -Z -n 1G -c 192.168.241.2
|
||||
|
||||
# TCP over IPv6
|
||||
n1 iperf3 -s -1 -B fd00::1 &
|
||||
waitiperf $netns1
|
||||
n2 iperf3 -Z -n 1G -c fd00::1
|
||||
|
||||
# UDP over IPv4
|
||||
n1 iperf3 -s -1 -B 192.168.241.1 &
|
||||
waitiperf $netns1
|
||||
n2 iperf3 -Z -n 1G -b 0 -u -c 192.168.241.1
|
||||
|
||||
# UDP over IPv6
|
||||
n2 iperf3 -s -1 -B fd00::2 &
|
||||
waitiperf $netns2
|
||||
n1 iperf3 -Z -n 1G -b 0 -u -c fd00::2
|
||||
}
|
||||
|
||||
[[ $(ip1 link show dev wg1) =~ mtu\ ([0-9]+) ]] && orig_mtu="${BASH_REMATCH[1]}"
|
||||
big_mtu=$(( 34816 - 1500 + $orig_mtu ))
|
||||
|
||||
# Test using IPv4 as outer transport
|
||||
n0 wg set wg1 peer "$pub2" endpoint 127.0.0.1:20000
|
||||
n0 wg set wg2 peer "$pub1" endpoint 127.0.0.1:10000
|
||||
|
||||
# Before calling tests, we first make sure that the stats counters are working
|
||||
n2 ping -c 10 -f -W 1 192.168.241.1
|
||||
{ read _; read _; read _; read rx_bytes _; read _; read tx_bytes _; } < <(ip2 -stats link show dev wg2)
|
||||
ip2 -stats link show dev wg2
|
||||
n0 wg show
|
||||
[[ $rx_bytes -ge 840 && $tx_bytes -ge 880 && $rx_bytes -lt 2500 && $rx_bytes -lt 2500 ]]
|
||||
echo "counters working"
|
||||
tests
|
||||
ip1 link set wg1 mtu $big_mtu
|
||||
ip2 link set wg2 mtu $big_mtu
|
||||
tests
|
||||
|
||||
ip1 link set wg1 mtu $orig_mtu
|
||||
ip2 link set wg2 mtu $orig_mtu
|
||||
|
||||
# Test using IPv6 as outer transport
|
||||
n0 wg set wg1 peer "$pub2" endpoint [::1]:20000
|
||||
n0 wg set wg2 peer "$pub1" endpoint [::1]:10000
|
||||
tests
|
||||
ip1 link set wg1 mtu $big_mtu
|
||||
ip2 link set wg2 mtu $big_mtu
|
||||
tests
|
||||
|
||||
ip1 link set wg1 mtu $orig_mtu
|
||||
ip2 link set wg2 mtu $orig_mtu
|
||||
|
||||
# Test using IPv4 that roaming works
|
||||
ip0 -4 addr del 127.0.0.1/8 dev lo
|
||||
ip0 -4 addr add 127.212.121.99/8 dev lo
|
||||
n0 wg set wg1 listen-port 9999
|
||||
n0 wg set wg1 peer "$pub2" endpoint 127.0.0.1:20000
|
||||
n1 ping6 -W 1 -c 1 fd00::2
|
||||
[[ $(n2 wg show wg2 endpoints) == "$pub1 127.212.121.99:9999" ]]
|
||||
|
||||
# Test using IPv6 that roaming works
|
||||
n1 wg set wg1 listen-port 9998
|
||||
n1 wg set wg1 peer "$pub2" endpoint [::1]:20000
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
[[ $(n2 wg show wg2 endpoints) == "$pub1 [::1]:9998" ]]
|
||||
|
||||
# Test that crypto-RP filter works
|
||||
n1 wg set wg1 peer "$pub2" allowed-ips 192.168.241.0/24
|
||||
exec 4< <(n1 ncat -l -u -p 1111)
|
||||
nmap_pid=$!
|
||||
waitncatudp $netns1
|
||||
n2 ncat -u 192.168.241.1 1111 <<<"X"
|
||||
read -r -N 1 -t 1 out <&4 && [[ $out == "X" ]]
|
||||
kill $nmap_pid
|
||||
more_specific_key="$(pp wg genkey | pp wg pubkey)"
|
||||
n0 wg set wg1 peer "$more_specific_key" allowed-ips 192.168.241.2/32
|
||||
n0 wg set wg2 listen-port 9997
|
||||
exec 4< <(n1 ncat -l -u -p 1111)
|
||||
nmap_pid=$!
|
||||
waitncatudp $netns1
|
||||
n2 ncat -u 192.168.241.1 1111 <<<"X"
|
||||
! read -r -N 1 -t 1 out <&4
|
||||
kill $nmap_pid
|
||||
n0 wg set wg1 peer "$more_specific_key" remove
|
||||
[[ $(n1 wg show wg1 endpoints) == "$pub2 [::1]:9997" ]]
|
||||
|
||||
ip1 link del wg1
|
||||
ip2 link del wg2
|
||||
|
||||
# Test using NAT. We now change the topology to this:
|
||||
# ┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐
|
||||
# │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │
|
||||
# │ │ │ │ │ │
|
||||
# │ ┌─────┐ ┌─────┐ │ │ ┌──────┐ ┌──────┐ │ │ ┌─────┐ ┌─────┐ │
|
||||
# │ │ wg1 │─────────────│vethc│───────────┼────┼────│vethrc│ │vethrs│──────────────┼─────┼──│veths│────────────│ wg2 │ │
|
||||
# │ ├─────┴──────────┐ ├─────┴──────────┐│ │ ├──────┴─────────┐ ├──────┴────────────┐ │ │ ├─────┴──────────┐ ├─────┴──────────┐ │
|
||||
# │ │192.168.241.1/24│ │192.168.1.100/24││ │ │192.168.1.100/24│ │10.0.0.1/24 │ │ │ │10.0.0.100/24 │ │192.168.241.2/24│ │
|
||||
# │ │fd00::1/24 │ │ ││ │ │ │ │SNAT:192.168.1.0/24│ │ │ │ │ │fd00::2/24 │ │
|
||||
# │ └────────────────┘ └────────────────┘│ │ └────────────────┘ └───────────────────┘ │ │ └────────────────┘ └────────────────┘ │
|
||||
# └────────────────────────────────────────┘ └────────────────────────────────────────────────┘ └────────────────────────────────────────┘
|
||||
|
||||
# ip1 link add dev wg1 type wireguard
|
||||
# ip2 link add dev wg1 type wireguard
|
||||
|
||||
n1 $program wg1
|
||||
n2 $program wg2
|
||||
|
||||
configure_peers
|
||||
|
||||
ip0 link add vethrc type veth peer name vethc
|
||||
ip0 link add vethrs type veth peer name veths
|
||||
ip0 link set vethc netns $netns1
|
||||
ip0 link set veths netns $netns2
|
||||
ip0 link set vethrc up
|
||||
ip0 link set vethrs up
|
||||
ip0 addr add 192.168.1.1/24 dev vethrc
|
||||
ip0 addr add 10.0.0.1/24 dev vethrs
|
||||
ip1 addr add 192.168.1.100/24 dev vethc
|
||||
ip1 link set vethc up
|
||||
ip1 route add default via 192.168.1.1
|
||||
ip2 addr add 10.0.0.100/24 dev veths
|
||||
ip2 link set veths up
|
||||
waitiface $netns0 vethrc
|
||||
waitiface $netns0 vethrs
|
||||
waitiface $netns1 vethc
|
||||
waitiface $netns2 veths
|
||||
|
||||
n0 bash -c 'printf 1 > /proc/sys/net/ipv4/ip_forward'
|
||||
n0 bash -c 'printf 2 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout'
|
||||
n0 bash -c 'printf 2 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream'
|
||||
n0 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 10.0.0.0/24 -j SNAT --to 10.0.0.1
|
||||
|
||||
n0 wg set wg1 peer "$pub2" endpoint 10.0.0.100:20000 persistent-keepalive 1
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
[[ $(n2 wg show wg2 endpoints) == "$pub1 10.0.0.1:10000" ]]
|
||||
# Demonstrate n2 can still send packets to n1, since persistent-keepalive will prevent connection tracking entry from expiring (to see entries: `n0 conntrack -L`).
|
||||
pp sleep 3
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
|
||||
n0 iptables -t nat -F
|
||||
ip0 link del vethrc
|
||||
ip0 link del vethrs
|
||||
ip1 link del wg1
|
||||
ip2 link del wg2
|
||||
|
||||
# Test that saddr routing is sticky but not too sticky, changing to this topology:
|
||||
# ┌────────────────────────────────────────┐ ┌────────────────────────────────────────┐
|
||||
# │ $ns1 namespace │ │ $ns2 namespace │
|
||||
# │ │ │ │
|
||||
# │ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │
|
||||
# │ │ wg1 │─────────────│veth1│───────────┼────┼──│veth2│────────────│ wg2 │ │
|
||||
# │ ├─────┴──────────┐ ├─────┴──────────┐│ │ ├─────┴──────────┐ ├─────┴──────────┐ │
|
||||
# │ │192.168.241.1/24│ │10.0.0.1/24 ││ │ │10.0.0.2/24 │ │192.168.241.2/24│ │
|
||||
# │ │fd00::1/24 │ │fd00:aa::1/96 ││ │ │fd00:aa::2/96 │ │fd00::2/24 │ │
|
||||
# │ └────────────────┘ └────────────────┘│ │ └────────────────┘ └────────────────┘ │
|
||||
# └────────────────────────────────────────┘ └────────────────────────────────────────┘
|
||||
|
||||
# ip1 link add dev wg1 type wireguard
|
||||
# ip2 link add dev wg1 type wireguard
|
||||
n1 $program wg1
|
||||
n2 $program wg2
|
||||
|
||||
configure_peers
|
||||
|
||||
ip1 link add veth1 type veth peer name veth2
|
||||
ip1 link set veth2 netns $netns2
|
||||
n1 bash -c 'printf 0 > /proc/sys/net/ipv6/conf/veth1/accept_dad'
|
||||
n2 bash -c 'printf 0 > /proc/sys/net/ipv6/conf/veth2/accept_dad'
|
||||
n1 bash -c 'printf 1 > /proc/sys/net/ipv4/conf/veth1/promote_secondaries'
|
||||
|
||||
# First we check that we aren't overly sticky and can fall over to new IPs when old ones are removed
|
||||
ip1 addr add 10.0.0.1/24 dev veth1
|
||||
ip1 addr add fd00:aa::1/96 dev veth1
|
||||
ip2 addr add 10.0.0.2/24 dev veth2
|
||||
ip2 addr add fd00:aa::2/96 dev veth2
|
||||
ip1 link set veth1 up
|
||||
ip2 link set veth2 up
|
||||
waitiface $netns1 veth1
|
||||
waitiface $netns2 veth2
|
||||
n0 wg set wg1 peer "$pub2" endpoint 10.0.0.2:20000
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
ip1 addr add 10.0.0.10/24 dev veth1
|
||||
ip1 addr del 10.0.0.1/24 dev veth1
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
n0 wg set wg1 peer "$pub2" endpoint [fd00:aa::2]:20000
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
ip1 addr add fd00:aa::10/96 dev veth1
|
||||
ip1 addr del fd00:aa::1/96 dev veth1
|
||||
n1 ping -W 1 -c 1 192.168.241.2
|
||||
|
||||
# Now we show that we can successfully do reply to sender routing
|
||||
ip1 link set veth1 down
|
||||
ip2 link set veth2 down
|
||||
ip1 addr flush dev veth1
|
||||
ip2 addr flush dev veth2
|
||||
ip1 addr add 10.0.0.1/24 dev veth1
|
||||
ip1 addr add 10.0.0.2/24 dev veth1
|
||||
ip1 addr add fd00:aa::1/96 dev veth1
|
||||
ip1 addr add fd00:aa::2/96 dev veth1
|
||||
ip2 addr add 10.0.0.3/24 dev veth2
|
||||
ip2 addr add fd00:aa::3/96 dev veth2
|
||||
ip1 link set veth1 up
|
||||
ip2 link set veth2 up
|
||||
waitiface $netns1 veth1
|
||||
waitiface $netns2 veth2
|
||||
n0 wg set wg2 peer "$pub1" endpoint 10.0.0.1:10000
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
[[ $(n0 wg show wg2 endpoints) == "$pub1 10.0.0.1:10000" ]]
|
||||
n0 wg set wg2 peer "$pub1" endpoint [fd00:aa::1]:10000
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
[[ $(n0 wg show wg2 endpoints) == "$pub1 [fd00:aa::1]:10000" ]]
|
||||
n0 wg set wg2 peer "$pub1" endpoint 10.0.0.2:10000
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
[[ $(n0 wg show wg2 endpoints) == "$pub1 10.0.0.2:10000" ]]
|
||||
n0 wg set wg2 peer "$pub1" endpoint [fd00:aa::2]:10000
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
[[ $(n0 wg show wg2 endpoints) == "$pub1 [fd00:aa::2]:10000" ]]
|
||||
|
||||
ip1 link del veth1
|
||||
ip1 link del wg1
|
||||
ip2 link del wg2
|
||||
|
||||
# Test that Netlink/IPC is working properly by doing things that usually cause split responses
|
||||
|
||||
n0 $program wg0
|
||||
sleep 5
|
||||
config=( "[Interface]" "PrivateKey=$(wg genkey)" "[Peer]" "PublicKey=$(wg genkey)" )
|
||||
for a in {1..255}; do
|
||||
for b in {0..255}; do
|
||||
config+=( "AllowedIPs=$a.$b.0.0/16,$a::$b/128" )
|
||||
done
|
||||
done
|
||||
n0 wg setconf wg0 <(printf '%s\n' "${config[@]}")
|
||||
i=0
|
||||
for ip in $(n0 wg show wg0 allowed-ips); do
|
||||
((++i))
|
||||
done
|
||||
((i == 255*256*2+1))
|
||||
ip0 link del wg0
|
||||
|
||||
n0 $program wg0
|
||||
config=( "[Interface]" "PrivateKey=$(wg genkey)" )
|
||||
for a in {1..40}; do
|
||||
config+=( "[Peer]" "PublicKey=$(wg genkey)" )
|
||||
for b in {1..52}; do
|
||||
config+=( "AllowedIPs=$a.$b.0.0/16" )
|
||||
done
|
||||
done
|
||||
n0 wg setconf wg0 <(printf '%s\n' "${config[@]}")
|
||||
i=0
|
||||
while read -r line; do
|
||||
j=0
|
||||
for ip in $line; do
|
||||
((++j))
|
||||
done
|
||||
((j == 53))
|
||||
((++i))
|
||||
done < <(n0 wg show wg0 allowed-ips)
|
||||
((i == 40))
|
||||
ip0 link del wg0
|
||||
|
||||
n0 $program wg0
|
||||
config=( )
|
||||
for i in {1..29}; do
|
||||
config+=( "[Peer]" "PublicKey=$(wg genkey)" )
|
||||
done
|
||||
config+=( "[Peer]" "PublicKey=$(wg genkey)" "AllowedIPs=255.2.3.4/32,abcd::255/128" )
|
||||
n0 wg setconf wg0 <(printf '%s\n' "${config[@]}")
|
||||
n0 wg showconf wg0 > /dev/null
|
||||
ip0 link del wg0
|
||||
|
||||
! n0 wg show doesnotexist || false
|
||||
|
||||
declare -A objects
|
||||
while read -t 0.1 -r line 2>/dev/null || [[ $? -ne 142 ]]; do
|
||||
[[ $line =~ .*(wg[0-9]+:\ [A-Z][a-z]+\ [0-9]+)\ .*(created|destroyed).* ]] || continue
|
||||
objects["${BASH_REMATCH[1]}"]+="${BASH_REMATCH[2]}"
|
||||
done < /dev/kmsg
|
||||
alldeleted=1
|
||||
for object in "${!objects[@]}"; do
|
||||
if [[ ${objects["$object"]} != *createddestroyed ]]; then
|
||||
echo "Error: $object: merely ${objects["$object"]}" >&3
|
||||
alldeleted=0
|
||||
fi
|
||||
done
|
||||
[[ $alldeleted -eq 1 ]]
|
||||
pretty "" "Objects that were created were also destroyed."
|
@ -1,67 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func checkAlignment(t *testing.T, name string, offset uintptr) {
|
||||
t.Helper()
|
||||
if offset%8 != 0 {
|
||||
t.Errorf("offset of %q within struct is %d bytes, which does not align to 64-bit word boundaries (missing %d bytes). Atomic operations will crash on 32-bit systems.", name, offset, 8-(offset%8))
|
||||
}
|
||||
}
|
||||
|
||||
// TestRateJugglerAlignment checks that atomically-accessed fields are
|
||||
// aligned to 64-bit boundaries, as required by the atomic package.
|
||||
//
|
||||
// Unfortunately, violating this rule on 32-bit platforms results in a
|
||||
// hard segfault at runtime.
|
||||
func TestRateJugglerAlignment(t *testing.T) {
|
||||
var r rateJuggler
|
||||
|
||||
typ := reflect.TypeOf(&r).Elem()
|
||||
t.Logf("Peer type size: %d, with fields:", typ.Size())
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)",
|
||||
field.Name,
|
||||
field.Offset,
|
||||
field.Type.Size(),
|
||||
field.Type.Align(),
|
||||
)
|
||||
}
|
||||
|
||||
checkAlignment(t, "rateJuggler.current", unsafe.Offsetof(r.current))
|
||||
checkAlignment(t, "rateJuggler.nextByteCount", unsafe.Offsetof(r.nextByteCount))
|
||||
checkAlignment(t, "rateJuggler.nextStartTime", unsafe.Offsetof(r.nextStartTime))
|
||||
}
|
||||
|
||||
// TestNativeTunAlignment checks that atomically-accessed fields are
|
||||
// aligned to 64-bit boundaries, as required by the atomic package.
|
||||
//
|
||||
// Unfortunately, violating this rule on 32-bit platforms results in a
|
||||
// hard segfault at runtime.
|
||||
func TestNativeTunAlignment(t *testing.T) {
|
||||
var tun NativeTun
|
||||
|
||||
typ := reflect.TypeOf(&tun).Elem()
|
||||
t.Logf("Peer type size: %d, with fields:", typ.Size())
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)",
|
||||
field.Name,
|
||||
field.Offset,
|
||||
field.Type.Size(),
|
||||
field.Type.Align(),
|
||||
)
|
||||
}
|
||||
|
||||
checkAlignment(t, "NativeTun.rate", unsafe.Offsetof(tun.rate))
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// +build ignore
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/device"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tun, tnet, err := netstack.CreateNetTUN(
|
||||
[]net.IP{net.ParseIP("192.168.4.29")},
|
||||
[]net.IP{net.ParseIP("8.8.8.8")},
|
||||
1420)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, ""))
|
||||
dev.IpcSet(`private_key=a8dac1d8a70a751f0f699fb14ba1cff7b79cf4fbd8f09f44c6e6a90d0369604f
|
||||
public_key=25123c5dcd3328ff645e4f2a3fce0d754400d3887a0cb7c56f0267e20fbf3c5b
|
||||
endpoint=163.172.161.0:12912
|
||||
allowed_ip=0.0.0.0/0
|
||||
`)
|
||||
err = dev.Up()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: tnet.DialContext,
|
||||
},
|
||||
}
|
||||
resp, err := client.Get("https://www.zx2c4.com/ip")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Println(string(body))
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// +build ignore
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/conn"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/device"
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tun, tnet, err := netstack.CreateNetTUN(
|
||||
[]net.IP{net.ParseIP("192.168.4.29")},
|
||||
[]net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")},
|
||||
1420,
|
||||
)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, ""))
|
||||
dev.IpcSet(`private_key=a8dac1d8a70a751f0f699fb14ba1cff7b79cf4fbd8f09f44c6e6a90d0369604f
|
||||
public_key=25123c5dcd3328ff645e4f2a3fce0d754400d3887a0cb7c56f0267e20fbf3c5b
|
||||
endpoint=163.172.161.0:12912
|
||||
allowed_ip=0.0.0.0/0
|
||||
persistent_keepalive_interval=25
|
||||
`)
|
||||
dev.Up()
|
||||
listener, err := tnet.ListenTCP(&net.TCPAddr{Port: 80})
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||
log.Printf("> %s - %s - %s", request.RemoteAddr, request.URL.String(), request.UserAgent())
|
||||
io.WriteString(writer, "Hello from userspace TCP!")
|
||||
})
|
||||
err = http.Serve(listener, nil)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
module golang.zx2c4.com/wireguard/tun/netstack
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0
|
||||
)
|
@ -1,605 +0,0 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
|
||||
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
|
||||
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210115211752-39141e76b647/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22 h1:ytS28bw9HtZVDRMDxviC6ryCJuccw+zXhh04u2IRWJw=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.36.0-dev.0.20210208035533-9280052d3665/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0 h1:Ny53d6x76f7EgvYe7pi8SQcIzdRM69y1ckQ8rRtT6ww=
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0/go.mod h1:ucHEMlckp+S/YzKEpwwAyGBhAh807Wxq/8Erc6gFxCE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs=
|
||||
k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
|
||||
k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
|
||||
k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
@ -1,818 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
)
|
||||
|
||||
type netTun struct {
|
||||
stack *stack.Stack
|
||||
dispatcher stack.NetworkDispatcher
|
||||
events chan tun.Event
|
||||
incomingPacket chan buffer.VectorisedView
|
||||
mtu int
|
||||
dnsServers []net.IP
|
||||
hasV4, hasV6 bool
|
||||
}
|
||||
type endpoint netTun
|
||||
type Net netTun
|
||||
|
||||
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
}
|
||||
|
||||
func (e *endpoint) IsAttached() bool {
|
||||
return e.dispatcher != nil
|
||||
}
|
||||
|
||||
func (e *endpoint) MTU() uint32 {
|
||||
mtu, err := (*netTun)(e).MTU()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uint32(mtu)
|
||||
}
|
||||
|
||||
func (*endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityNone
|
||||
}
|
||||
|
||||
func (*endpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Net, error) {
|
||||
opts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
HandleLocal: true,
|
||||
}
|
||||
dev := &netTun{
|
||||
stack: stack.New(opts),
|
||||
events: make(chan tun.Event, 10),
|
||||
incomingPacket: make(chan buffer.VectorisedView),
|
||||
dnsServers: dnsServers,
|
||||
mtu: mtu,
|
||||
}
|
||||
tcpipErr := dev.stack.CreateNIC(1, (*endpoint)(dev))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||
}
|
||||
for _, ip := range localAddresses {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv4.ProtocolNumber, tcpip.Address(ip4))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
dev.hasV4 = true
|
||||
} else {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv6.ProtocolNumber, tcpip.Address(ip))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
dev.hasV6 = true
|
||||
}
|
||||
}
|
||||
if dev.hasV4 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1})
|
||||
}
|
||||
if dev.hasV6 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||
}
|
||||
|
||||
dev.events <- tun.EventUp
|
||||
return dev, (*Net)(dev), nil
|
||||
}
|
||||
|
||||
func (tun *netTun) Name() (string, error) {
|
||||
return "go", nil
|
||||
}
|
||||
|
||||
func (tun *netTun) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *netTun) Events() chan tun.Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *netTun) Read(buf []byte, offset int) (int, error) {
|
||||
view, ok := <-tun.incomingPacket
|
||||
if !ok {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
return view.Read(buf[offset:])
|
||||
}
|
||||
|
||||
func (tun *netTun) Write(buf []byte, offset int) (int, error) {
|
||||
packet := buf[offset:]
|
||||
if len(packet) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Data: buffer.NewVectorisedView(len(packet), []buffer.View{buffer.NewViewFromBytes(packet)})})
|
||||
switch packet[0] >> 4 {
|
||||
case 4:
|
||||
tun.dispatcher.DeliverNetworkPacket("", "", ipv4.ProtocolNumber, pkb)
|
||||
case 6:
|
||||
tun.dispatcher.DeliverNetworkPacket("", "", ipv6.ProtocolNumber, pkb)
|
||||
}
|
||||
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (tun *netTun) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *netTun) Close() error {
|
||||
tun.stack.RemoveNIC(1)
|
||||
|
||||
if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
if tun.incomingPacket != nil {
|
||||
close(tun.incomingPacket)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *netTun) MTU() (int, error) {
|
||||
return tun.mtu, nil
|
||||
}
|
||||
|
||||
func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip4),
|
||||
Port: uint16(port),
|
||||
}, ipv4.ProtocolNumber
|
||||
} else {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip),
|
||||
Port: uint16(port),
|
||||
}, ipv6.ProtocolNumber
|
||||
}
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
var lfa, rfa *tcpip.FullAddress
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
if laddr != nil {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(laddr.IP, laddr.Port)
|
||||
lfa = &addr
|
||||
}
|
||||
if raddr != nil {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(raddr.IP, raddr.Port)
|
||||
rfa = &addr
|
||||
}
|
||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||
}
|
||||
|
||||
var (
|
||||
errNoSuchHost = errors.New("no such host")
|
||||
errLameReferral = errors.New("lame referral")
|
||||
errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
|
||||
errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
|
||||
errServerMisbehaving = errors.New("server misbehaving")
|
||||
errInvalidDNSResponse = errors.New("invalid DNS response")
|
||||
errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
|
||||
errServerTemporarilyMisbehaving = errors.New("server misbehaving")
|
||||
errCanceled = errors.New("operation was canceled")
|
||||
errTimeout = errors.New("i/o timeout")
|
||||
errNumericPort = errors.New("port must be numeric")
|
||||
errNoSuitableAddress = errors.New("no suitable address found")
|
||||
errMissingAddress = errors.New("missing address")
|
||||
)
|
||||
|
||||
func (net *Net) LookupHost(host string) (addrs []string, err error) {
|
||||
return net.LookupContextHost(context.Background(), host)
|
||||
}
|
||||
|
||||
func isDomainName(s string) bool {
|
||||
l := len(s)
|
||||
if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
|
||||
return false
|
||||
}
|
||||
last := byte('.')
|
||||
nonNumeric := false
|
||||
partlen := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
|
||||
nonNumeric = true
|
||||
partlen++
|
||||
case '0' <= c && c <= '9':
|
||||
partlen++
|
||||
case c == '-':
|
||||
if last == '.' {
|
||||
return false
|
||||
}
|
||||
partlen++
|
||||
nonNumeric = true
|
||||
case c == '.':
|
||||
if last == '.' || last == '-' {
|
||||
return false
|
||||
}
|
||||
if partlen > 63 || partlen == 0 {
|
||||
return false
|
||||
}
|
||||
partlen = 0
|
||||
}
|
||||
last = c
|
||||
}
|
||||
if last == '-' || partlen > 63 {
|
||||
return false
|
||||
}
|
||||
return nonNumeric
|
||||
}
|
||||
|
||||
func randU16() uint16 {
|
||||
var b [2]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return binary.LittleEndian.Uint16(b[:])
|
||||
}
|
||||
|
||||
func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) {
|
||||
id = randU16()
|
||||
b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true})
|
||||
b.EnableCompression()
|
||||
if err := b.StartQuestions(); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
if err := b.Question(q); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
tcpReq, err = b.Finish()
|
||||
udpReq = tcpReq[2:]
|
||||
l := len(tcpReq) - 2
|
||||
tcpReq[0] = byte(l >> 8)
|
||||
tcpReq[1] = byte(l)
|
||||
return id, udpReq, tcpReq, err
|
||||
}
|
||||
|
||||
func equalASCIIName(x, y dnsmessage.Name) bool {
|
||||
if x.Length != y.Length {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < int(x.Length); i++ {
|
||||
a := x.Data[i]
|
||||
b := y.Data[i]
|
||||
if 'A' <= a && a <= 'Z' {
|
||||
a += 0x20
|
||||
}
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
b += 0x20
|
||||
}
|
||||
if a != b {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
|
||||
if !respHdr.Response {
|
||||
return false
|
||||
}
|
||||
if reqID != respHdr.ID {
|
||||
return false
|
||||
}
|
||||
if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func dnsPacketRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
if _, err := c.Write(b); err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
b = make([]byte, 512)
|
||||
for {
|
||||
n, err := c.Read(b)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
h, err := p.Start(b[:n])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
q, err := p.Question()
|
||||
if err != nil || !checkResponse(id, query, h, q) {
|
||||
continue
|
||||
}
|
||||
return p, h, nil
|
||||
}
|
||||
}
|
||||
|
||||
func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
if _, err := c.Write(b); err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
b = make([]byte, 1280)
|
||||
if _, err := io.ReadFull(c, b[:2]); err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
l := int(b[0])<<8 | int(b[1])
|
||||
if l > len(b) {
|
||||
b = make([]byte, l)
|
||||
}
|
||||
n, err := io.ReadFull(c, b[:l])
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
h, err := p.Start(b[:n])
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
|
||||
}
|
||||
q, err := p.Question()
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
|
||||
}
|
||||
if !checkResponse(id, query, h, q) {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
|
||||
}
|
||||
return p, h, nil
|
||||
}
|
||||
|
||||
func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
q.Class = dnsmessage.ClassINET
|
||||
id, udpReq, tcpReq, err := newRequest(q)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
|
||||
}
|
||||
|
||||
for _, useUDP := range []bool{true, false} {
|
||||
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
|
||||
defer cancel()
|
||||
|
||||
var c net.Conn
|
||||
var err error
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: server, Port: 53})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(ctx, &net.TCPAddr{IP: server, Port: 53})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
if d, ok := ctx.Deadline(); ok && !d.IsZero() {
|
||||
c.SetDeadline(d)
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
var h dnsmessage.Header
|
||||
if useUDP {
|
||||
p, h, err = dnsPacketRoundTrip(c, id, q, udpReq)
|
||||
} else {
|
||||
p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq)
|
||||
}
|
||||
c.Close()
|
||||
if err != nil {
|
||||
if err == context.Canceled {
|
||||
err = errCanceled
|
||||
} else if err == context.DeadlineExceeded {
|
||||
err = errTimeout
|
||||
}
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
|
||||
}
|
||||
if h.Truncated {
|
||||
continue
|
||||
}
|
||||
return p, h, nil
|
||||
}
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
|
||||
}
|
||||
|
||||
func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
|
||||
if h.RCode == dnsmessage.RCodeNameError {
|
||||
return errNoSuchHost
|
||||
}
|
||||
_, err := p.AnswerHeader()
|
||||
if err != nil && err != dnsmessage.ErrSectionDone {
|
||||
return errCannotUnmarshalDNSMessage
|
||||
}
|
||||
if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
|
||||
return errLameReferral
|
||||
}
|
||||
if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
|
||||
if h.RCode == dnsmessage.RCodeServerFailure {
|
||||
return errServerTemporarilyMisbehaving
|
||||
}
|
||||
return errServerMisbehaving
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
|
||||
for {
|
||||
h, err := p.AnswerHeader()
|
||||
if err == dnsmessage.ErrSectionDone {
|
||||
return errNoSuchHost
|
||||
}
|
||||
if err != nil {
|
||||
return errCannotUnmarshalDNSMessage
|
||||
}
|
||||
if h.Type == qtype {
|
||||
return nil
|
||||
}
|
||||
if err := p.SkipAnswer(); err != nil {
|
||||
return errCannotUnmarshalDNSMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tnet *Net) tryOneName(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
|
||||
var lastErr error
|
||||
|
||||
n, err := dnsmessage.NewName(name)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
|
||||
}
|
||||
q := dnsmessage.Question{
|
||||
Name: n,
|
||||
Type: qtype,
|
||||
Class: dnsmessage.ClassINET,
|
||||
}
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
for _, server := range tnet.dnsServers {
|
||||
p, h, err := tnet.exchange(ctx, server, q, time.Second*5)
|
||||
if err != nil {
|
||||
dnsErr := &net.DNSError{
|
||||
Err: err.Error(),
|
||||
Name: name,
|
||||
Server: server.String(),
|
||||
}
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
dnsErr.IsTimeout = true
|
||||
}
|
||||
if _, ok := err.(*net.OpError); ok {
|
||||
dnsErr.IsTemporary = true
|
||||
}
|
||||
lastErr = dnsErr
|
||||
continue
|
||||
}
|
||||
|
||||
if err := checkHeader(&p, h); err != nil {
|
||||
dnsErr := &net.DNSError{
|
||||
Err: err.Error(),
|
||||
Name: name,
|
||||
Server: server.String(),
|
||||
}
|
||||
if err == errServerTemporarilyMisbehaving {
|
||||
dnsErr.IsTemporary = true
|
||||
}
|
||||
if err == errNoSuchHost {
|
||||
dnsErr.IsNotFound = true
|
||||
return p, server.String(), dnsErr
|
||||
}
|
||||
lastErr = dnsErr
|
||||
continue
|
||||
}
|
||||
|
||||
err = skipToAnswer(&p, qtype)
|
||||
if err == nil {
|
||||
return p, server.String(), nil
|
||||
}
|
||||
lastErr = &net.DNSError{
|
||||
Err: err.Error(),
|
||||
Name: name,
|
||||
Server: server.String(),
|
||||
}
|
||||
if err == errNoSuchHost {
|
||||
lastErr.(*net.DNSError).IsNotFound = true
|
||||
return p, server.String(), lastErr
|
||||
}
|
||||
}
|
||||
}
|
||||
return dnsmessage.Parser{}, "", lastErr
|
||||
}
|
||||
|
||||
func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string, error) {
|
||||
if host == "" || (!tnet.hasV6 && !tnet.hasV4) {
|
||||
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||
}
|
||||
zlen := len(host)
|
||||
if strings.IndexByte(host, ':') != -1 {
|
||||
if zidx := strings.LastIndexByte(host, '%'); zidx != -1 {
|
||||
zlen = zidx
|
||||
}
|
||||
}
|
||||
if ip := net.ParseIP(host[:zlen]); ip != nil {
|
||||
return []string{host[:zlen]}, nil
|
||||
}
|
||||
|
||||
if !isDomainName(host) {
|
||||
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||
}
|
||||
type result struct {
|
||||
p dnsmessage.Parser
|
||||
server string
|
||||
error
|
||||
}
|
||||
var addrsV4, addrsV6 []net.IP
|
||||
lanes := 0
|
||||
if tnet.hasV4 {
|
||||
lanes++
|
||||
}
|
||||
if tnet.hasV6 {
|
||||
lanes++
|
||||
}
|
||||
lane := make(chan result, lanes)
|
||||
var lastErr error
|
||||
if tnet.hasV4 {
|
||||
go func() {
|
||||
p, server, err := tnet.tryOneName(ctx, host+".", dnsmessage.TypeA)
|
||||
lane <- result{p, server, err}
|
||||
}()
|
||||
}
|
||||
if tnet.hasV6 {
|
||||
go func() {
|
||||
p, server, err := tnet.tryOneName(ctx, host+".", dnsmessage.TypeAAAA)
|
||||
lane <- result{p, server, err}
|
||||
}()
|
||||
}
|
||||
for l := 0; l < lanes; l++ {
|
||||
result := <-lane
|
||||
if result.error != nil {
|
||||
if lastErr == nil {
|
||||
lastErr = result.error
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
h, err := result.p.AnswerHeader()
|
||||
if err != nil && err != dnsmessage.ErrSectionDone {
|
||||
lastErr = &net.DNSError{
|
||||
Err: errCannotMarshalDNSMessage.Error(),
|
||||
Name: host,
|
||||
Server: result.server,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch h.Type {
|
||||
case dnsmessage.TypeA:
|
||||
a, err := result.p.AResource()
|
||||
if err != nil {
|
||||
lastErr = &net.DNSError{
|
||||
Err: errCannotMarshalDNSMessage.Error(),
|
||||
Name: host,
|
||||
Server: result.server,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV4 = append(addrsV4, net.IP(a.A[:]))
|
||||
|
||||
case dnsmessage.TypeAAAA:
|
||||
aaaa, err := result.p.AAAAResource()
|
||||
if err != nil {
|
||||
lastErr = &net.DNSError{
|
||||
Err: errCannotMarshalDNSMessage.Error(),
|
||||
Name: host,
|
||||
Server: result.server,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV6 = append(addrsV6, net.IP(aaaa.AAAA[:]))
|
||||
|
||||
default:
|
||||
if err := result.p.SkipAnswer(); err != nil {
|
||||
lastErr = &net.DNSError{
|
||||
Err: errCannotMarshalDNSMessage.Error(),
|
||||
Name: host,
|
||||
Server: result.server,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
// We don't do RFC6724. Instead just put V6 addresess first if an IPv6 address is enabled
|
||||
var addrs []net.IP
|
||||
if tnet.hasV6 {
|
||||
addrs = append(addrsV6, addrsV4...)
|
||||
} else {
|
||||
addrs = append(addrsV4, addrsV6...)
|
||||
}
|
||||
|
||||
if len(addrs) == 0 && lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
saddrs := make([]string, 0, len(addrs))
|
||||
for _, ip := range addrs {
|
||||
saddrs = append(saddrs, ip.String())
|
||||
}
|
||||
return saddrs, nil
|
||||
}
|
||||
|
||||
func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
|
||||
if deadline.IsZero() {
|
||||
return deadline, nil
|
||||
}
|
||||
timeRemaining := deadline.Sub(now)
|
||||
if timeRemaining <= 0 {
|
||||
return time.Time{}, errTimeout
|
||||
}
|
||||
timeout := timeRemaining / time.Duration(addrsRemaining)
|
||||
const saneMinimum = 2 * time.Second
|
||||
if timeout < saneMinimum {
|
||||
if timeRemaining < saneMinimum {
|
||||
timeout = timeRemaining
|
||||
} else {
|
||||
timeout = saneMinimum
|
||||
}
|
||||
}
|
||||
return now.Add(timeout), nil
|
||||
}
|
||||
|
||||
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
var acceptV4, acceptV6, useUDP bool
|
||||
if len(network) == 3 {
|
||||
acceptV4 = true
|
||||
acceptV6 = true
|
||||
} else if len(network) == 4 {
|
||||
acceptV4 = network[3] == '4'
|
||||
acceptV6 = network[3] == '6'
|
||||
}
|
||||
if !acceptV4 && !acceptV6 {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
if network[:3] == "udp" {
|
||||
useUDP = true
|
||||
} else if network[:3] != "tcp" {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
host, sport, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
}
|
||||
allAddr, err := tnet.LookupContextHost(ctx, host)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
var addrs []net.IP
|
||||
for _, addr := range allAddr {
|
||||
if strings.IndexByte(addr, ':') != -1 && acceptV6 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
} else if strings.IndexByte(addr, '.') != -1 && acceptV4 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 && len(allAddr) != 0 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNoSuitableAddress}
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for i, addr := range addrs {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
if err == context.Canceled {
|
||||
err = errCanceled
|
||||
} else if err == context.DeadlineExceeded {
|
||||
err = errTimeout
|
||||
}
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
default:
|
||||
}
|
||||
|
||||
dialCtx := ctx
|
||||
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||
partialDeadline, err := partialDeadline(time.Now(), deadline, len(addrs)-i)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
break
|
||||
}
|
||||
if partialDeadline.Before(deadline) {
|
||||
var cancel context.CancelFunc
|
||||
dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
|
||||
defer cancel()
|
||||
}
|
||||
}
|
||||
|
||||
var c net.Conn
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: addr, Port: port})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(dialCtx, &net.TCPAddr{IP: addr, Port: port})
|
||||
}
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = &net.OpError{Op: "dial", Err: errMissingAddress}
|
||||
}
|
||||
return nil, firstErr
|
||||
}
|
||||
|
||||
func (tnet *Net) Dial(network, address string) (net.Conn, error) {
|
||||
return tnet.DialContext(context.Background(), network, address)
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(fn)
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
|
||||
}
|
||||
}
|
29
tun/tun.go
29
tun/tun.go
@ -1,29 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type Event int
|
||||
|
||||
const (
|
||||
EventUp = 1 << iota
|
||||
EventDown
|
||||
EventMTUUpdate
|
||||
)
|
||||
|
||||
type Device interface {
|
||||
File() *os.File // returns the file descriptor of the device
|
||||
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
|
||||
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
|
||||
Flush() error // flush all previous writes to the device
|
||||
MTU() (int, error) // returns the MTU of the device
|
||||
Name() (string, error) // fetches and returns the current name
|
||||
Events() chan Event // returns a constant channel of events related to the device
|
||||
Close() error // stops the device and closes the event channel
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const utunControlName = "com.apple.net.utun_control"
|
||||
|
||||
type NativeTun struct {
|
||||
name string
|
||||
tunFile *os.File
|
||||
events chan Event
|
||||
errors chan error
|
||||
routeSocket int
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func retryInterfaceByIndex(index int) (iface *net.Interface, err error) {
|
||||
for i := 0; i < 20; i++ {
|
||||
iface, err = net.InterfaceByIndex(index)
|
||||
if err != nil && errors.Is(err, syscall.ENOMEM) {
|
||||
time.Sleep(time.Duration(i) * time.Second / 3)
|
||||
continue
|
||||
}
|
||||
return iface, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 14 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := retryInterfaceByIndex(ifindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- EventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- EventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- EventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
}
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (Device, error) {
|
||||
ifIndex := -1
|
||||
if name != "utun" {
|
||||
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
|
||||
if err != nil || ifIndex < 0 {
|
||||
return nil, fmt.Errorf("Interface name must be utun[0-9]*")
|
||||
}
|
||||
}
|
||||
|
||||
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctlInfo := &unix.CtlInfo{}
|
||||
copy(ctlInfo.Name[:], []byte(utunControlName))
|
||||
err = unix.IoctlCtlInfo(fd, ctlInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("IoctlGetCtlInfo: %w", err)
|
||||
}
|
||||
|
||||
sc := &unix.SockaddrCtl{
|
||||
ID: ctlInfo.Id,
|
||||
Unit: uint32(ifIndex) + 1,
|
||||
}
|
||||
|
||||
err = unix.Connect(fd, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
|
||||
|
||||
if err == nil && name == "utun" {
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
os.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
}
|
||||
|
||||
return tun, err
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 10),
|
||||
errors: make(chan error, 5),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
if mtu > 0 {
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
var err error
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
tun.name, err = unix.GetsockoptString(
|
||||
int(fd),
|
||||
2, /* #define SYSPROTO_CONTROL 2 */
|
||||
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||
)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetSockoptString: %w", err)
|
||||
}
|
||||
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
buff[2] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[3] = unix.AF_INET6
|
||||
} else {
|
||||
buff[3] = unix.AF_INET
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
// TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1, err2 error
|
||||
tun.closeOnce.Do(func() {
|
||||
err1 = tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
})
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
var ifr unix.IfreqMTU
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = int32(n)
|
||||
err = unix.IoctlSetIfreqMTU(fd, &ifr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set MTU on %s: %w", tun.name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
ifr, err := unix.IoctlGetIfreqMTU(fd, tun.name)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, err)
|
||||
}
|
||||
|
||||
return int(ifr.MTU), nil
|
||||
}
|
@ -1,430 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
_TUNSIFHEAD = 0x80047460
|
||||
_TUNSIFMODE = 0x8004745e
|
||||
_TUNGIFNAME = 0x4020745d
|
||||
_TUNSIFPID = 0x2000745f
|
||||
|
||||
_SIOCGIFINFO_IN6 = 0xc048696c
|
||||
_SIOCSIFINFO_IN6 = 0xc048696d
|
||||
_ND6_IFF_AUTO_LINKLOCAL = 0x20
|
||||
_ND6_IFF_NO_DAD = 0x100
|
||||
)
|
||||
|
||||
// Iface requests with just the name
|
||||
type ifreqName struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
_ [16]byte
|
||||
}
|
||||
|
||||
// Iface requests with a pointer
|
||||
type ifreqPtr struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Data uintptr
|
||||
_ [16 - unsafe.Sizeof(uintptr(0))]byte
|
||||
}
|
||||
|
||||
// Iface requests with MTU
|
||||
type ifreqMtu struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU uint32
|
||||
_ [12]byte
|
||||
}
|
||||
|
||||
// ND6 flag manipulation
|
||||
type nd6Req struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Linkmtu uint32
|
||||
Maxmtu uint32
|
||||
Basereachable uint32
|
||||
Reachable uint32
|
||||
Retrans uint32
|
||||
Flags uint32
|
||||
Recalctm int
|
||||
Chlim uint8
|
||||
Initialized uint8
|
||||
Randomseed0 [8]byte
|
||||
Randomseed1 [8]byte
|
||||
Randomid [8]byte
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
name string
|
||||
tunFile *os.File
|
||||
events chan Event
|
||||
errors chan error
|
||||
routeSocket int
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errors.Is(err, syscall.EINTR) {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 14 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByIndex(ifindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- EventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- EventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- EventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
}
|
||||
}
|
||||
|
||||
func tunName(fd uintptr) (string, error) {
|
||||
var ifreq ifreqName
|
||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, _TUNGIFNAME, uintptr(unsafe.Pointer(&ifreq)))
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
return unix.ByteSliceToString(ifreq.Name[:]), nil
|
||||
}
|
||||
|
||||
// Destroy a named system interface
|
||||
func tunDestroy(name string) error {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer unix.Close(fd)
|
||||
|
||||
var ifr [32]byte
|
||||
copy(ifr[:], name)
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCIFDESTROY), uintptr(unsafe.Pointer(&ifr[0])))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to destroy interface %s: %w", name, errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (Device, error) {
|
||||
if len(name) > unix.IFNAMSIZ-1 {
|
||||
return nil, errors.New("interface name too long")
|
||||
}
|
||||
|
||||
// See if interface already exists
|
||||
iface, _ := net.InterfaceByName(name)
|
||||
if iface != nil {
|
||||
return nil, fmt.Errorf("interface %s already exists", name)
|
||||
}
|
||||
|
||||
tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun := NativeTun{tunFile: tunFile}
|
||||
var assignedName string
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
assignedName, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
|
||||
ifheadmode := 1
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFHEAD, uintptr(unsafe.Pointer(&ifheadmode)))
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("unable to put into IFHEAD mode: %w", errno)
|
||||
}
|
||||
|
||||
// Get out of PTP mode.
|
||||
ifflags := syscall.IFF_BROADCAST | syscall.IFF_MULTICAST
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(_TUNSIFMODE), uintptr(unsafe.Pointer(&ifflags)))
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("unable to put into IFF_BROADCAST mode: %w", errno)
|
||||
}
|
||||
|
||||
// Disable link-local v6, not just because WireGuard doesn't do that anyway, but
|
||||
// also because there are serious races with attaching and detaching LLv6 addresses
|
||||
// in relation to interface lifetime within the FreeBSD kernel.
|
||||
confd6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, err
|
||||
}
|
||||
defer unix.Close(confd6)
|
||||
var ndireq nd6Req
|
||||
copy(ndireq.Name[:], assignedName)
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCGIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("unable to get nd6 flags for %s: %w", assignedName, errno)
|
||||
}
|
||||
ndireq.Flags = ndireq.Flags &^ _ND6_IFF_AUTO_LINKLOCAL
|
||||
ndireq.Flags = ndireq.Flags | _ND6_IFF_NO_DAD
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCSIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("unable to set nd6 flags for %s: %w", assignedName, errno)
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
confd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, err
|
||||
}
|
||||
defer unix.Close(confd)
|
||||
var newnp [unix.IFNAMSIZ]byte
|
||||
copy(newnp[:], name)
|
||||
var ifr ifreqPtr
|
||||
copy(ifr.Name[:], assignedName)
|
||||
ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("Failed to rename %s to %s: %w", assignedName, name, errno)
|
||||
}
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(tunFile, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFPID, uintptr(0))
|
||||
})
|
||||
if errno != 0 {
|
||||
tun.tunFile.Close()
|
||||
return nil, fmt.Errorf("unable to become controlling TUN process: %w", errno)
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
var name string
|
||||
var err error
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
name, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tun.name = name
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buf []byte, offset int) (int, error) {
|
||||
if offset < 4 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
buf = buf[offset-4:]
|
||||
if len(buf) < 5 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
buf[0] = 0x00
|
||||
buf[1] = 0x00
|
||||
buf[2] = 0x00
|
||||
switch buf[4] >> 4 {
|
||||
case 4:
|
||||
buf[3] = unix.AF_INET
|
||||
case 6:
|
||||
buf[3] = unix.AF_INET6
|
||||
default:
|
||||
return 0, unix.EAFNOSUPPORT
|
||||
}
|
||||
return tun.tunFile.Write(buf)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
// TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1, err2, err3 error
|
||||
tun.closeOnce.Do(func() {
|
||||
err1 = tun.tunFile.Close()
|
||||
err2 = tunDestroy(tun.name)
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err3 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
})
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer unix.Close(fd)
|
||||
|
||||
var ifr ifreqMtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = uint32(n)
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer unix.Close(fd)
|
||||
|
||||
var ifr ifreqMtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno)
|
||||
}
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
|
||||
}
|
509
tun/tun_linux.go
509
tun/tun_linux.go
@ -1,509 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
/* Implementation of the TUN device interface for linux
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/rwcancel"
|
||||
)
|
||||
|
||||
const (
|
||||
cloneDevicePath = "/dev/net/tun"
|
||||
ifReqSize = unix.IFNAMSIZ + 64
|
||||
)
|
||||
|
||||
type NativeTun struct {
|
||||
tunFile *os.File
|
||||
index int32 // if index
|
||||
errors chan error // async error handling
|
||||
events chan Event // device related events
|
||||
nopi bool // the device was passed IFF_NO_PI
|
||||
netlinkSock int
|
||||
netlinkCancel *rwcancel.RWCancel
|
||||
hackListenerClosed sync.Mutex
|
||||
statusListenersShutdown chan struct{}
|
||||
|
||||
closeOnce sync.Once
|
||||
|
||||
nameOnce sync.Once // guards calling initNameCache, which sets following fields
|
||||
nameCache string // name of interface
|
||||
nameErr error
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineHackListener() {
|
||||
defer tun.hackListenerClosed.Unlock()
|
||||
/* This is needed for the detection to work across network namespaces
|
||||
* If you are reading this and know a better method, please get in touch.
|
||||
*/
|
||||
last := 0
|
||||
const (
|
||||
up = 1
|
||||
down = 2
|
||||
)
|
||||
for {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
_, err = unix.Write(int(fd), nil)
|
||||
})
|
||||
if err2 != nil {
|
||||
return
|
||||
}
|
||||
switch err {
|
||||
case unix.EINVAL:
|
||||
if last != up {
|
||||
// If the tunnel is up, it reports that write() is
|
||||
// allowed but we provided invalid data.
|
||||
tun.events <- EventUp
|
||||
last = up
|
||||
}
|
||||
case unix.EIO:
|
||||
if last != down {
|
||||
// If the tunnel is down, it reports that no I/O
|
||||
// is possible, without checking our provided data.
|
||||
tun.events <- EventDown
|
||||
last = down
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
// nothing
|
||||
case <-tun.statusListenersShutdown:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createNetlinkSocket() (int, error) {
|
||||
sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
saddr := &unix.SockaddrNetlink{
|
||||
Family: unix.AF_NETLINK,
|
||||
Groups: unix.RTMGRP_LINK | unix.RTMGRP_IPV4_IFADDR | unix.RTMGRP_IPV6_IFADDR,
|
||||
}
|
||||
err = unix.Bind(sock, saddr)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return sock, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineNetlinkListener() {
|
||||
defer func() {
|
||||
unix.Close(tun.netlinkSock)
|
||||
tun.hackListenerClosed.Lock()
|
||||
close(tun.events)
|
||||
tun.netlinkCancel.Close()
|
||||
}()
|
||||
|
||||
for msg := make([]byte, 1<<16); ; {
|
||||
var err error
|
||||
var msgn int
|
||||
for {
|
||||
msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0)
|
||||
if err == nil || !rwcancel.RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
if !tun.netlinkCancel.ReadyRead() {
|
||||
tun.errors <- fmt.Errorf("netlink socket closed: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("failed to receive netlink message: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-tun.statusListenersShutdown:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
wasEverUp := false
|
||||
for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; {
|
||||
|
||||
hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0]))
|
||||
|
||||
if int(hdr.Len) > len(remain) {
|
||||
break
|
||||
}
|
||||
|
||||
switch hdr.Type {
|
||||
case unix.NLMSG_DONE:
|
||||
remain = []byte{}
|
||||
|
||||
case unix.RTM_NEWLINK:
|
||||
info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr]))
|
||||
remain = remain[hdr.Len:]
|
||||
|
||||
if info.Index != tun.index {
|
||||
// not our interface
|
||||
continue
|
||||
}
|
||||
|
||||
if info.Flags&unix.IFF_RUNNING != 0 {
|
||||
tun.events <- EventUp
|
||||
wasEverUp = true
|
||||
}
|
||||
|
||||
if info.Flags&unix.IFF_RUNNING == 0 {
|
||||
// Don't emit EventDown before we've ever emitted EventUp.
|
||||
// This avoids a startup race with HackListener, which
|
||||
// might detect Up before we have finished reporting Down.
|
||||
if wasEverUp {
|
||||
tun.events <- EventDown
|
||||
}
|
||||
}
|
||||
|
||||
tun.events <- EventMTUUpdate
|
||||
|
||||
default:
|
||||
remain = remain[hdr.Len:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIFIndex(name string) (int32, error) {
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
copy(ifr[:], name)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFINDEX),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return 0, errno
|
||||
}
|
||||
|
||||
return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// open datagram socket
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
var ifr [ifReqSize]byte
|
||||
copy(ifr[:], name)
|
||||
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCSIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU of TUN device: %w", errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// open datagram socket
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
copy(ifr[:], name)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU of TUN device: %w", errno)
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
tun.nameOnce.Do(tun.initNameCache)
|
||||
return tun.nameCache, tun.nameErr
|
||||
}
|
||||
|
||||
func (tun *NativeTun) initNameCache() {
|
||||
tun.nameCache, tun.nameErr = tun.nameSlow()
|
||||
}
|
||||
|
||||
func (tun *NativeTun) nameSlow() (string, error) {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var ifr [ifReqSize]byte
|
||||
var errno syscall.Errno
|
||||
err = sysconn.Control(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd,
|
||||
uintptr(unix.TUNGETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get name of TUN device: %w", err)
|
||||
}
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("failed to get name of TUN device: %w", errno)
|
||||
}
|
||||
name := ifr[:]
|
||||
if i := bytes.IndexByte(name, 0); i != -1 {
|
||||
name = name[:i]
|
||||
}
|
||||
return string(name), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buf []byte, offset int) (int, error) {
|
||||
if tun.nopi {
|
||||
buf = buf[offset:]
|
||||
} else {
|
||||
// reserve space for header
|
||||
buf = buf[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
buf[0] = 0x00
|
||||
buf[1] = 0x00
|
||||
if buf[4]>>4 == ipv6.Version {
|
||||
buf[2] = 0x86
|
||||
buf[3] = 0xdd
|
||||
} else {
|
||||
buf[2] = 0x08
|
||||
buf[3] = 0x00
|
||||
}
|
||||
}
|
||||
|
||||
n, err := tun.tunFile.Write(buf)
|
||||
if errors.Is(err, syscall.EBADFD) {
|
||||
err = os.ErrClosed
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
// TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buf []byte, offset int) (n int, err error) {
|
||||
select {
|
||||
case err = <-tun.errors:
|
||||
default:
|
||||
if tun.nopi {
|
||||
n, err = tun.tunFile.Read(buf[offset:])
|
||||
} else {
|
||||
buff := buf[offset-4:]
|
||||
n, err = tun.tunFile.Read(buff[:])
|
||||
if errors.Is(err, syscall.EBADFD) {
|
||||
err = os.ErrClosed
|
||||
}
|
||||
if n < 4 {
|
||||
n = 0
|
||||
} else {
|
||||
n -= 4
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1, err2 error
|
||||
tun.closeOnce.Do(func() {
|
||||
if tun.statusListenersShutdown != nil {
|
||||
close(tun.statusListenersShutdown)
|
||||
if tun.netlinkCancel != nil {
|
||||
err1 = tun.netlinkCancel.Cancel()
|
||||
}
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
err2 = tun.tunFile.Close()
|
||||
})
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (Device, error) {
|
||||
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("CreateTUN(%q) failed; %s does not exist", name, cloneDevicePath)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
|
||||
nameBytes := []byte(name)
|
||||
if len(nameBytes) >= unix.IFNAMSIZ {
|
||||
return nil, fmt.Errorf("interface name too long: %w", unix.ENAMETOOLONG)
|
||||
}
|
||||
copy(ifr[:], nameBytes)
|
||||
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(nfd),
|
||||
uintptr(unix.TUNSETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
|
||||
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(fd, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 5),
|
||||
errors: make(chan error, 5),
|
||||
statusListenersShutdown: make(chan struct{}),
|
||||
nopi: false,
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start event listener
|
||||
|
||||
tun.index, err = getIFIndex(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.netlinkSock, err = createNetlinkSocket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock)
|
||||
if err != nil {
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.hackListenerClosed.Lock()
|
||||
go tun.routineNetlinkListener()
|
||||
go tun.routineHackListener() // cross namespace
|
||||
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) {
|
||||
err := unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 5),
|
||||
errors: make(chan error, 5),
|
||||
nopi: true,
|
||||
}
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return tun, name, nil
|
||||
}
|
@ -1,334 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Structure for iface mtu get/set ioctls
|
||||
type ifreq_mtu struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU uint32
|
||||
Pad0 [12]byte
|
||||
}
|
||||
|
||||
const _TUNSIFMODE = 0x8004745d
|
||||
|
||||
type NativeTun struct {
|
||||
name string
|
||||
tunFile *os.File
|
||||
events chan Event
|
||||
errors chan error
|
||||
routeSocket int
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
check := func() bool {
|
||||
iface, err := net.InterfaceByIndex(tunIfindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return true
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- EventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- EventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- EventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
return false
|
||||
}
|
||||
|
||||
if check() {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 8 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[6 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
if check() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (Device, error) {
|
||||
ifIndex := -1
|
||||
if name != "tun" {
|
||||
_, err := fmt.Sscanf(name, "tun%d", &ifIndex)
|
||||
if err != nil || ifIndex < 0 {
|
||||
return nil, fmt.Errorf("Interface name must be tun[0-9]*")
|
||||
}
|
||||
}
|
||||
|
||||
var tunfile *os.File
|
||||
var err error
|
||||
|
||||
if ifIndex != -1 {
|
||||
tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0)
|
||||
} else {
|
||||
for ifIndex = 0; ifIndex < 256; ifIndex++ {
|
||||
tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0)
|
||||
if err == nil || !errors.Is(err, syscall.EBUSY) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun, err := CreateTUNFromFile(tunfile, mtu)
|
||||
|
||||
if err == nil && name == "tun" {
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
os.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
}
|
||||
|
||||
return tun, err
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
currentMTU, err := tun.MTU()
|
||||
if err != nil || currentMTU != mtu {
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
gostat, err := tun.tunFile.Stat()
|
||||
if err != nil {
|
||||
tun.name = ""
|
||||
return "", err
|
||||
}
|
||||
stat := gostat.Sys().(*syscall.Stat_t)
|
||||
tun.name = fmt.Sprintf("tun%d", stat.Rdev%256)
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
buff[2] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[3] = unix.AF_INET6
|
||||
} else {
|
||||
buff[3] = unix.AF_INET
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
// TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1, err2 error
|
||||
tun.closeOnce.Do(func() {
|
||||
err1 = tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
})
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = uint32(n)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCSIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
_ "unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/wintun"
|
||||
)
|
||||
|
||||
const (
|
||||
rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond)
|
||||
spinloopRateThreshold = 800000000 / 8 // 800mbps
|
||||
spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s
|
||||
)
|
||||
|
||||
type rateJuggler struct {
|
||||
current uint64
|
||||
nextByteCount uint64
|
||||
nextStartTime int64
|
||||
changing int32
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
wt *wintun.Adapter
|
||||
handle windows.Handle
|
||||
rate rateJuggler
|
||||
session wintun.Session
|
||||
readWait windows.Handle
|
||||
events chan Event
|
||||
running sync.WaitGroup
|
||||
closeOnce sync.Once
|
||||
close int32
|
||||
forcedMTU int
|
||||
}
|
||||
|
||||
var WintunPool, _ = wintun.MakePool("WireGuard")
|
||||
var WintunStaticRequestedGUID *windows.GUID
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
func procyield(cycles uint32)
|
||||
|
||||
//go:linkname nanotime runtime.nanotime
|
||||
func nanotime() int64
|
||||
|
||||
//
|
||||
// CreateTUN creates a Wintun interface with the given name. Should a Wintun
|
||||
// interface with the same name exist, it is reused.
|
||||
//
|
||||
func CreateTUN(ifname string, mtu int) (Device, error) {
|
||||
return CreateTUNWithRequestedGUID(ifname, WintunStaticRequestedGUID, mtu)
|
||||
}
|
||||
|
||||
//
|
||||
// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and
|
||||
// a requested GUID. Should a Wintun interface with the same name exist, it is reused.
|
||||
//
|
||||
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int) (Device, error) {
|
||||
var err error
|
||||
var wt *wintun.Adapter
|
||||
|
||||
// Does an interface with this name already exist?
|
||||
wt, err = WintunPool.OpenAdapter(ifname)
|
||||
if err == nil {
|
||||
// If so, we delete it, in case it has weird residual configuration.
|
||||
_, err = wt.Delete(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error deleting already existing interface: %w", err)
|
||||
}
|
||||
}
|
||||
wt, rebootRequired, err := WintunPool.CreateAdapter(ifname, requestedGUID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating interface: %w", err)
|
||||
}
|
||||
if rebootRequired {
|
||||
log.Println("Windows indicated a reboot is required.")
|
||||
}
|
||||
|
||||
forcedMTU := 1420
|
||||
if mtu > 0 {
|
||||
forcedMTU = mtu
|
||||
}
|
||||
|
||||
tun := &NativeTun{
|
||||
wt: wt,
|
||||
handle: windows.InvalidHandle,
|
||||
events: make(chan Event, 10),
|
||||
forcedMTU: forcedMTU,
|
||||
}
|
||||
|
||||
tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB
|
||||
if err != nil {
|
||||
tun.wt.Delete(false)
|
||||
close(tun.events)
|
||||
return nil, fmt.Errorf("Error starting session: %w", err)
|
||||
}
|
||||
tun.readWait = tun.session.ReadWaitEvent()
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return "", os.ErrClosed
|
||||
}
|
||||
return tun.wt.Name()
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err error
|
||||
tun.closeOnce.Do(func() {
|
||||
atomic.StoreInt32(&tun.close, 1)
|
||||
windows.SetEvent(tun.readWait)
|
||||
tun.running.Wait()
|
||||
tun.session.End()
|
||||
if tun.wt != nil {
|
||||
_, err = tun.wt.Delete(false)
|
||||
}
|
||||
close(tun.events)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
return tun.forcedMTU, nil
|
||||
}
|
||||
|
||||
// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
|
||||
func (tun *NativeTun) ForceMTU(mtu int) {
|
||||
update := tun.forcedMTU != mtu
|
||||
tun.forcedMTU = mtu
|
||||
if update {
|
||||
tun.events <- EventMTUUpdate
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking.
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
retry:
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
start := nanotime()
|
||||
shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2
|
||||
for {
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
packet, err := tun.session.ReceivePacket()
|
||||
switch err {
|
||||
case nil:
|
||||
packetSize := len(packet)
|
||||
copy(buff[offset:], packet)
|
||||
tun.session.ReleaseReceivePacket(packet)
|
||||
tun.rate.update(uint64(packetSize))
|
||||
return packetSize, nil
|
||||
case windows.ERROR_NO_MORE_ITEMS:
|
||||
if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration {
|
||||
windows.WaitForSingleObject(tun.readWait, windows.INFINITE)
|
||||
goto retry
|
||||
}
|
||||
procyield(1)
|
||||
continue
|
||||
case windows.ERROR_HANDLE_EOF:
|
||||
return 0, os.ErrClosed
|
||||
case windows.ERROR_INVALID_DATA:
|
||||
return 0, errors.New("Send ring corrupt")
|
||||
}
|
||||
return 0, fmt.Errorf("Read failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
|
||||
packetSize := len(buff) - offset
|
||||
tun.rate.update(uint64(packetSize))
|
||||
|
||||
packet, err := tun.session.AllocateSendPacket(packetSize)
|
||||
if err == nil {
|
||||
copy(packet, buff[offset:])
|
||||
tun.session.SendPacket(packet)
|
||||
return packetSize, nil
|
||||
}
|
||||
switch err {
|
||||
case windows.ERROR_HANDLE_EOF:
|
||||
return 0, os.ErrClosed
|
||||
case windows.ERROR_BUFFER_OVERFLOW:
|
||||
return 0, nil // Dropping when ring is full.
|
||||
}
|
||||
return 0, fmt.Errorf("Write failed: %w", err)
|
||||
}
|
||||
|
||||
// LUID returns Windows interface instance ID.
|
||||
func (tun *NativeTun) LUID() uint64 {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0
|
||||
}
|
||||
return tun.wt.LUID()
|
||||
}
|
||||
|
||||
// RunningVersion returns the running version of the Wintun driver.
|
||||
func (tun *NativeTun) RunningVersion() (version uint32, err error) {
|
||||
return wintun.RunningVersion()
|
||||
}
|
||||
|
||||
func (rate *rateJuggler) update(packetLen uint64) {
|
||||
now := nanotime()
|
||||
total := atomic.AddUint64(&rate.nextByteCount, packetLen)
|
||||
period := uint64(now - atomic.LoadInt64(&rate.nextStartTime))
|
||||
if period >= rateMeasurementGranularity {
|
||||
if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) {
|
||||
return
|
||||
}
|
||||
atomic.StoreInt64(&rate.nextStartTime, now)
|
||||
atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period)
|
||||
atomic.StoreUint64(&rate.nextByteCount, 0)
|
||||
atomic.StoreInt32(&rate.changing, 0)
|
||||
}
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tuntest
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun"
|
||||
)
|
||||
|
||||
func Ping(dst, src net.IP) []byte {
|
||||
localPort := uint16(1337)
|
||||
seq := uint16(0)
|
||||
|
||||
payload := make([]byte, 4)
|
||||
binary.BigEndian.PutUint16(payload[0:], localPort)
|
||||
binary.BigEndian.PutUint16(payload[2:], seq)
|
||||
|
||||
return genICMPv4(payload, dst, src)
|
||||
}
|
||||
|
||||
// Checksum is the "internet checksum" from https://tools.ietf.org/html/rfc1071.
|
||||
func checksum(buf []byte, initial uint16) uint16 {
|
||||
v := uint32(initial)
|
||||
for i := 0; i < len(buf)-1; i += 2 {
|
||||
v += uint32(binary.BigEndian.Uint16(buf[i:]))
|
||||
}
|
||||
if len(buf)%2 == 1 {
|
||||
v += uint32(buf[len(buf)-1]) << 8
|
||||
}
|
||||
for v > 0xffff {
|
||||
v = (v >> 16) + (v & 0xffff)
|
||||
}
|
||||
return ^uint16(v)
|
||||
}
|
||||
|
||||
func genICMPv4(payload []byte, dst, src net.IP) []byte {
|
||||
const (
|
||||
icmpv4ProtocolNumber = 1
|
||||
icmpv4Echo = 8
|
||||
icmpv4ChecksumOffset = 2
|
||||
icmpv4Size = 8
|
||||
ipv4Size = 20
|
||||
ipv4TotalLenOffset = 2
|
||||
ipv4ChecksumOffset = 10
|
||||
ttl = 65
|
||||
headerSize = ipv4Size + icmpv4Size
|
||||
)
|
||||
|
||||
pkt := make([]byte, headerSize+len(payload))
|
||||
|
||||
ip := pkt[0:ipv4Size]
|
||||
icmpv4 := pkt[ipv4Size : ipv4Size+icmpv4Size]
|
||||
|
||||
// https://tools.ietf.org/html/rfc792
|
||||
icmpv4[0] = icmpv4Echo // type
|
||||
icmpv4[1] = 0 // code
|
||||
chksum := ^checksum(icmpv4, checksum(payload, 0))
|
||||
binary.BigEndian.PutUint16(icmpv4[icmpv4ChecksumOffset:], chksum)
|
||||
|
||||
// https://tools.ietf.org/html/rfc760 section 3.1
|
||||
length := uint16(len(pkt))
|
||||
ip[0] = (4 << 4) | (ipv4Size / 4)
|
||||
binary.BigEndian.PutUint16(ip[ipv4TotalLenOffset:], length)
|
||||
ip[8] = ttl
|
||||
ip[9] = icmpv4ProtocolNumber
|
||||
copy(ip[12:], src.To4())
|
||||
copy(ip[16:], dst.To4())
|
||||
chksum = ^checksum(ip[:], 0)
|
||||
binary.BigEndian.PutUint16(ip[ipv4ChecksumOffset:], chksum)
|
||||
|
||||
copy(pkt[headerSize:], payload)
|
||||
return pkt
|
||||
}
|
||||
|
||||
type ChannelTUN struct {
|
||||
Inbound chan []byte // incoming packets, closed on TUN close
|
||||
Outbound chan []byte // outbound packets, blocks forever on TUN close
|
||||
|
||||
closed chan struct{}
|
||||
events chan tun.Event
|
||||
tun chTun
|
||||
}
|
||||
|
||||
func NewChannelTUN() *ChannelTUN {
|
||||
c := &ChannelTUN{
|
||||
Inbound: make(chan []byte),
|
||||
Outbound: make(chan []byte),
|
||||
closed: make(chan struct{}),
|
||||
events: make(chan tun.Event, 1),
|
||||
}
|
||||
c.tun.c = c
|
||||
c.events <- tun.EventUp
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ChannelTUN) TUN() tun.Device {
|
||||
return &c.tun
|
||||
}
|
||||
|
||||
type chTun struct {
|
||||
c *ChannelTUN
|
||||
}
|
||||
|
||||
func (t *chTun) File() *os.File { return nil }
|
||||
|
||||
func (t *chTun) Read(data []byte, offset int) (int, error) {
|
||||
select {
|
||||
case <-t.c.closed:
|
||||
return 0, os.ErrClosed
|
||||
case msg := <-t.c.Outbound:
|
||||
return copy(data[offset:], msg), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Write is called by the wireguard device to deliver a packet for routing.
|
||||
func (t *chTun) Write(data []byte, offset int) (int, error) {
|
||||
if offset == -1 {
|
||||
close(t.c.closed)
|
||||
close(t.c.events)
|
||||
return 0, io.EOF
|
||||
}
|
||||
msg := make([]byte, len(data)-offset)
|
||||
copy(msg, data[offset:])
|
||||
select {
|
||||
case <-t.c.closed:
|
||||
return 0, os.ErrClosed
|
||||
case t.c.Inbound <- msg:
|
||||
return len(data) - offset, nil
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
func (t *chTun) Flush() error { return nil }
|
||||
func (t *chTun) MTU() (int, error) { return DefaultMTU, nil }
|
||||
func (t *chTun) Name() (string, error) { return "loopbackTun1", nil }
|
||||
func (t *chTun) Events() chan tun.Event { return t.c.events }
|
||||
func (t *chTun) Close() error {
|
||||
t.Write(nil, -1)
|
||||
return nil
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// +build !load_wintun_from_rsrc
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type lazyDLL struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
module windows.Handle
|
||||
onLoad func(d *lazyDLL)
|
||||
}
|
||||
|
||||
func (d *lazyDLL) Load() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.module != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
)
|
||||
module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load library: %w", err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||
if d.onLoad != nil {
|
||||
d.onLoad(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||
return windows.GetProcAddress(p.dll.module, p.Name)
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// +build load_wintun_from_rsrc
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/KusakabeSi/EtherGuardVPN/tun/wintun/memmod"
|
||||
)
|
||||
|
||||
type lazyDLL struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
module *memmod.Module
|
||||
onLoad func(d *lazyDLL)
|
||||
}
|
||||
|
||||
func (d *lazyDLL) Load() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.module != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
const ourModule windows.Handle = 0
|
||||
resInfo, err := windows.FindResource(ourModule, d.Name, windows.RT_RCDATA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %w", d.Name, err)
|
||||
}
|
||||
data, err := windows.LoadResourceData(ourModule, resInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load resource: %w", err)
|
||||
}
|
||||
module, err := memmod.LoadLibrary(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load library: %w", err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||
if d.onLoad != nil {
|
||||
d.onLoad(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||
return p.dll.module.ProcAddressByName(p.Name)
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
||||
return &lazyDLL{Name: name, onLoad: onLoad}
|
||||
}
|
||||
|
||||
func (d *lazyDLL) NewProc(name string) *lazyProc {
|
||||
return &lazyProc{dll: d, Name: name}
|
||||
}
|
||||
|
||||
type lazyProc struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
dll *lazyDLL
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
func (p *lazyProc) Find() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
|
||||
return nil
|
||||
}
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.addr != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := p.dll.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err)
|
||||
}
|
||||
addr, err := p.nameToAddr()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting %v address: %w", p.Name, err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) Addr() uintptr {
|
||||
err := p.Find()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p.addr
|
||||
}
|
@ -1,635 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type addressList struct {
|
||||
next *addressList
|
||||
address uintptr
|
||||
}
|
||||
|
||||
func (head *addressList) free() {
|
||||
for node := head; node != nil; node = node.next {
|
||||
windows.VirtualFree(node.address, 0, windows.MEM_RELEASE)
|
||||
}
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
headers *IMAGE_NT_HEADERS
|
||||
codeBase uintptr
|
||||
modules []windows.Handle
|
||||
initialized bool
|
||||
isDLL bool
|
||||
isRelocated bool
|
||||
nameExports map[string]uint16
|
||||
entry uintptr
|
||||
blockedMemory *addressList
|
||||
}
|
||||
|
||||
func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY {
|
||||
return &module.headers.OptionalHeader.DataDirectory[idx]
|
||||
}
|
||||
|
||||
func (module *Module) copySections(address uintptr, size uintptr, oldHeaders *IMAGE_NT_HEADERS) error {
|
||||
sections := module.headers.Sections()
|
||||
for i := range sections {
|
||||
if sections[i].SizeOfRawData == 0 {
|
||||
// Section doesn't contain data in the dll itself, but may define uninitialized data.
|
||||
sectionSize := oldHeaders.OptionalHeader.SectionAlignment
|
||||
if sectionSize == 0 {
|
||||
continue
|
||||
}
|
||||
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
uintptr(sectionSize),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating section: %w", err)
|
||||
}
|
||||
|
||||
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||
dest = module.codeBase + uintptr(sections[i].VirtualAddress)
|
||||
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||
var dst []byte
|
||||
unsafeSlice(unsafe.Pointer(&dst), a2p(dest), int(sectionSize))
|
||||
for j := range dst {
|
||||
dst[j] = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) {
|
||||
return errors.New("Incomplete section")
|
||||
}
|
||||
|
||||
// Commit memory block and copy data from dll.
|
||||
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
uintptr(sections[i].SizeOfRawData),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||
}
|
||||
|
||||
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||
memcpy(
|
||||
module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
address+uintptr(sections[i].PointerToRawData),
|
||||
uintptr(sections[i].SizeOfRawData))
|
||||
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr {
|
||||
size := section.SizeOfRawData
|
||||
if size != 0 {
|
||||
return uintptr(size)
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 {
|
||||
return uintptr(module.headers.OptionalHeader.SizeOfInitializedData)
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 {
|
||||
return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type sectionFinalizeData struct {
|
||||
address uintptr
|
||||
alignedAddress uintptr
|
||||
size uintptr
|
||||
characteristics uint32
|
||||
last bool
|
||||
}
|
||||
|
||||
func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error {
|
||||
if sectionData.size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 {
|
||||
// Section is not needed any more and can safely be freed.
|
||||
if sectionData.address == sectionData.alignedAddress &&
|
||||
(sectionData.last ||
|
||||
(sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) {
|
||||
// Only allowed to decommit whole pages.
|
||||
windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// determine protection flags based on characteristics
|
||||
var ProtectionFlags = [8]uint32{
|
||||
windows.PAGE_NOACCESS, // not writeable, not readable, not executable
|
||||
windows.PAGE_EXECUTE, // not writeable, not readable, executable
|
||||
windows.PAGE_READONLY, // not writeable, readable, not executable
|
||||
windows.PAGE_EXECUTE_READ, // not writeable, readable, executable
|
||||
windows.PAGE_WRITECOPY, // writeable, not readable, not executable
|
||||
windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable
|
||||
windows.PAGE_READWRITE, // writeable, readable, not executable
|
||||
windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable
|
||||
}
|
||||
protect := ProtectionFlags[sectionData.characteristics>>29]
|
||||
if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 {
|
||||
protect |= windows.PAGE_NOCACHE
|
||||
}
|
||||
|
||||
// Change memory access flags.
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error protecting memory page: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rtlAddFunctionTable = windows.NewLazySystemDLL("ntdll.dll").NewProc("RtlAddFunctionTable")
|
||||
|
||||
func (module *Module) registerExceptionHandlers() {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXCEPTION)
|
||||
if directory.Size == 0 || directory.VirtualAddress == 0 {
|
||||
return
|
||||
}
|
||||
rtlAddFunctionTable.Call(module.codeBase+uintptr(directory.VirtualAddress), uintptr(directory.Size)/unsafe.Sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY{}), module.codeBase)
|
||||
}
|
||||
|
||||
func (module *Module) finalizeSections() error {
|
||||
sections := module.headers.Sections()
|
||||
imageOffset := module.headers.OptionalHeader.imageOffset()
|
||||
sectionData := sectionFinalizeData{}
|
||||
sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset
|
||||
sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||
sectionData.size = module.realSectionSize(§ions[0])
|
||||
sections[0].SetVirtualSize(uint32(sectionData.size))
|
||||
sectionData.characteristics = sections[0].Characteristics
|
||||
|
||||
// Loop through all sections and change access flags.
|
||||
for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ {
|
||||
sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset
|
||||
alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||
sectionSize := module.realSectionSize(§ions[i])
|
||||
sections[i].SetVirtualSize(uint32(sectionSize))
|
||||
// Combine access flags of all sections that share a page.
|
||||
// TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
|
||||
if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress {
|
||||
// Section shares page with previous.
|
||||
if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 {
|
||||
sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE
|
||||
} else {
|
||||
sectionData.characteristics |= sections[i].Characteristics
|
||||
}
|
||||
sectionData.size = sectionAddress + sectionSize - sectionData.address
|
||||
continue
|
||||
}
|
||||
|
||||
err := module.finalizeSection(§ionData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finalizing section: %w", err)
|
||||
}
|
||||
sectionData.address = sectionAddress
|
||||
sectionData.alignedAddress = alignedAddress
|
||||
sectionData.size = sectionSize
|
||||
sectionData.characteristics = sections[i].Characteristics
|
||||
}
|
||||
sectionData.last = true
|
||||
err := module.finalizeSection(§ionData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finalizing section: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) executeTLS() {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS)
|
||||
if directory.VirtualAddress == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
callback := tls.AddressOfCallbacks
|
||||
if callback != 0 {
|
||||
for {
|
||||
f := *(*uintptr)(a2p(callback))
|
||||
if f == 0 {
|
||||
break
|
||||
}
|
||||
syscall.Syscall(f, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), uintptr(0))
|
||||
callback += unsafe.Sizeof(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||
if directory.Size == 0 {
|
||||
return delta == 0, nil
|
||||
}
|
||||
|
||||
relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
for relocationHdr.VirtualAddress > 0 {
|
||||
dest := module.codeBase + uintptr(relocationHdr.VirtualAddress)
|
||||
|
||||
var relInfos []uint16
|
||||
unsafeSlice(
|
||||
unsafe.Pointer(&relInfos),
|
||||
a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr)),
|
||||
int((uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(relInfos[0])))
|
||||
for _, relInfo := range relInfos {
|
||||
// The upper 4 bits define the type of relocation.
|
||||
relType := relInfo >> 12
|
||||
// The lower 12 bits define the offset.
|
||||
relOffset := uintptr(relInfo & 0xfff)
|
||||
|
||||
switch relType {
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
// Skip relocation.
|
||||
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
*(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff)
|
||||
break
|
||||
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
*(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16)
|
||||
break
|
||||
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
*(*uint32)(a2p(dest + relOffset)) += uint32(delta)
|
||||
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
*(*uint64)(a2p(dest + relOffset)) += uint64(delta)
|
||||
|
||||
case IMAGE_REL_BASED_THUMB_MOV32:
|
||||
inst := *(*uint32)(a2p(dest + relOffset))
|
||||
imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||
if (inst & 0x8000fbf0) != 0x0000f240 {
|
||||
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst)
|
||||
}
|
||||
imm16 += uint32(delta) & 0xffff
|
||||
hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16)
|
||||
*(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) +
|
||||
((imm16 >> 12) & 0x000f) +
|
||||
((imm16 << 20) & 0x70000000) +
|
||||
((imm16 << 16) & 0xff0000)
|
||||
if hiDelta != 0 {
|
||||
inst = *(*uint32)(a2p(dest + relOffset + 4))
|
||||
imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||
if (inst & 0x8000fbf0) != 0x0000f2c0 {
|
||||
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst)
|
||||
}
|
||||
imm16 += hiDelta
|
||||
if imm16 > 0xffff {
|
||||
return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16)
|
||||
}
|
||||
*(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) +
|
||||
((imm16 >> 1) & 0x0400) +
|
||||
((imm16 >> 12) & 0x000f) +
|
||||
((imm16 << 20) & 0x70000000) +
|
||||
((imm16 << 16) & 0xff0000)
|
||||
}
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("Unsupported relocation: %v", relType)
|
||||
}
|
||||
}
|
||||
|
||||
// Advance to next relocation block.
|
||||
relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock)))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (module *Module) buildImportTable() error {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
if directory.Size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
module.modules = make([]windows.Handle, 0, 16)
|
||||
importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
for importDesc.Name != 0 {
|
||||
handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading module: %w", err)
|
||||
}
|
||||
var thunkRef, funcRef *uintptr
|
||||
if importDesc.OriginalFirstThunk() != 0 {
|
||||
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk())))
|
||||
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
} else {
|
||||
// No hint table.
|
||||
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
}
|
||||
for *thunkRef != 0 {
|
||||
if IMAGE_SNAP_BY_ORDINAL(*thunkRef) {
|
||||
*funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef))
|
||||
} else {
|
||||
thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef))
|
||||
*funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0]))
|
||||
}
|
||||
if err != nil {
|
||||
windows.FreeLibrary(handle)
|
||||
return fmt.Errorf("Error getting function address: %w", err)
|
||||
}
|
||||
thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef)))
|
||||
funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef)))
|
||||
}
|
||||
module.modules = append(module.modules, handle)
|
||||
importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) buildNameExports() error {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 {
|
||||
return errors.New("No functions exported")
|
||||
}
|
||||
if exports.NumberOfNames == 0 {
|
||||
return errors.New("No functions exported by name")
|
||||
}
|
||||
var nameRefs []uint32
|
||||
unsafeSlice(unsafe.Pointer(&nameRefs), a2p(module.codeBase+uintptr(exports.AddressOfNames)), int(exports.NumberOfNames))
|
||||
var ordinals []uint16
|
||||
unsafeSlice(unsafe.Pointer(&ordinals), a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals)), int(exports.NumberOfNames))
|
||||
module.nameExports = make(map[string]uint16)
|
||||
for i := range nameRefs {
|
||||
nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i]))))
|
||||
module.nameExports[nameArray] = ordinals[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLibrary loads module image to memory.
|
||||
func LoadLibrary(data []byte) (module *Module, err error) {
|
||||
addr := uintptr(unsafe.Pointer(&data[0]))
|
||||
size := uintptr(len(data))
|
||||
if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) {
|
||||
return nil, errors.New("Incomplete IMAGE_DOS_HEADER")
|
||||
}
|
||||
dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr))
|
||||
if dosHeader.E_magic != IMAGE_DOS_SIGNATURE {
|
||||
return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE)
|
||||
}
|
||||
if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) {
|
||||
return nil, errors.New("Incomplete IMAGE_NT_HEADERS")
|
||||
}
|
||||
oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew)))
|
||||
if oldHeader.Signature != IMAGE_NT_SIGNATURE {
|
||||
return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE)
|
||||
}
|
||||
if oldHeader.FileHeader.Machine != imageFileProcess {
|
||||
return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess)
|
||||
}
|
||||
if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 {
|
||||
return nil, errors.New("Unaligned section")
|
||||
}
|
||||
lastSectionEnd := uintptr(0)
|
||||
sections := oldHeader.Sections()
|
||||
optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment
|
||||
for i := range sections {
|
||||
var endOfSection uintptr
|
||||
if sections[i].SizeOfRawData == 0 {
|
||||
// Section without data in the DLL
|
||||
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize)
|
||||
} else {
|
||||
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData)
|
||||
}
|
||||
if endOfSection > lastSectionEnd {
|
||||
lastSectionEnd = endOfSection
|
||||
}
|
||||
}
|
||||
alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment))
|
||||
if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) {
|
||||
return nil, errors.New("Section is not page-aligned")
|
||||
}
|
||||
|
||||
module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
module.Free()
|
||||
module = nil
|
||||
}
|
||||
}()
|
||||
|
||||
// Reserve memory for image of library.
|
||||
// TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't.
|
||||
module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
// Try to allocate memory at arbitrary position.
|
||||
module.codeBase, err = windows.VirtualAlloc(0,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error allocating code: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = module.check4GBBoundaries(alignedImageSize)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error reallocating code: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) {
|
||||
err = errors.New("Incomplete headers")
|
||||
return
|
||||
}
|
||||
// Commit memory for headers.
|
||||
headers, err := windows.VirtualAlloc(module.codeBase,
|
||||
uintptr(oldHeader.OptionalHeader.SizeOfHeaders),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error allocating headers: %w", err)
|
||||
return
|
||||
}
|
||||
// Copy PE header to code.
|
||||
memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders))
|
||||
module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew)))
|
||||
|
||||
// Update position.
|
||||
module.headers.OptionalHeader.ImageBase = module.codeBase
|
||||
|
||||
// Copy sections from DLL file block to new memory location.
|
||||
err = module.copySections(addr, size, oldHeader)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error copying sections: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust base address of imported data.
|
||||
locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase
|
||||
if locationDelta != 0 {
|
||||
module.isRelocated, err = module.performBaseRelocation(locationDelta)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error relocating module: %w", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
module.isRelocated = true
|
||||
}
|
||||
|
||||
// Load required dlls and adjust function table of imports.
|
||||
err = module.buildImportTable()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error building import table: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Mark memory pages depending on section headers and release sections that are marked as "discardable".
|
||||
err = module.finalizeSections()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error finalizing sections: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Register exception tables, if they exist.
|
||||
module.registerExceptionHandlers()
|
||||
|
||||
// TLS callbacks are executed BEFORE the main loading.
|
||||
module.executeTLS()
|
||||
|
||||
// Get entry point of loaded module.
|
||||
if module.headers.OptionalHeader.AddressOfEntryPoint != 0 {
|
||||
module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint)
|
||||
if module.isDLL {
|
||||
// Notify library about attaching to process.
|
||||
r0, _, _ := syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), 0)
|
||||
successful := r0 != 0
|
||||
if !successful {
|
||||
err = windows.ERROR_DLL_INIT_FAILED
|
||||
return
|
||||
}
|
||||
module.initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
module.buildNameExports()
|
||||
return
|
||||
}
|
||||
|
||||
// Free releases module resources and unloads it.
|
||||
func (module *Module) Free() {
|
||||
if module.initialized {
|
||||
// Notify library about detaching from process.
|
||||
syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_DETACH), 0)
|
||||
module.initialized = false
|
||||
}
|
||||
if module.modules != nil {
|
||||
// Free previously opened libraries.
|
||||
for _, handle := range module.modules {
|
||||
windows.FreeLibrary(handle)
|
||||
}
|
||||
module.modules = nil
|
||||
}
|
||||
if module.codeBase != 0 {
|
||||
windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE)
|
||||
module.codeBase = 0
|
||||
}
|
||||
if module.blockedMemory != nil {
|
||||
module.blockedMemory.free()
|
||||
module.blockedMemory = nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProcAddressByName returns function address by exported name.
|
||||
func (module *Module) ProcAddressByName(name string) (uintptr, error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return 0, errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if module.nameExports == nil {
|
||||
return 0, errors.New("No functions exported by name")
|
||||
}
|
||||
if idx, ok := module.nameExports[name]; ok {
|
||||
if uint32(idx) > exports.NumberOfFunctions {
|
||||
return 0, errors.New("Ordinal number too high")
|
||||
}
|
||||
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||
}
|
||||
return 0, errors.New("Function not found by name")
|
||||
}
|
||||
|
||||
// ProcAddressByOrdinal returns function address by exported ordinal.
|
||||
func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return 0, errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if uint32(ordinal) < exports.Base {
|
||||
return 0, errors.New("Ordinal number too low")
|
||||
}
|
||||
idx := ordinal - uint16(exports.Base)
|
||||
if uint32(idx) > exports.NumberOfFunctions {
|
||||
return 0, errors.New("Ordinal number too high")
|
||||
}
|
||||
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||
}
|
||||
|
||||
func alignDown(value, alignment uintptr) uintptr {
|
||||
return value & ^(alignment - 1)
|
||||
}
|
||||
|
||||
func alignUp(value, alignment uintptr) uintptr {
|
||||
return (value + alignment - 1) & ^(alignment - 1)
|
||||
}
|
||||
|
||||
func a2p(addr uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(addr)
|
||||
}
|
||||
|
||||
func memcpy(dst, src, size uintptr) {
|
||||
var d, s []byte
|
||||
unsafeSlice(unsafe.Pointer(&d), a2p(dst), int(size))
|
||||
unsafeSlice(unsafe.Pointer(&s), a2p(src), int(size))
|
||||
copy(d, s)
|
||||
}
|
||||
|
||||
// unsafeSlice updates the slice slicePtr to be a slice
|
||||
// referencing the provided data with its length & capacity set to
|
||||
// lenCap.
|
||||
//
|
||||
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||
// update callers to use unsafe.Slice instead of this.
|
||||
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
h := (*sliceHeader)(slicePtr)
|
||||
h.Data = data
|
||||
h.Len = lenCap
|
||||
h.Cap = lenCap
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// +build windows,386 windows,arm
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||
return
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_I386
|
@ -1,36 +0,0 @@
|
||||
// +build windows,amd64 windows,arm64
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||
return uintptr(opthdr.ImageBase & 0xffffffff00000000)
|
||||
}
|
||||
|
||||
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||
for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) {
|
||||
node := &addressList{
|
||||
next: module.blockedMemory,
|
||||
address: module.codeBase,
|
||||
}
|
||||
module.blockedMemory = node
|
||||
module.codeBase, err = windows.VirtualAlloc(0,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_AMD64
|
@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT
|
@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_ARM64
|
@ -1,398 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
IMAGE_DOS_SIGNATURE = 0x5A4D // MZ
|
||||
IMAGE_OS2_SIGNATURE = 0x454E // NE
|
||||
IMAGE_OS2_SIGNATURE_LE = 0x454C // LE
|
||||
IMAGE_VXD_SIGNATURE = 0x454C // LE
|
||||
IMAGE_NT_SIGNATURE = 0x00004550 // PE00
|
||||
)
|
||||
|
||||
// DOS .EXE header
|
||||
type IMAGE_DOS_HEADER struct {
|
||||
E_magic uint16 // Magic number
|
||||
E_cblp uint16 // Bytes on last page of file
|
||||
E_cp uint16 // Pages in file
|
||||
E_crlc uint16 // Relocations
|
||||
E_cparhdr uint16 // Size of header in paragraphs
|
||||
E_minalloc uint16 // Minimum extra paragraphs needed
|
||||
E_maxalloc uint16 // Maximum extra paragraphs needed
|
||||
E_ss uint16 // Initial (relative) SS value
|
||||
E_sp uint16 // Initial SP value
|
||||
E_csum uint16 // Checksum
|
||||
E_ip uint16 // Initial IP value
|
||||
E_cs uint16 // Initial (relative) CS value
|
||||
E_lfarlc uint16 // File address of relocation table
|
||||
E_ovno uint16 // Overlay number
|
||||
E_res [4]uint16 // Reserved words
|
||||
E_oemid uint16 // OEM identifier (for e_oeminfo)
|
||||
E_oeminfo uint16 // OEM information; e_oemid specific
|
||||
E_res2 [10]uint16 // Reserved words
|
||||
E_lfanew int32 // File address of new exe header
|
||||
}
|
||||
|
||||
// File header format
|
||||
type IMAGE_FILE_HEADER struct {
|
||||
Machine uint16
|
||||
NumberOfSections uint16
|
||||
TimeDateStamp uint32
|
||||
PointerToSymbolTable uint32
|
||||
NumberOfSymbols uint32
|
||||
SizeOfOptionalHeader uint16
|
||||
Characteristics uint16
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_SIZEOF_FILE_HEADER = 20
|
||||
|
||||
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file.
|
||||
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references).
|
||||
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file.
|
||||
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file.
|
||||
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set
|
||||
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses
|
||||
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed.
|
||||
IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine.
|
||||
IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file
|
||||
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file.
|
||||
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file.
|
||||
IMAGE_FILE_SYSTEM = 0x1000 // System File.
|
||||
IMAGE_FILE_DLL = 0x2000 // File is a DLL.
|
||||
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine
|
||||
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed.
|
||||
|
||||
IMAGE_FILE_MACHINE_UNKNOWN = 0
|
||||
IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
|
||||
IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386.
|
||||
IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian
|
||||
IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian
|
||||
IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian
|
||||
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2
|
||||
IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP
|
||||
IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian
|
||||
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
|
||||
IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian
|
||||
IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian
|
||||
IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5
|
||||
IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian
|
||||
IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
|
||||
IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian
|
||||
IMAGE_FILE_MACHINE_AM33 = 0x01d3
|
||||
IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian
|
||||
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
|
||||
IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64
|
||||
IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS
|
||||
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64
|
||||
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS
|
||||
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS
|
||||
IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64
|
||||
IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon
|
||||
IMAGE_FILE_MACHINE_CEF = 0x0CEF
|
||||
IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code
|
||||
IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8)
|
||||
IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian
|
||||
IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian
|
||||
IMAGE_FILE_MACHINE_CEE = 0xC0EE
|
||||
)
|
||||
|
||||
// Directory format
|
||||
type IMAGE_DATA_DIRECTORY struct {
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
}
|
||||
|
||||
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
||||
|
||||
type IMAGE_NT_HEADERS struct {
|
||||
Signature uint32
|
||||
FileHeader IMAGE_FILE_HEADER
|
||||
OptionalHeader IMAGE_OPTIONAL_HEADER
|
||||
}
|
||||
|
||||
func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER {
|
||||
return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer(
|
||||
(uintptr)(unsafe.Pointer(ntheader)) +
|
||||
unsafe.Offsetof(ntheader.OptionalHeader) +
|
||||
uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections]
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory
|
||||
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory
|
||||
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory
|
||||
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory
|
||||
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory
|
||||
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table
|
||||
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory
|
||||
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage)
|
||||
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data
|
||||
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP
|
||||
IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory
|
||||
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory
|
||||
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers
|
||||
IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table
|
||||
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors
|
||||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor
|
||||
)
|
||||
|
||||
const IMAGE_SIZEOF_SHORT_NAME = 8
|
||||
|
||||
// Section header format
|
||||
type IMAGE_SECTION_HEADER struct {
|
||||
Name [IMAGE_SIZEOF_SHORT_NAME]byte
|
||||
physicalAddressOrVirtualSize uint32
|
||||
VirtualAddress uint32
|
||||
SizeOfRawData uint32
|
||||
PointerToRawData uint32
|
||||
PointerToRelocations uint32
|
||||
PointerToLinenumbers uint32
|
||||
NumberOfRelocations uint16
|
||||
NumberOfLinenumbers uint16
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 {
|
||||
return ishdr.physicalAddressOrVirtualSize
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) {
|
||||
ishdr.physicalAddressOrVirtualSize = addr
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 {
|
||||
return ishdr.physicalAddressOrVirtualSize
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) {
|
||||
ishdr.physicalAddressOrVirtualSize = addr
|
||||
}
|
||||
|
||||
const (
|
||||
// Dll characteristics.
|
||||
IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
|
||||
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
|
||||
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080
|
||||
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100
|
||||
IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION = 0x0200
|
||||
IMAGE_DLL_CHARACTERISTICS_NO_SEH = 0x0400
|
||||
IMAGE_DLL_CHARACTERISTICS_NO_BIND = 0x0800
|
||||
IMAGE_DLL_CHARACTERISTICS_APPCONTAINER = 0x1000
|
||||
IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER = 0x2000
|
||||
IMAGE_DLL_CHARACTERISTICS_GUARD_CF = 0x4000
|
||||
IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
|
||||
)
|
||||
|
||||
const (
|
||||
// Section characteristics.
|
||||
IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved.
|
||||
IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved.
|
||||
IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved.
|
||||
IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved.
|
||||
IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved.
|
||||
IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved.
|
||||
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code.
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data.
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data.
|
||||
|
||||
IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved.
|
||||
IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information.
|
||||
IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved.
|
||||
IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image.
|
||||
IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat.
|
||||
IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete.
|
||||
IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||
IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP
|
||||
IMAGE_SCN_MEM_FARDATA = 0x00008000
|
||||
IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete.
|
||||
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
|
||||
IMAGE_SCN_MEM_16BIT = 0x00020000
|
||||
IMAGE_SCN_MEM_LOCKED = 0x00040000
|
||||
IMAGE_SCN_MEM_PRELOAD = 0x00080000
|
||||
|
||||
IMAGE_SCN_ALIGN_1BYTES = 0x00100000 //
|
||||
IMAGE_SCN_ALIGN_2BYTES = 0x00200000 //
|
||||
IMAGE_SCN_ALIGN_4BYTES = 0x00300000 //
|
||||
IMAGE_SCN_ALIGN_8BYTES = 0x00400000 //
|
||||
IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified.
|
||||
IMAGE_SCN_ALIGN_32BYTES = 0x00600000 //
|
||||
IMAGE_SCN_ALIGN_64BYTES = 0x00700000 //
|
||||
IMAGE_SCN_ALIGN_128BYTES = 0x00800000 //
|
||||
IMAGE_SCN_ALIGN_256BYTES = 0x00900000 //
|
||||
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 //
|
||||
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 //
|
||||
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 //
|
||||
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 //
|
||||
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 //
|
||||
IMAGE_SCN_ALIGN_MASK = 0x00F00000
|
||||
|
||||
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations.
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded.
|
||||
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable.
|
||||
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable.
|
||||
IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable.
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable.
|
||||
IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable.
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable.
|
||||
|
||||
// TLS Characteristic Flags
|
||||
IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled.
|
||||
)
|
||||
|
||||
// Based relocation format
|
||||
type IMAGE_BASE_RELOCATION struct {
|
||||
VirtualAddress uint32
|
||||
SizeOfBlock uint32
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_REL_BASED_ABSOLUTE = 0
|
||||
IMAGE_REL_BASED_HIGH = 1
|
||||
IMAGE_REL_BASED_LOW = 2
|
||||
IMAGE_REL_BASED_HIGHLOW = 3
|
||||
IMAGE_REL_BASED_HIGHADJ = 4
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5
|
||||
IMAGE_REL_BASED_RESERVED = 6
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9
|
||||
IMAGE_REL_BASED_DIR64 = 10
|
||||
|
||||
IMAGE_REL_BASED_IA64_IMM64 = 9
|
||||
|
||||
IMAGE_REL_BASED_MIPS_JMPADDR = 5
|
||||
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9
|
||||
|
||||
IMAGE_REL_BASED_ARM_MOV32 = 5
|
||||
IMAGE_REL_BASED_THUMB_MOV32 = 7
|
||||
)
|
||||
|
||||
// Export Format
|
||||
type IMAGE_EXPORT_DIRECTORY struct {
|
||||
Characteristics uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
Name uint32
|
||||
Base uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32
|
||||
AddressOfFunctions uint32 // RVA from base of image
|
||||
AddressOfNames uint32 // RVA from base of image
|
||||
AddressOfNameOrdinals uint32 // RVA from base of image
|
||||
}
|
||||
|
||||
type IMAGE_IMPORT_BY_NAME struct {
|
||||
Hint uint16
|
||||
Name [1]byte
|
||||
}
|
||||
|
||||
func IMAGE_ORDINAL(ordinal uintptr) uintptr {
|
||||
return ordinal & 0xffff
|
||||
}
|
||||
|
||||
func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool {
|
||||
return (ordinal & IMAGE_ORDINAL_FLAG) != 0
|
||||
}
|
||||
|
||||
// Thread Local Storage
|
||||
type IMAGE_TLS_DIRECTORY struct {
|
||||
StartAddressOfRawData uintptr
|
||||
EndAddressOfRawData uintptr
|
||||
AddressOfIndex uintptr // PDWORD
|
||||
AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *;
|
||||
SizeOfZeroFill uint32
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
type IMAGE_IMPORT_DESCRIPTOR struct {
|
||||
characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor
|
||||
// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
||||
TimeDateStamp uint32 // 0 if not bound,
|
||||
// -1 if bound, and real date\time stamp
|
||||
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||
ForwarderChain uint32 // -1 if no forwarders
|
||||
Name uint32
|
||||
FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses)
|
||||
}
|
||||
|
||||
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 {
|
||||
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||
}
|
||||
|
||||
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 {
|
||||
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||
}
|
||||
|
||||
type IMAGE_DELAYLOAD_DESCRIPTOR struct {
|
||||
Attributes uint32
|
||||
DllNameRVA uint32
|
||||
ModuleHandleRVA uint32
|
||||
ImportAddressTableRVA uint32
|
||||
ImportNameTableRVA uint32
|
||||
BoundImportAddressTableRVA uint32
|
||||
UnloadInformationTableRVA uint32
|
||||
TimeDateStamp uint32
|
||||
}
|
||||
|
||||
type IMAGE_LOAD_CONFIG_CODE_INTEGRITY struct {
|
||||
Flags uint16
|
||||
Catalog uint16
|
||||
CatalogOffset uint32
|
||||
Reserved uint32
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_GUARD_CF_INSTRUMENTED = 0x00000100
|
||||
IMAGE_GUARD_CFW_INSTRUMENTED = 0x00000200
|
||||
IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT = 0x00000400
|
||||
IMAGE_GUARD_SECURITY_COOKIE_UNUSED = 0x00000800
|
||||
IMAGE_GUARD_PROTECT_DELAYLOAD_IAT = 0x00001000
|
||||
IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x00002000
|
||||
IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x00004000
|
||||
IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION = 0x00008000
|
||||
IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT = 0x00010000
|
||||
IMAGE_GUARD_RF_INSTRUMENTED = 0x00020000
|
||||
IMAGE_GUARD_RF_ENABLE = 0x00040000
|
||||
IMAGE_GUARD_RF_STRICT = 0x00080000
|
||||
IMAGE_GUARD_RETPOLINE_PRESENT = 0x00100000
|
||||
IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT = 0x00400000
|
||||
IMAGE_GUARD_XFG_ENABLED = 0x00800000
|
||||
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000
|
||||
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT = 28
|
||||
)
|
||||
|
||||
type IMAGE_RUNTIME_FUNCTION_ENTRY struct {
|
||||
BeginAddress uint32
|
||||
EndAddress uint32
|
||||
UnwindInfoAddress uint32
|
||||
}
|
||||
|
||||
const (
|
||||
DLL_PROCESS_ATTACH = 1
|
||||
DLL_THREAD_ATTACH = 2
|
||||
DLL_THREAD_DETACH = 3
|
||||
DLL_PROCESS_DETACH = 0
|
||||
)
|
||||
|
||||
type SYSTEM_INFO struct {
|
||||
ProcessorArchitecture uint16
|
||||
Reserved uint16
|
||||
PageSize uint32
|
||||
MinimumApplicationAddress uintptr
|
||||
MaximumApplicationAddress uintptr
|
||||
ActiveProcessorMask uintptr
|
||||
NumberOfProcessors uint32
|
||||
ProcessorType uint32
|
||||
AllocationGranularity uint32
|
||||
ProcessorLevel uint16
|
||||
ProcessorRevision uint16
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
// +build windows,386 windows,arm
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
// Optional header format
|
||||
type IMAGE_OPTIONAL_HEADER struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
BaseOfData uint32
|
||||
ImageBase uintptr
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uintptr
|
||||
SizeOfStackCommit uintptr
|
||||
SizeOfHeapReserve uintptr
|
||||
SizeOfHeapCommit uintptr
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||
}
|
||||
|
||||
const IMAGE_ORDINAL_FLAG uintptr = 0x80000000
|
||||
|
||||
type IMAGE_LOAD_CONFIG_DIRECTORY struct {
|
||||
Size uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
GlobalFlagsClear uint32
|
||||
GlobalFlagsSet uint32
|
||||
CriticalSectionDefaultTimeout uint32
|
||||
DeCommitFreeBlockThreshold uint32
|
||||
DeCommitTotalFreeThreshold uint32
|
||||
LockPrefixTable uint32
|
||||
MaximumAllocationSize uint32
|
||||
VirtualMemoryThreshold uint32
|
||||
ProcessHeapFlags uint32
|
||||
ProcessAffinityMask uint32
|
||||
CSDVersion uint16
|
||||
DependentLoadFlags uint16
|
||||
EditList uint32
|
||||
SecurityCookie uint32
|
||||
SEHandlerTable uint32
|
||||
SEHandlerCount uint32
|
||||
GuardCFCheckFunctionPointer uint32
|
||||
GuardCFDispatchFunctionPointer uint32
|
||||
GuardCFFunctionTable uint32
|
||||
GuardCFFunctionCount uint32
|
||||
GuardFlags uint32
|
||||
CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY
|
||||
GuardAddressTakenIatEntryTable uint32
|
||||
GuardAddressTakenIatEntryCount uint32
|
||||
GuardLongJumpTargetTable uint32
|
||||
GuardLongJumpTargetCount uint32
|
||||
DynamicValueRelocTable uint32
|
||||
CHPEMetadataPointer uint32
|
||||
GuardRFFailureRoutine uint32
|
||||
GuardRFFailureRoutineFunctionPointer uint32
|
||||
DynamicValueRelocTableOffset uint32
|
||||
DynamicValueRelocTableSection uint16
|
||||
Reserved2 uint16
|
||||
GuardRFVerifyStackPointerFunctionPointer uint32
|
||||
HotPatchTableOffset uint32
|
||||
Reserved3 uint32
|
||||
EnclaveConfigurationPointer uint32
|
||||
VolatileMetadataPointer uint32
|
||||
GuardEHContinuationTable uint32
|
||||
GuardEHContinuationCount uint32
|
||||
GuardXFGCheckFunctionPointer uint32
|
||||
GuardXFGDispatchFunctionPointer uint32
|
||||
GuardXFGTableDispatchFunctionPointer uint32
|
||||
CastGuardOsDeterminedFailureMode uint32
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
// +build windows,amd64 windows,arm64
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
// Optional header format
|
||||
type IMAGE_OPTIONAL_HEADER struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
ImageBase uintptr
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uintptr
|
||||
SizeOfStackCommit uintptr
|
||||
SizeOfHeapReserve uintptr
|
||||
SizeOfHeapCommit uintptr
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||
}
|
||||
|
||||
const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000
|
||||
|
||||
type IMAGE_LOAD_CONFIG_DIRECTORY struct {
|
||||
Size uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
GlobalFlagsClear uint32
|
||||
GlobalFlagsSet uint32
|
||||
CriticalSectionDefaultTimeout uint32
|
||||
DeCommitFreeBlockThreshold uint64
|
||||
DeCommitTotalFreeThreshold uint64
|
||||
LockPrefixTable uint64
|
||||
MaximumAllocationSize uint64
|
||||
VirtualMemoryThreshold uint64
|
||||
ProcessAffinityMask uint64
|
||||
ProcessHeapFlags uint32
|
||||
CSDVersion uint16
|
||||
DependentLoadFlags uint16
|
||||
EditList uint64
|
||||
SecurityCookie uint64
|
||||
SEHandlerTable uint64
|
||||
SEHandlerCount uint64
|
||||
GuardCFCheckFunctionPointer uint64
|
||||
GuardCFDispatchFunctionPointer uint64
|
||||
GuardCFFunctionTable uint64
|
||||
GuardCFFunctionCount uint64
|
||||
GuardFlags uint32
|
||||
CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY
|
||||
GuardAddressTakenIatEntryTable uint64
|
||||
GuardAddressTakenIatEntryCount uint64
|
||||
GuardLongJumpTargetTable uint64
|
||||
GuardLongJumpTargetCount uint64
|
||||
DynamicValueRelocTable uint64
|
||||
CHPEMetadataPointer uint64
|
||||
GuardRFFailureRoutine uint64
|
||||
GuardRFFailureRoutineFunctionPointer uint64
|
||||
DynamicValueRelocTableOffset uint32
|
||||
DynamicValueRelocTableSection uint16
|
||||
Reserved2 uint16
|
||||
GuardRFVerifyStackPointerFunctionPointer uint64
|
||||
HotPatchTableOffset uint32
|
||||
Reserved3 uint32
|
||||
EnclaveConfigurationPointer uint64
|
||||
VolatileMetadataPointer uint64
|
||||
GuardEHContinuationTable uint64
|
||||
GuardEHContinuationCount uint64
|
||||
GuardXFGCheckFunctionPointer uint64
|
||||
GuardXFGDispatchFunctionPointer uint64
|
||||
GuardXFGTableDispatchFunctionPointer uint64
|
||||
CastGuardOsDeterminedFailureMode uint64
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
handle uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
PacketSizeMax = 0xffff // Maximum packet size
|
||||
RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB)
|
||||
RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB)
|
||||
)
|
||||
|
||||
// Packet with data
|
||||
type Packet struct {
|
||||
Next *Packet // Pointer to next packet in queue
|
||||
Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE)
|
||||
Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet
|
||||
}
|
||||
|
||||
var (
|
||||
procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket")
|
||||
procWintunEndSession = modwintun.NewProc("WintunEndSession")
|
||||
procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent")
|
||||
procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket")
|
||||
procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket")
|
||||
procWintunSendPacket = modwintun.NewProc("WintunSendPacket")
|
||||
procWintunStartSession = modwintun.NewProc("WintunStartSession")
|
||||
)
|
||||
|
||||
func (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0)
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
} else {
|
||||
session = Session{r0}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (session Session) End() {
|
||||
syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0)
|
||||
session.handle = 0
|
||||
}
|
||||
|
||||
func (session Session) ReadWaitEvent() (handle windows.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0)
|
||||
handle = windows.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func (session Session) ReceivePacket() (packet []byte, err error) {
|
||||
var packetSize uint32
|
||||
r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0)
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||
return
|
||||
}
|
||||
|
||||
func (session Session) ReleaseReceivePacket(packet []byte) {
|
||||
syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||
}
|
||||
|
||||
func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0)
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||
return
|
||||
}
|
||||
|
||||
func (session Session) SendPacket(packet []byte) {
|
||||
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||
}
|
||||
|
||||
// unsafeSlice updates the slice slicePtr to be a slice
|
||||
// referencing the provided data with its length & capacity set to
|
||||
// lenCap.
|
||||
//
|
||||
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||
// update callers to use unsafe.Slice instead of this.
|
||||
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
h := (*sliceHeader)(slicePtr)
|
||||
h.Data = data
|
||||
h.Len = lenCap
|
||||
h.Cap = lenCap
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type loggerLevel int
|
||||
|
||||
const (
|
||||
logInfo loggerLevel = iota
|
||||
logWarn
|
||||
logErr
|
||||
)
|
||||
|
||||
const (
|
||||
PoolNameMax = 256
|
||||
AdapterNameMax = 128
|
||||
)
|
||||
|
||||
type Pool [PoolNameMax]uint16
|
||||
type Adapter struct {
|
||||
handle uintptr
|
||||
}
|
||||
|
||||
var (
|
||||
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
||||
|
||||
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
||||
procWintunDeleteAdapter = modwintun.NewProc("WintunDeleteAdapter")
|
||||
procWintunDeletePoolDriver = modwintun.NewProc("WintunDeletePoolDriver")
|
||||
procWintunEnumAdapters = modwintun.NewProc("WintunEnumAdapters")
|
||||
procWintunFreeAdapter = modwintun.NewProc("WintunFreeAdapter")
|
||||
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
|
||||
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
|
||||
procWintunGetAdapterName = modwintun.NewProc("WintunGetAdapterName")
|
||||
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
|
||||
procWintunSetAdapterName = modwintun.NewProc("WintunSetAdapterName")
|
||||
)
|
||||
|
||||
func setupLogger(dll *lazyDLL) {
|
||||
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, windows.NewCallback(func(level loggerLevel, msg *uint16) int {
|
||||
log.Println("[Wintun]", windows.UTF16PtrToString(msg))
|
||||
return 0
|
||||
}), 0, 0)
|
||||
}
|
||||
|
||||
func MakePool(poolName string) (pool *Pool, err error) {
|
||||
poolName16, err := windows.UTF16FromString(poolName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(poolName16) > PoolNameMax {
|
||||
err = errors.New("Pool name too long")
|
||||
return
|
||||
}
|
||||
pool = &Pool{}
|
||||
copy(pool[:], poolName16)
|
||||
return
|
||||
}
|
||||
|
||||
func (pool *Pool) String() string {
|
||||
return windows.UTF16ToString(pool[:])
|
||||
}
|
||||
|
||||
func freeAdapter(wintun *Adapter) {
|
||||
syscall.Syscall(procWintunFreeAdapter.Addr(), 1, uintptr(wintun.handle), 0, 0)
|
||||
}
|
||||
|
||||
// OpenAdapter finds a Wintun adapter by its name. This function returns the adapter if found, or
|
||||
// windows.ERROR_FILE_NOT_FOUND otherwise. If the adapter is found but not a Wintun-class or a
|
||||
// member of the pool, this function returns windows.ERROR_ALREADY_EXISTS. The adapter must be
|
||||
// released after use.
|
||||
func (pool *Pool) OpenAdapter(ifname string) (wintun *Adapter, err error) {
|
||||
ifname16, err := windows.UTF16PtrFromString(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), 0)
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
wintun = &Adapter{r0}
|
||||
runtime.SetFinalizer(wintun, freeAdapter)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateAdapter creates a Wintun adapter. ifname is the requested name of the adapter, while
|
||||
// requestedGUID is the GUID of the created network adapter, which then influences NLA generation
|
||||
// deterministically. If it is set to nil, the GUID is chosen by the system at random, and hence a
|
||||
// new NLA entry is created for each new adapter. It is called "requested" GUID because the API it
|
||||
// uses is completely undocumented, and so there could be minor interesting complications with its
|
||||
// usage. This function returns the network adapter ID and a flag if reboot is required.
|
||||
func (pool *Pool) CreateAdapter(ifname string, requestedGUID *windows.GUID) (wintun *Adapter, rebootRequired bool, err error) {
|
||||
var ifname16 *uint16
|
||||
ifname16, err = windows.UTF16PtrFromString(ifname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p0 uint32
|
||||
r0, _, e1 := syscall.Syscall6(procWintunCreateAdapter.Addr(), 4, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), uintptr(unsafe.Pointer(requestedGUID)), uintptr(unsafe.Pointer(&_p0)), 0, 0)
|
||||
rebootRequired = _p0 != 0
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
wintun = &Adapter{r0}
|
||||
runtime.SetFinalizer(wintun, freeAdapter)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes a Wintun adapter. This function succeeds if the adapter was not found. It returns
|
||||
// a bool indicating whether a reboot is required.
|
||||
func (wintun *Adapter) Delete(forceCloseSessions bool) (rebootRequired bool, err error) {
|
||||
var _p0 uint32
|
||||
if forceCloseSessions {
|
||||
_p0 = 1
|
||||
}
|
||||
var _p1 uint32
|
||||
r1, _, e1 := syscall.Syscall(procWintunDeleteAdapter.Addr(), 3, uintptr(wintun.handle), uintptr(_p0), uintptr(unsafe.Pointer(&_p1)))
|
||||
rebootRequired = _p1 != 0
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteMatchingAdapters deletes all Wintun adapters, which match
|
||||
// given criteria, and returns which ones it deleted, whether a reboot
|
||||
// is required after, and which errors occurred during the process.
|
||||
func (pool *Pool) DeleteMatchingAdapters(matches func(adapter *Adapter) bool, forceCloseSessions bool) (rebootRequired bool, errors []error) {
|
||||
cb := func(handle uintptr, _ uintptr) int {
|
||||
adapter := &Adapter{handle}
|
||||
if !matches(adapter) {
|
||||
return 1
|
||||
}
|
||||
rebootRequired2, err := adapter.Delete(forceCloseSessions)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
return 1
|
||||
}
|
||||
rebootRequired = rebootRequired || rebootRequired2
|
||||
return 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procWintunEnumAdapters.Addr(), 3, uintptr(unsafe.Pointer(pool)), uintptr(windows.NewCallback(cb)), 0)
|
||||
if r1 == 0 {
|
||||
errors = append(errors, e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Name returns the name of the Wintun adapter.
|
||||
func (wintun *Adapter) Name() (ifname string, err error) {
|
||||
var ifname16 [AdapterNameMax]uint16
|
||||
r1, _, e1 := syscall.Syscall(procWintunGetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
ifname = windows.UTF16ToString(ifname16[:])
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDriver deletes all Wintun adapters in a pool and if there are no more adapters in any other
|
||||
// pools, also removes Wintun from the driver store, usually called by uninstallers.
|
||||
func (pool *Pool) DeleteDriver() (rebootRequired bool, err error) {
|
||||
var _p0 uint32
|
||||
r1, _, e1 := syscall.Syscall(procWintunDeletePoolDriver.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(&_p0)), 0)
|
||||
rebootRequired = _p0 != 0
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// SetName sets name of the Wintun adapter.
|
||||
func (wintun *Adapter) SetName(ifname string) (err error) {
|
||||
ifname16, err := windows.UTF16FromString(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procWintunSetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RunningVersion returns the version of the running Wintun driver.
|
||||
func RunningVersion() (version uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0)
|
||||
version = uint32(r0)
|
||||
if version == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LUID returns the LUID of the adapter.
|
||||
func (wintun *Adapter) LUID() (luid uint64) {
|
||||
syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0)
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue
Block a user