blob: 599234cc2e47c53958934c8d22b0d24e7093b041 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/* SPDX-License-Identifier: Apache-2.0
*
* Copyright (C) 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2025 Mullvad VPN AB. All Rights Reserved.
*/
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include <stdint.h>
import "C"
import (
"bufio"
"bytes"
"runtime"
"strings"
"unsafe"
"github.com/mullvad/mullvadvpn-app/wireguard/libwg/tunnelcontainer"
"golang.zx2c4.com/wireguard/device"
)
// FFI integer result codes
// NOTE: Must be kept in sync with the Error enum in wireguard-go-rs
const (
OK = C.int32_t(-iota)
// Something went wrong.
ERROR_GENERAL_FAILURE
// Something went wrong, but trying again might help.
ERROR_INTERMITTENT_FAILURE
// A bad argument was provided to libwg.
ERROR_INVALID_ARGUMENT
// The provided tunnel handle did not refer to an existing tunnel.
ERROR_UNKNOWN_TUNNEL
// The provided public key did not refer to an existing peer.
ERROR_UNKNOWN_PEER
// Something went wrong when enabling DAITA.
ERROR_ENABLE_DAITA
)
var tunnels tunnelcontainer.Container
func init() {
tunnels = tunnelcontainer.New()
}
type EventContext struct {
tunnelHandle int32
peer device.NoisePublicKey
}
//export wgTurnOff
func wgTurnOff(tunnelHandle int32) {
{
tunnel, err := tunnels.Remove(tunnelHandle)
if err != nil {
return
}
tunnel.Device.Close()
if tunnel.EntryDevice != nil {
tunnel.EntryDevice.Close()
}
}
// Calling twice convinces the GC to release NOW.
runtime.GC()
runtime.GC()
}
//export wgGetConfig
func wgGetConfig(tunnelHandle int32) *C.char {
tunnel, err := tunnels.Get(tunnelHandle)
if err != nil {
return nil
}
settings := new(bytes.Buffer)
writer := bufio.NewWriter(settings)
if err := tunnel.Device.IpcGetOperation(writer); err != nil {
tunnel.Logger.Errorf("Failed to get config for tunnel: %s\n", err)
return nil
}
writer.Flush()
return C.CString(settings.String())
}
//export wgSetConfig
func wgSetConfig(tunnelHandle int32, cSettings *C.char) C.int32_t {
tunnel, err := tunnels.Get(tunnelHandle)
if err != nil {
return ERROR_UNKNOWN_TUNNEL
}
if cSettings == nil {
tunnel.Logger.Errorf("cSettings is null\n")
return ERROR_INVALID_ARGUMENT
}
settings := goStringFixed(cSettings)
setError := tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
if setError != nil {
tunnel.Logger.Errorf("Failed to set device configuration\n")
tunnel.Logger.Errorf("%s\n", setError)
return ERROR_GENERAL_FAILURE
}
return 0
}
//export wgFreePtr
func wgFreePtr(ptr unsafe.Pointer) {
C.free(ptr)
}
func main() {}
//Instead of using the normal version of C.GoString we need to use the version that takes
//a length argument (C.GoStringN). That is because the normal C.GoString reads a whole
//page of memory to determine the length of the string. This causes a crash if
//mte is turned on. So instead we determine the length of the c string by reading
//each character until we reach the end of the string.
func goStringFixed(cString *C.char) string {
ptr := unsafe.Pointer(cString)
i := 0
for {
byte := (*C.char)(unsafe.Pointer(uintptr(ptr) + uintptr(i)))
if *byte == 0 {
break
}
i += 1
}
return C.GoStringN(cString, C.int(i))
}
|