From 5f5503afa8c8b9cf2bc2bbe5a3a588e68eae15ef Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Mon, 14 May 2018 03:55:46 +0200
Subject: [PATCH] Add rwcancelation to darwin

---
 rwcancel/rwcancel_unix.go |  4 +--
 rwcancel/select_darwin.go | 12 ++++++++
 rwcancel/select_linux.go  | 13 +++++++++
 tun_darwin.go             | 61 +++++++++++++++++++++++++++++----------
 4 files changed, 73 insertions(+), 17 deletions(-)
 create mode 100644 rwcancel/select_darwin.go
 create mode 100644 rwcancel/select_linux.go

diff --git a/rwcancel/rwcancel_unix.go b/rwcancel/rwcancel_unix.go
index cd3661f..7f2c9e0 100644
--- a/rwcancel/rwcancel_unix.go
+++ b/rwcancel/rwcancel_unix.go
@@ -77,7 +77,7 @@ func (rw *RWCancel) ReadyRead() bool {
 	fdset := fdSet{}
 	fdset.set(rw.fd)
 	fdset.set(closeFd)
-	_, err := unix.Select(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil)
+	err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil)
 	if err != nil {
 		return false
 	}
@@ -92,7 +92,7 @@ func (rw *RWCancel) ReadyWrite() bool {
 	fdset := fdSet{}
 	fdset.set(rw.fd)
 	fdset.set(closeFd)
-	_, err := unix.Select(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil)
+	err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil)
 	if err != nil {
 		return false
 	}
diff --git a/rwcancel/select_darwin.go b/rwcancel/select_darwin.go
new file mode 100644
index 0000000..d14edc8
--- /dev/null
+++ b/rwcancel/select_darwin.go
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+package rwcancel
+
+import "golang.org/x/sys/unix"
+
+func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error {
+	return unix.Select(nfd, r, w, e, timeout)
+}
diff --git a/rwcancel/select_linux.go b/rwcancel/select_linux.go
new file mode 100644
index 0000000..c3d4e27
--- /dev/null
+++ b/rwcancel/select_linux.go
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+package rwcancel
+
+import "golang.org/x/sys/unix"
+
+func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) (err error) {
+	_, err = unix.Select(nfd, r, w, e, timeout)
+	return
+}
diff --git a/tun_darwin.go b/tun_darwin.go
index fa8efe0..1ce039d 100644
--- a/tun_darwin.go
+++ b/tun_darwin.go
@@ -6,7 +6,9 @@
 package main
 
 import (
+	"./rwcancel"
 	"encoding/binary"
+	"errors"
 	"fmt"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/sys/unix"
@@ -34,9 +36,10 @@ type sockaddrCtl struct {
 // NativeTun is a hack to work around the first 4 bytes "packet
 // information" because there doesn't seem to be an IFF_NO_PI for darwin.
 type NativeTun struct {
-	name string
-	fd   *os.File
-	mtu  int
+	name     string
+	fd       *os.File
+	rwcancel *rwcancel.RWCancel
+	mtu      int
 
 	events chan TUNEvent
 	errors chan error
@@ -121,6 +124,17 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
 		return nil, err
 	}
 
+	// set default MTU
+	err = tun.setMTU(DefaultMTU)
+	if err != nil {
+		return nil, err
+	}
+
+	tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
+	if err != nil {
+		return nil, err
+	}
+
 	// TODO: Fix this very naive implementation
 	go func(tun *NativeTun) {
 		var (
@@ -153,10 +167,7 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
 		}
 	}(tun)
 
-	// set default MTU
-	err = tun.setMTU(DefaultMTU)
-
-	return tun, err
+	return tun, nil
 }
 
 func (tun *NativeTun) Name() (string, error) {
@@ -190,14 +201,30 @@ func (tun *NativeTun) Events() chan TUNEvent {
 	return tun.events
 }
 
-func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
-
-	buff = buff[offset-4:]
-	n, err := tun.fd.Read(buff[:])
-	if n < 4 {
+func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
+	select {
+	case err := <-tun.errors:
 		return 0, err
+	default:
+		buff := buff[offset-4:]
+		n, err := tun.fd.Read(buff[:])
+		if n < 4 {
+			return 0, err
+		}
+		return n - 4, err
+	}
+}
+
+func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
+	for {
+		n, err := tun.doRead(buff, offset)
+		if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
+			return n, err
+		}
+		if !tun.rwcancel.ReadyRead() {
+			return 0, errors.New("tun device closed")
+		}
 	}
-	return n - 4, err
 }
 
 func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
@@ -224,9 +251,13 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
 }
 
 func (tun *NativeTun) Close() error {
-	err := tun.fd.Close()
+	err1 := tun.rwcancel.Cancel()
+	err2 := tun.fd.Close()
 	close(tun.events)
-	return err
+	if err1 != nil {
+		return err1
+	}
+	return err2
 }
 
 func (tun *NativeTun) setMTU(n int) error {