Start to dust off Darwin

This commit is contained in:
Jason A. Donenfeld 2018-05-03 04:49:35 +02:00
parent 168ef61a63
commit 258a9223b9
8 changed files with 206 additions and 179 deletions

View file

@ -2,17 +2,10 @@ package main
import (
"os"
"os/exec"
)
/* Daemonizes the process on linux
*
* This is done by spawning and releasing a copy with the --foreground flag
*/
func Daemonize(attr *os.ProcAttr) error {
// I would like to use os.Executable,
// however this means dropping support for Go <1.8
path, err := exec.LookPath(os.Args[0])
path, err := os.Executable()
if err != nil {
return err
}

View file

@ -1,9 +0,0 @@
package main
import (
"errors"
)
func Daemonize() error {
return errors.New("Not implemented on OSX")
}

View file

@ -1,34 +0,0 @@
package main
import (
"os"
)
/* Daemonizes the process on windows
*
* This is done by spawning and releasing a copy with the --foreground flag
*/
func Daemonize() error {
argv := []string{os.Args[0], "--foreground"}
argv = append(argv, os.Args[1:]...)
attr := &os.ProcAttr{
Dir: ".",
Env: os.Environ(),
Files: []*os.File{
os.Stdin,
nil,
nil,
},
}
process, err := os.StartProcess(
argv[0],
argv,
attr,
)
if err != nil {
return err
}
process.Release()
return nil
}

View file

@ -25,6 +25,8 @@ func printUsage() {
func main() {
Warning()
// parse arguments
var foreground bool

View file

@ -1,22 +1,12 @@
/* Copyright (c) 2016, Song Gao <song@gao.io>
* All rights reserved.
*
* Code from https://github.com/songgao/water
*/
package main
import (
"encoding/binary"
"errors"
"fmt"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"io"
"net"
"os"
"sync"
"time"
"unsafe"
)
@ -36,26 +26,20 @@ type sockaddrCtl struct {
scReserved [5]uint32
}
// NativeTUN is a hack to work around the first 4 bytes "packet
// 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 {
type NativeTun struct {
name string
f io.ReadWriteCloser
fd *os.File
mtu int
rMu sync.Mutex
rBuf []byte
wMu sync.Mutex
wBuf []byte
events chan TUNEvent
errors chan error
}
var sockaddrCtlSize uintptr = 32
func CreateTUN(name string) (ifce TUNDevice, err error) {
func CreateTUN(name string) (TUNDevice, error) {
ifIndex := -1
fmt.Sscanf(name, "utun%d", &ifIndex)
if ifIndex < 0 {
@ -65,7 +49,7 @@ func CreateTUN(name string) (ifce TUNDevice, err error) {
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
if err != nil {
return nil, fmt.Errorf("error in unix.Socket: %v", err)
return nil, err
}
var ctlInfo = &struct {
@ -83,8 +67,7 @@ func CreateTUN(name string) (ifce TUNDevice, err error) {
)
if errno != 0 {
err = errno
return nil, fmt.Errorf("error in unix.Syscall(unix.SYS_IOTL, ...): %v", err)
return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno)
}
sc := sockaddrCtl{
@ -105,148 +88,133 @@ func CreateTUN(name string) (ifce TUNDevice, err error) {
)
if errno != 0 {
err = errno
return nil, fmt.Errorf("error in unix.RawSyscall(unix.SYS_CONNECT, ...): %v", err)
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
}
// read (new) name of interface
return CreateTUNFromFile(os.NewFile(uintptr(fd), ""))
}
var ifName struct {
name [16]byte
}
ifNameSize := uintptr(16)
func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
_, _, errno = unix.Syscall6(
unix.SYS_GETSOCKOPT,
uintptr(fd),
2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)),
uintptr(unsafe.Pointer(&ifNameSize)), 0)
if errno != 0 {
err = errno
return nil, fmt.Errorf("error in unix.Syscall6(unix.SYS_GETSOCKOPT, ...): %v", err)
}
device := &NativeTUN{
name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]),
f: os.NewFile(uintptr(fd), string(ifName.name[:])),
tun := &NativeTun{
fd: file,
mtu: 1500,
events: make(chan TUNEvent, 10),
errors: make(chan error, 1),
}
// start listener
_, err := tun.Name()
if err != nil {
return nil, err
}
go func(native *NativeTUN) {
// TODO: Fix this very niave implementation
// TODO: Fix this very naive implementation
go func(tun *NativeTun) {
var (
statusUp bool
statusMTU int
)
for ; ; time.Sleep(time.Second) {
intr, err := net.InterfaceByName(device.name)
intr, err := net.InterfaceByName(tun.name)
if err != nil {
native.errors <- err
tun.errors <- err
return
}
// Up / Down event
up := (intr.Flags & net.FlagUp) != 0
if up != statusUp && up {
native.events <- TUNEventUp
tun.events <- TUNEventUp
}
if up != statusUp && !up {
native.events <- TUNEventDown
tun.events <- TUNEventDown
}
statusUp = up
// MTU changes
if intr.MTU != statusMTU {
native.events <- TUNEventMTUUpdate
tun.events <- TUNEventMTUUpdate
}
statusMTU = intr.MTU
}
}(device)
}(tun)
// set default MTU
err = tun.setMTU(DefaultMTU)
err = device.setMTU(DefaultMTU)
return device, err
return tun, err
}
var _ io.ReadWriteCloser = (*NativeTUN)(nil)
func (tun *NativeTun) Name() (string, error) {
func (t *NativeTUN) Events() chan TUNEvent {
return t.events
}
func (t *NativeTUN) Read(to []byte) (int, error) {
t.rMu.Lock()
defer t.rMu.Unlock()
if cap(t.rBuf) < len(to)+4 {
t.rBuf = make([]byte, len(to)+4)
var ifName struct {
name [16]byte
}
t.rBuf = t.rBuf[:len(to)+4]
ifNameSize := uintptr(16)
n, err := t.f.Read(t.rBuf)
copy(to, t.rBuf[4:])
_, _, errno := unix.Syscall6(
unix.SYS_GETSOCKOPT,
uintptr(tun.fd.Fd()),
2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)),
uintptr(unsafe.Pointer(&ifNameSize)), 0)
if errno != 0 {
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
}
tun.name = string(ifName.name[:ifNameSize-1])
return tun.name, nil
}
func (tun *NativeTun) File() *os.File {
return tun.fd
}
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 {
return 0, err
}
return n - 4, err
}
func (t *NativeTUN) Write(from []byte) (int, error) {
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
if len(from) == 0 {
return 0, unix.EIO
}
// reserve space for header
t.wMu.Lock()
defer t.wMu.Unlock()
buff = buff[offset-4:]
if cap(t.wBuf) < len(from)+4 {
t.wBuf = make([]byte, len(from)+4)
}
t.wBuf = t.wBuf[:len(from)+4]
// add packet information header
// determine the IP Family for the NULL L2 Header
buff[0] = 0x00
buff[1] = 0x00
buff[2] = 0x00
ipVer := from[0] >> 4
if ipVer == ipv4.Version {
t.wBuf[3] = unix.AF_INET
} else if ipVer == ipv6.Version {
t.wBuf[3] = unix.AF_INET6
if buff[4]>>4 == ipv6.Version {
buff[3] = unix.AF_INET6
} else {
return 0, errors.New("Unable to determine IP version from packet.")
buff[3] = unix.AF_INET
}
copy(t.wBuf[4:], from)
// write
n, err := t.f.Write(t.wBuf)
return n - 4, err
return tun.fd.Write(buff)
}
func (t *NativeTUN) Close() error {
// lock to make sure no read/write is in process.
t.rMu.Lock()
defer t.rMu.Unlock()
t.wMu.Lock()
defer t.wMu.Unlock()
return t.f.Close()
func (tun *NativeTun) Close() error {
return tun.fd.Close()
}
func (t *NativeTUN) Name() string {
return t.name
}
func (t *NativeTUN) setMTU(n int) error {
func (tun *NativeTun) setMTU(n int) error {
// open datagram socket
@ -267,7 +235,7 @@ func (t *NativeTUN) setMTU(n int) error {
// do ioctl call
var ifr [32]byte
copy(ifr[:], t.name)
copy(ifr[:], tun.name)
binary.LittleEndian.PutUint32(ifr[16:20], uint32(n))
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
@ -277,13 +245,13 @@ func (t *NativeTUN) setMTU(n int) error {
)
if errno != 0 {
return fmt.Errorf("Failed to set MTU on %s", t.name)
return fmt.Errorf("Failed to set MTU on %s", tun.name)
}
return nil
}
func (t *NativeTUN) MTU() (int, error) {
func (tun *NativeTun) MTU() (int, error) {
// open datagram socket
@ -302,7 +270,7 @@ func (t *NativeTUN) MTU() (int, error) {
// do ioctl call
var ifr [64]byte
copy(ifr[:], t.name)
copy(ifr[:], tun.name)
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
@ -310,7 +278,7 @@ func (t *NativeTUN) MTU() (int, error) {
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return 0, fmt.Errorf("Failed to get MTU on %s", t.name)
return 0, fmt.Errorf("Failed to get MTU on %s", tun.name)
}
// convert result to signed 32-bit int

View file

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"golang.org/x/sys/unix"
"net"
@ -44,23 +45,11 @@ func (l *UAPIListener) Addr() net.Addr {
return nil
}
func NewUAPIListener(name string) (net.Listener, error) {
func UAPIListen(name string, file *os.File) (net.Listener, error) {
// check if path exist
// wrap file in listener
err := os.MkdirAll(socketDirectory, 077)
if err != nil && !os.IsExist(err) {
return nil, err
}
// open UNIX socket
socketPath := path.Join(
socketDirectory,
fmt.Sprintf(socketName, name),
)
listener, err := net.Listen("unix", socketPath)
listener, err := net.FileListener(file)
if err != nil {
return nil, err
}
@ -73,6 +62,13 @@ func NewUAPIListener(name string) (net.Listener, error) {
// watch for deletion of socket
socketPath := path.Join(
socketDirectory,
fmt.Sprintf(socketName, name),
)
// watch for deletion of socket
go func(l *UAPIListener) {
for ; ; time.Sleep(time.Second) {
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
@ -97,3 +93,56 @@ func NewUAPIListener(name string) (net.Listener, error) {
return uapi, nil
}
func UAPIOpen(name string) (*os.File, error) {
// check if path exist
err := os.MkdirAll(socketDirectory, 0600)
if err != nil && !os.IsExist(err) {
return nil, err
}
// open UNIX socket
socketPath := path.Join(
socketDirectory,
fmt.Sprintf(socketName, name),
)
addr, err := net.ResolveUnixAddr("unix", socketPath)
if err != nil {
return nil, err
}
listener, err := func() (*net.UnixListener, error) {
// initial connection attempt
listener, err := net.ListenUnix("unix", addr)
if err == nil {
return listener, nil
}
// check if socket already active
_, err = net.Dial("unix", socketPath)
if err == nil {
return nil, errors.New("unix socket in use")
}
// cleanup & attempt again
err = os.Remove(socketPath)
if err != nil {
return nil, err
}
return net.ListenUnix("unix", addr)
}()
if err != nil {
return nil, err
}
return listener.File()
}

19
warning_default.go Normal file
View file

@ -0,0 +1,19 @@
// +build !linux
package main
import (
"fmt"
"os"
)
func Warning() {
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G")
fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G")
fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G")
fmt.Fprintln(os.Stderr, "W at your own risk. G")
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
}

39
warning_linux.go Normal file
View file

@ -0,0 +1,39 @@
package main
import (
"fmt"
"os"
)
func Warning() {
shouldQuit := os.Getenv("WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD") != "1"
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G")
fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G")
fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G")
fmt.Fprintln(os.Stderr, "W at your own risk. G")
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W Furthermore, you are running this software on a G")
fmt.Fprintln(os.Stderr, "W Linux kernel, which is probably unnecessary and G")
fmt.Fprintln(os.Stderr, "W foolish. This is because the Linux kernel has G")
fmt.Fprintln(os.Stderr, "W built-in first class support for WireGuard, and G")
fmt.Fprintln(os.Stderr, "W this support is much more refined than this G")
fmt.Fprintln(os.Stderr, "W program. For more information on installing the G")
fmt.Fprintln(os.Stderr, "W kernel module, please visit: G")
fmt.Fprintln(os.Stderr, "W https://www.wireguard.com/install G")
if shouldQuit {
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "W If you still want to use this program, against G")
fmt.Fprintln(os.Stderr, "W the sage advice here, please first export this G")
fmt.Fprintln(os.Stderr, "W environment variable: G")
fmt.Fprintln(os.Stderr, "W WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1 G")
}
fmt.Fprintln(os.Stderr, "W G")
fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING")
if shouldQuit {
os.Exit(1)
}
}