]> git.ipfire.org Git - thirdparty/wireguard-go.git/commitdiff
Import windows scafolding
authorJason A. Donenfeld <Jason@zx2c4.com>
Mon, 4 Feb 2019 16:29:52 +0000 (17:29 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Tue, 5 Feb 2019 11:59:42 +0000 (12:59 +0100)
go.mod
go.sum
main.go
main_windows.go [new file with mode: 0644]
tun/tun_windows.go [new file with mode: 0644]
uapi_windows.go [new file with mode: 0644]

diff --git a/go.mod b/go.mod
index 9dcbf7cfb7f269815e16a7e97581ab63b9b9ad2e..38fb50d7ae602a1f3cb8dbdf016feb4e446aa4fb 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,8 @@
 module git.zx2c4.com/wireguard-go
 
 require (
-       golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
-       golang.org/x/net v0.0.0-20181207154023-610586996380
-       golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5
+       github.com/Microsoft/go-winio v0.4.11
+       golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
+       golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3
+       golang.org/x/sys v0.0.0-20190204103248-980327fe3c65
 )
diff --git a/go.sum b/go.sum
index f2c94ff866ec0d6c52cec4f00c201bcb89ee92b8..4ff39fa3a01ae49eb90971944b7021140f629733 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/net v0.0.0-20181207154023-610586996380 h1:zPQexyRtNYBc7bcHmehl1dH6TB3qn8zytv8cBGLDNY0=
-golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5 h1:SlFRMb9PEnqzqnBRCynVOhxv4vHjB2lnIoxK6p5nzFM=
-golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
+golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sys v0.0.0-20190204103248-980327fe3c65 h1:kWe3kjq30rdgl/nMB+ZR7kWcMnlZZ35LNkvwLN7yBco=
+golang.org/x/sys v0.0.0-20190204103248-980327fe3c65/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/main.go b/main.go
index 0f5759d6e71059c99d8da3228653230484be251c..6779fc13cbe1d9c85e1b9ce300eca299161a5b09 100644 (file)
--- a/main.go
+++ b/main.go
@@ -1,3 +1,5 @@
+// +build !windows
+
 /* SPDX-License-Identifier: MIT
  *
  * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
diff --git a/main_windows.go b/main_windows.go
new file mode 100644 (file)
index 0000000..f6a0b88
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package main
+
+import (
+       "fmt"
+       "git.zx2c4.com/wireguard-go/tun"
+       "os"
+       "os/signal"
+)
+
+const (
+       ExitSetupSuccess = 0
+       ExitSetupFailed  = 1
+)
+
+func main() {
+
+       if len(os.Args) != 1 {
+               os.Exit(ExitSetupFailed)
+       }
+       //configFile := os.Args[1]
+       interfaceName := "TODO"
+
+       logger := NewLogger(
+               LogLevelDebug,
+               fmt.Sprintf("(%s) ", interfaceName),
+       )
+       logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion)
+       logger.Debug.Println("Debug log enabled")
+
+       tun, err := tun.CreateTUN(interfaceName)
+       if err == nil {
+               realInterfaceName, err2 := tun.Name()
+               if err2 == nil {
+                       interfaceName = realInterfaceName
+               }
+       } else {
+               logger.Error.Println("Failed to create TUN device:", err)
+               os.Exit(ExitSetupFailed)
+       }
+
+       device := NewDevice(tun, logger)
+       logger.Info.Println("Device started")
+
+       uapi, err := UAPIListen(interfaceName)
+       if err != nil {
+               logger.Error.Println("Failed to listen on uapi socket:", 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 ipcHandle(device, conn)
+               }
+       }()
+       logger.Info.Println("UAPI listener started")
+
+       // wait for program to terminate
+
+       signal.Notify(term, os.Interrupt)
+
+       select {
+       case <-term:
+       case <-errs:
+       case <-device.Wait():
+       }
+
+       // clean up
+
+       uapi.Close()
+       device.Close()
+
+       logger.Info.Println("Shutting down")
+}
diff --git a/tun/tun_windows.go b/tun/tun_windows.go
new file mode 100644 (file)
index 0000000..19a9d2d
--- /dev/null
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package tun
+
+import (
+       "errors"
+       "fmt"
+       "os"
+       "unsafe"
+
+       "golang.org/x/sys/windows"
+)
+
+const (
+       TUN_MIN_PACKET_SIZE      = 20
+       TUN_MAX_PACKET_SIZE      = 1600
+       TUN_MAX_PACKET_EXCHANGE  = 256 // Number of packets that can be exchanged at a time
+       TUN_EXCHANGE_BUFFER_SIZE = 410632
+)
+
+const (
+       TUN_SIGNAL_DATA_AVAIL = 0
+       TUN_SIGNAL_CLOSE      = 1
+
+       TUN_SIGNAL_MAX = 2
+)
+
+type TunPacket struct {
+       size uint32
+       data [TUN_MAX_PACKET_SIZE]byte
+}
+
+type TunRWQueue struct {
+       numPackets uint32
+       packets    [TUN_MAX_PACKET_EXCHANGE]TunPacket
+       left       uint32
+}
+
+type nativeTun struct {
+       ifname       string
+       tunName      string
+       signalName   *uint16
+       tunFile      *os.File
+       wrBuff       TunRWQueue
+       rdBuff       TunRWQueue
+       signals      [TUN_SIGNAL_MAX]windows.Handle
+       rdNextPacket uint32
+       events       chan TUNEvent
+       errors       chan error
+}
+
+func CreateTUN(ifname string) (TUNDevice, error) {
+       signalNameUTF16, err := windows.UTF16PtrFromString(fmt.Sprintf("Global\\TUN_EVENT_%s", ifname))
+       if err != nil {
+               return nil, err
+       }
+
+       // Create instance.
+       tun := &nativeTun{
+               ifname:     ifname,
+               tunName:    fmt.Sprintf("\\\\.\\Global\\TUN_%s_DEVICE", ifname),
+               signalName: signalNameUTF16,
+               events:     make(chan TUNEvent, 10),
+               errors:     make(chan error, 1),
+       }
+
+       // Create close event.
+       tun.signals[TUN_SIGNAL_CLOSE], err = windows.CreateEvent(nil, 1 /*TRUE*/, 0 /*FALSE*/, nil)
+       if err != nil {
+               return nil, err
+       }
+
+       return tun, nil
+}
+
+func (tun *nativeTun) openTUN() error {
+       for {
+               // Open interface data pipe.
+               // Data pipe must be opened first, as the interface data available event is created when somebody actually connects to the data pipe.
+               file, err := os.OpenFile(tun.tunName, os.O_RDWR|os.O_SYNC, 0600)
+               if err != nil {
+                       // After examining possible error conditions, many arose that were only temporary: windows.ERROR_FILE_NOT_FOUND, "read <filename> closed", etc.
+                       // To simplify, we will enter a retry-loop on _any_ error until session is closed by user.
+                       switch evt, e := windows.WaitForSingleObject(tun.signals[TUN_SIGNAL_CLOSE], 1000); evt {
+                       case windows.WAIT_OBJECT_0, windows.WAIT_ABANDONED:
+                               return errors.New("TUN closed")
+                       case windows.WAIT_TIMEOUT:
+                               continue
+                       default:
+                               return fmt.Errorf("unexpected result from WaitForSingleObject:", e)
+                       }
+               }
+
+               // Open interface data available event.
+               event, err := windows.OpenEvent(windows.SYNCHRONIZE, false, tun.signalName)
+               if err != nil {
+                       file.Close()
+                       return fmt.Errorf("opening interface data ready event failed:", err)
+               }
+
+               tun.tunFile = file
+               tun.signals[TUN_SIGNAL_DATA_AVAIL] = event
+
+               return nil
+       }
+}
+
+func (tun *nativeTun) closeTUN() (err error) {
+       if tun.signals[TUN_SIGNAL_DATA_AVAIL] != 0 {
+               // Close interface data ready event.
+               e := windows.CloseHandle(tun.signals[TUN_SIGNAL_DATA_AVAIL])
+               if err != nil {
+                       err = e
+               }
+
+               tun.signals[TUN_SIGNAL_DATA_AVAIL] = 0
+       }
+
+       if tun.tunFile != nil {
+               // Close interface data pipe.
+               e := tun.tunFile.Close()
+               if err != nil {
+                       err = e
+               }
+
+               tun.tunFile = nil
+       }
+
+       return
+}
+
+func (tun *nativeTun) Name() (string, error) {
+       return tun.ifname, nil
+}
+
+func (tun *nativeTun) File() *os.File {
+       return nil
+}
+
+func (tun *nativeTun) Events() chan TUNEvent {
+       return tun.events
+}
+
+func (tun *nativeTun) Close() error {
+       windows.SetEvent(tun.signals[TUN_SIGNAL_CLOSE])
+       err := windows.CloseHandle(tun.signals[TUN_SIGNAL_CLOSE])
+
+       e := tun.closeTUN()
+       if err == nil {
+               err = e
+       }
+
+       if tun.events != nil {
+               close(tun.events)
+       }
+
+       return err
+}
+
+func (tun *nativeTun) MTU() (int, error) {
+       return 1500, nil
+}
+
+func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
+       select {
+       case err := <-tun.errors:
+               return 0, err
+
+       default:
+               for {
+                       if tun.rdNextPacket < tun.rdBuff.numPackets {
+                               // Get packet from the queue.
+                               tunPacket := &tun.rdBuff.packets[tun.rdNextPacket]
+                               tun.rdNextPacket++
+
+                               if tunPacket.size < TUN_MIN_PACKET_SIZE || TUN_MAX_PACKET_SIZE < tunPacket.size {
+                                       // Invalid packet size.
+                                       continue
+                               }
+
+                               // Copy data.
+                               copy(buff[offset:], tunPacket.data[:tunPacket.size])
+                               return int(tunPacket.size), nil
+                       }
+
+                       if tun.signals[TUN_SIGNAL_DATA_AVAIL] == 0 {
+                               // Data pipe and interface data available event are not open (yet).
+                               err := tun.openTUN()
+                               if err != nil {
+                                       return 0, err
+                               }
+                       }
+
+                       if tun.rdBuff.numPackets < TUN_MAX_PACKET_EXCHANGE || tun.rdBuff.left == 0 {
+                               // Buffer was not full. Wait for the interface data or user close.
+                               r, err := windows.WaitForMultipleObjects(tun.signals[:], false, windows.INFINITE)
+                               if err != nil {
+                                       return 0, fmt.Errorf("waiting for data failed:", err)
+                               }
+                               switch r {
+                               case windows.WAIT_OBJECT_0 + TUN_SIGNAL_DATA_AVAIL:
+                                       // Data is available.
+                               case windows.WAIT_ABANDONED + TUN_SIGNAL_DATA_AVAIL:
+                                       // TUN stopped. Reopen it.
+                                       tun.closeTUN()
+                                       continue
+                               case windows.WAIT_OBJECT_0 + TUN_SIGNAL_CLOSE, windows.WAIT_ABANDONED + TUN_SIGNAL_CLOSE:
+                                       return 0, errors.New("TUN closed")
+                               case windows.WAIT_TIMEOUT:
+                                       // Congratulations, we reached infinity. Let's do it again! :)
+                                       continue
+                               default:
+                                       return 0, errors.New("unexpected result from WaitForMultipleObjects")
+                               }
+                       }
+
+                       // Fill queue.
+                       data := (*[TUN_EXCHANGE_BUFFER_SIZE]byte)(unsafe.Pointer(&tun.rdBuff))
+                       n, err := tun.tunFile.Read(data[:])
+                       tun.rdNextPacket = 0
+                       if n != TUN_EXCHANGE_BUFFER_SIZE || err != nil {
+                               // TUN interface stopped, returned incomplete data, etc.
+                               // Retry.
+                               tun.rdBuff.numPackets = 0
+                               tun.closeTUN()
+                               continue
+                       }
+               }
+       }
+}
+
+func (tun *nativeTun) flush() error {
+       // Flush write buffer.
+       data := (*[TUN_EXCHANGE_BUFFER_SIZE]byte)(unsafe.Pointer(&tun.wrBuff))
+       n, err := tun.tunFile.Write(data[:])
+       tun.wrBuff.numPackets = 0
+       if err != nil {
+               return err
+       }
+       if n != TUN_EXCHANGE_BUFFER_SIZE {
+               return fmt.Errorf("%d byte(s) written, %d byte(s) expected", n, TUN_EXCHANGE_BUFFER_SIZE)
+       }
+
+       return nil
+}
+
+func (tun *nativeTun) putTunPacket(buff []byte) error {
+       size := len(buff)
+       if size == 0 {
+               return errors.New("empty packet")
+       }
+       if size > TUN_MAX_PACKET_SIZE {
+               return errors.New("packet too big")
+       }
+
+       if tun.wrBuff.numPackets >= TUN_MAX_PACKET_EXCHANGE {
+               // Queue is full -> flush first.
+               err := tun.flush()
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Push packet to the buffer.
+       tunPacket := &tun.wrBuff.packets[tun.wrBuff.numPackets]
+       tunPacket.size = uint32(size)
+       copy(tunPacket.data[:size], buff)
+
+       tun.wrBuff.numPackets++
+
+       return nil
+}
+
+func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
+
+       err := tun.putTunPacket(buff[offset:])
+       if err != nil {
+               return 0, err
+       }
+
+       // Flush write buffer.
+       return len(buff) - offset, tun.flush()
+}
diff --git a/uapi_windows.go b/uapi_windows.go
new file mode 100644 (file)
index 0000000..64917f5
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package main
+
+import (
+       "github.com/Microsoft/go-winio"
+       "net"
+)
+
+//TODO: replace these with actual standard windows error numbers from the win package
+const (
+       ipcErrorIO        = -int64(5)
+       ipcErrorProtocol  = -int64(71)
+       ipcErrorInvalid   = -int64(22)
+       ipcErrorPortInUse = -int64(98)
+)
+
+type UAPIListener struct {
+       listener net.Listener // unix socket listener
+       connNew  chan net.Conn
+       connErr  chan error
+       kqueueFd int
+       keventFd int
+}
+
+func (l *UAPIListener) Accept() (net.Conn, error) {
+       for {
+               select {
+               case conn := <-l.connNew:
+                       return conn, nil
+
+               case err := <-l.connErr:
+                       return nil, err
+               }
+       }
+}
+
+func (l *UAPIListener) Close() error {
+       return l.listener.Close()
+}
+
+func (l *UAPIListener) Addr() net.Addr {
+       return l.listener.Addr()
+}
+
+func UAPIListen(name string) (net.Listener, error) {
+       config := winio.PipeConfig{
+               SecurityDescriptor: "", //TODO: we want this to be a very locked down pipe.
+       }
+       listener, err := winio.ListenPipe("\\\\.\\pipe\\wireguard\\"+name, &config) //TODO: choose sane name.
+       if err != nil {
+               return nil, err
+       }
+
+       uapi := &UAPIListener{
+               listener: listener,
+               connNew:  make(chan net.Conn, 1),
+               connErr:  make(chan error, 1),
+       }
+
+       go func(l *UAPIListener) {
+               for {
+                       conn, err := l.listener.Accept()
+                       if err != nil {
+                               l.connErr <- err
+                               break
+                       }
+                       l.connNew <- conn
+               }
+       }(uapi)
+
+       return uapi, nil
+}