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 ( import (
"os" "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 { func Daemonize(attr *os.ProcAttr) error {
// I would like to use os.Executable, path, err := os.Executable()
// however this means dropping support for Go <1.8
path, err := exec.LookPath(os.Args[0])
if err != nil { if err != nil {
return err 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() { func main() {
Warning()
// parse arguments // parse arguments
var foreground bool 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 package main
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"io"
"net" "net"
"os" "os"
"sync"
"time" "time"
"unsafe" "unsafe"
) )
@ -36,26 +26,20 @@ type sockaddrCtl struct {
scReserved [5]uint32 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. // information" because there doesn't seem to be an IFF_NO_PI for darwin.
type NativeTUN struct { type NativeTun struct {
name string name string
f io.ReadWriteCloser fd *os.File
mtu int mtu int
rMu sync.Mutex
rBuf []byte
wMu sync.Mutex
wBuf []byte
events chan TUNEvent events chan TUNEvent
errors chan error errors chan error
} }
var sockaddrCtlSize uintptr = 32 var sockaddrCtlSize uintptr = 32
func CreateTUN(name string) (ifce TUNDevice, err error) { func CreateTUN(name string) (TUNDevice, error) {
ifIndex := -1 ifIndex := -1
fmt.Sscanf(name, "utun%d", &ifIndex) fmt.Sscanf(name, "utun%d", &ifIndex)
if ifIndex < 0 { 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) fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
if err != nil { if err != nil {
return nil, fmt.Errorf("error in unix.Socket: %v", err) return nil, err
} }
var ctlInfo = &struct { var ctlInfo = &struct {
@ -83,8 +67,7 @@ func CreateTUN(name string) (ifce TUNDevice, err error) {
) )
if errno != 0 { if errno != 0 {
err = errno return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno)
return nil, fmt.Errorf("error in unix.Syscall(unix.SYS_IOTL, ...): %v", err)
} }
sc := sockaddrCtl{ sc := sockaddrCtl{
@ -105,148 +88,133 @@ func CreateTUN(name string) (ifce TUNDevice, err error) {
) )
if errno != 0 { if errno != 0 {
err = errno return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
return nil, fmt.Errorf("error in unix.RawSyscall(unix.SYS_CONNECT, ...): %v", err)
} }
// read (new) name of interface return CreateTUNFromFile(os.NewFile(uintptr(fd), ""))
}
var ifName struct { func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
name [16]byte
}
ifNameSize := uintptr(16)
_, _, errno = unix.Syscall6( tun := &NativeTun{
unix.SYS_GETSOCKOPT, fd: file,
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[:])),
mtu: 1500, mtu: 1500,
events: make(chan TUNEvent, 10), events: make(chan TUNEvent, 10),
errors: make(chan error, 1), errors: make(chan error, 1),
} }
// start listener _, err := tun.Name()
if err != nil {
return nil, err
}
go func(native *NativeTUN) { // TODO: Fix this very naive implementation
// TODO: Fix this very niave implementation go func(tun *NativeTun) {
var ( var (
statusUp bool statusUp bool
statusMTU int statusMTU int
) )
for ; ; time.Sleep(time.Second) { for ; ; time.Sleep(time.Second) {
intr, err := net.InterfaceByName(device.name) intr, err := net.InterfaceByName(tun.name)
if err != nil { if err != nil {
native.errors <- err tun.errors <- err
return return
} }
// Up / Down event // Up / Down event
up := (intr.Flags & net.FlagUp) != 0 up := (intr.Flags & net.FlagUp) != 0
if up != statusUp && up { if up != statusUp && up {
native.events <- TUNEventUp tun.events <- TUNEventUp
} }
if up != statusUp && !up { if up != statusUp && !up {
native.events <- TUNEventDown tun.events <- TUNEventDown
} }
statusUp = up statusUp = up
// MTU changes // MTU changes
if intr.MTU != statusMTU { if intr.MTU != statusMTU {
native.events <- TUNEventMTUUpdate tun.events <- TUNEventMTUUpdate
} }
statusMTU = intr.MTU statusMTU = intr.MTU
} }
}(device) }(tun)
// set default MTU // set default MTU
err = tun.setMTU(DefaultMTU)
err = device.setMTU(DefaultMTU) return tun, err
return device, err
} }
var _ io.ReadWriteCloser = (*NativeTUN)(nil) func (tun *NativeTun) Name() (string, error) {
func (t *NativeTUN) Events() chan TUNEvent { var ifName struct {
return t.events name [16]byte
}
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)
} }
t.rBuf = t.rBuf[:len(to)+4] ifNameSize := uintptr(16)
n, err := t.f.Read(t.rBuf) _, _, errno := unix.Syscall6(
copy(to, t.rBuf[4:]) 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 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 { // reserve space for header
return 0, unix.EIO
}
t.wMu.Lock() buff = buff[offset-4:]
defer t.wMu.Unlock()
if cap(t.wBuf) < len(from)+4 { // add packet information header
t.wBuf = make([]byte, len(from)+4)
}
t.wBuf = t.wBuf[:len(from)+4]
// determine the IP Family for the NULL L2 Header buff[0] = 0x00
buff[1] = 0x00
buff[2] = 0x00
ipVer := from[0] >> 4 if buff[4]>>4 == ipv6.Version {
if ipVer == ipv4.Version { buff[3] = unix.AF_INET6
t.wBuf[3] = unix.AF_INET
} else if ipVer == ipv6.Version {
t.wBuf[3] = unix.AF_INET6
} else { } 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 tun.fd.Write(buff)
return n - 4, err
} }
func (t *NativeTUN) Close() error { func (tun *NativeTun) Close() error {
return tun.fd.Close()
// 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 (t *NativeTUN) Name() string { func (tun *NativeTun) setMTU(n int) error {
return t.name
}
func (t *NativeTUN) setMTU(n int) error {
// open datagram socket // open datagram socket
@ -267,7 +235,7 @@ func (t *NativeTUN) setMTU(n int) error {
// do ioctl call // do ioctl call
var ifr [32]byte var ifr [32]byte
copy(ifr[:], t.name) copy(ifr[:], tun.name)
binary.LittleEndian.PutUint32(ifr[16:20], uint32(n)) binary.LittleEndian.PutUint32(ifr[16:20], uint32(n))
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
@ -277,13 +245,13 @@ func (t *NativeTUN) setMTU(n int) error {
) )
if errno != 0 { 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 return nil
} }
func (t *NativeTUN) MTU() (int, error) { func (tun *NativeTun) MTU() (int, error) {
// open datagram socket // open datagram socket
@ -302,7 +270,7 @@ func (t *NativeTUN) MTU() (int, error) {
// do ioctl call // do ioctl call
var ifr [64]byte var ifr [64]byte
copy(ifr[:], t.name) copy(ifr[:], tun.name)
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
uintptr(fd), uintptr(fd),
@ -310,7 +278,7 @@ func (t *NativeTUN) MTU() (int, error) {
uintptr(unsafe.Pointer(&ifr[0])), uintptr(unsafe.Pointer(&ifr[0])),
) )
if errno != 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 // convert result to signed 32-bit int

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"net" "net"
@ -44,23 +45,11 @@ func (l *UAPIListener) Addr() net.Addr {
return nil 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) listener, err := net.FileListener(file)
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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,6 +62,13 @@ func NewUAPIListener(name string) (net.Listener, error) {
// watch for deletion of socket // watch for deletion of socket
socketPath := path.Join(
socketDirectory,
fmt.Sprintf(socketName, name),
)
// watch for deletion of socket
go func(l *UAPIListener) { go func(l *UAPIListener) {
for ; ; time.Sleep(time.Second) { for ; ; time.Sleep(time.Second) {
if _, err := os.Stat(socketPath); os.IsNotExist(err) { if _, err := os.Stat(socketPath); os.IsNotExist(err) {
@ -97,3 +93,56 @@ func NewUAPIListener(name string) (net.Listener, error) {
return uapi, nil 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)
}
}