summaryrefslogtreecommitdiffhomepage
path: root/wireguard-go-rs/libwg/libwg_windows.go
blob: d9e80fdb3b0095a7b4664ba168379db94bf3605d (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
//go:build windows
// +build windows

/* 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 <stdlib.h>
// #include <stdint.h>
// #include <string.h>
import "C"

import (
	"bufio"
	"strings"
	"unsafe"

	"golang.org/x/sys/windows"

	"golang.zx2c4.com/wireguard/conn"
	"golang.zx2c4.com/wireguard/device"
	"golang.zx2c4.com/wireguard/tun"

	"github.com/mullvad/mullvadvpn-app/wireguard/libwg/logging"
	"github.com/mullvad/mullvadvpn-app/wireguard/libwg/tunnelcontainer"
)

// Redefined here because otherwise the compiler doesn't realize it's a type alias for a type that's safe to export.
// Taken from the contained logging package.
type LogSink = unsafe.Pointer
type LogContext = C.uint64_t

//export wgTurnOn
func wgTurnOn(cIfaceName *C.char, cIfaceNameOut *C.char, cIfaceNameOutSize C.size_t, cLuidOut *C.uint64_t, mtu C.uint16_t, cSettings *C.char, logSink LogSink, logContext LogContext) C.int32_t {
	logger := logging.NewLogger(logSink, logging.LogContext(logContext))

	if cIfaceName == nil {
		logger.Errorf("cIfaceName is null\n")
		return ERROR_GENERAL_FAILURE
	}

	if cSettings == nil {
		logger.Errorf("cSettings is null\n")
		return ERROR_GENERAL_FAILURE
	}

	settings := C.GoString(cSettings)
	ifaceName := C.GoString(cIfaceName)

	// {AFE43773-E1F8-4EBB-8536-576AB86AFE9A}
	networkId := windows.GUID{
		Data1: 0xafe43773,
		Data2: 0xe1f8,
		Data3: 0x4ebb,
		Data4: [8]byte{0x85, 0x36, 0x57, 0x6a, 0xb8, 0x6a, 0xfe, 0x9a},
	}

	tun.WintunTunnelType = "Mullvad"

	wintun, err := tun.CreateTUNWithRequestedGUID(ifaceName, &networkId, int(mtu))
	if err != nil {
		logger.Errorf("Failed to create tunnel\n")
		logger.Errorf("%s\n", err)
		return ERROR_INTERMITTENT_FAILURE
	}

	nativeTun := wintun.(*tun.NativeTun)

	actualInterfaceName, err := nativeTun.Name()
	if err != nil {
		nativeTun.Close()
		logger.Errorf("Failed to determine name of wintun adapter\n")
		return ERROR_GENERAL_FAILURE
	}
	if actualInterfaceName != ifaceName {
		// WireGuard picked a different name for the adapter than the one we expected.
		// This indicates there is already an adapter with the name we intended to use.
		logger.Verbosef("Failed to create adapter with specific name\n")
	}

	device := device.NewDevice(wintun, conn.NewDefaultBind(), logger)

	setError := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
	if setError != nil {
		logger.Errorf("Failed to set device configuration\n")
		logger.Errorf("%s\n", setError)
		device.Close()
		return ERROR_GENERAL_FAILURE
	}

	device.Up()

	context := tunnelcontainer.Context{
		Device: device,
		Logger: logger,
	}

	handle, err := tunnels.Insert(context)
	if err != nil {
		logger.Errorf("%s\n", err)
		device.Close()
		return ERROR_GENERAL_FAILURE
	}

	if cIfaceNameOut != nil {
		if int(cIfaceNameOutSize) <= len(actualInterfaceName) {
			logger.Errorf("Interface name buffer too small\n")
			device.Close()
			return ERROR_GENERAL_FAILURE
		}
		cName := C.CString(actualInterfaceName)
		C.strcpy(cIfaceNameOut, cName)
		C.free(unsafe.Pointer(cName))
	}
	if cLuidOut != nil {
		*cLuidOut = C.uint64_t(nativeTun.LUID())
	}

	return C.int32_t(handle)
}

//export wgUpdateBind
func wgUpdateBind() {
	tunnels.ForEach(func(tunnel tunnelcontainer.Context) {
		tunnel.Device.BindUpdate()
	})
}