From 124eb81ca27a8042f8d588b6abbbf11d806405cb Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 19 May 2016 10:49:07 +0100 Subject: [PATCH] go: vendor shared hvsock/vsock packages These packages will be shared by a number of utilities so vendoring them in a shared place. Signed-off-by: Rolf Neugebauer --- .../github.com/rneugeba/virtsock/go/LICENSE | 71 +++ .../rneugeba/virtsock/go/examples/client.go | 49 +++ .../rneugeba/virtsock/go/examples/hvgoecho.go | 131 ++++++ .../virtsock/go/examples/hvgostress.go | 243 +++++++++++ .../rneugeba/virtsock/go/hvsock/hvsock.go | 412 ++++++++++++++++++ .../virtsock/go/hvsock/hvsock_darwin.go | 69 +++ .../virtsock/go/hvsock/hvsock_linux.go | 147 +++++++ .../virtsock/go/hvsock/hvsock_windows.go | 309 +++++++++++++ .../virtsock/go/hvsock/zsyscall_windows.go | 100 +++++ .../rneugeba/virtsock/go/vsock/vsock_linux.go | 171 ++++++++ alpine/packages/go/vendor/manifest | 14 + 11 files changed, 1716 insertions(+) create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/LICENSE create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/client.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgoecho.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgostress.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_darwin.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_linux.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_windows.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/zsyscall_windows.go create mode 100644 alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/vsock/vsock_linux.go create mode 100644 alpine/packages/go/vendor/manifest diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/LICENSE b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/LICENSE new file mode 100644 index 000000000..a85d08915 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/LICENSE @@ -0,0 +1,71 @@ +Copyright 2016 Rolf Neugebauer + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Some of the code in ./go/hvsock.go, ./go/hvsock_windows.go, and +zsyscall_windows.go is covered by the following licenses: +============================================================================== + +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +and +=== + +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/client.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/client.go new file mode 100644 index 000000000..dd93b3439 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/client.go @@ -0,0 +1,49 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "log" + + "../" +) + +var ( + vmstr string + portstr string +) + +func init() { + flag.StringVar(&vmstr, "vm", "", "Hyper-V VM to connect to") + flag.StringVar(&portstr, "port", "23a432c2-537a-4291-bcb5-d62504644739", "Hyper-V sockets service/port") +} + +func main() { + log.SetFlags(log.LstdFlags) + flag.Parse() + + vmid, err := hvsock.GuidFromString(vmstr) + if err != nil { + log.Fatalln("Failed to parse GUID", vmstr, err) + } + svcid, err := hvsock.GuidFromString(portstr) + if err != nil { + log.Fatalln("Failed to parse GUID", portstr, err) + } + + c, err := hvsock.Dial(hvsock.HypervAddr{VmId: vmid, ServiceId: svcid}) + if err != nil { + log.Fatalln("Failed to Dial:\n", vmstr, portstr, err) + } + + fmt.Println("Send: hello") + l, err := fmt.Fprintf(c, "hello\n") + if err != nil { + log.Fatalln("Failed to send: ", err) + } + fmt.Println("Sent: %s bytes", l) + + message, _ := bufio.NewReader(c).ReadString('\n') + fmt.Println("From SVR: " + message) +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgoecho.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgoecho.go new file mode 100644 index 000000000..f0ab0f4c0 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgoecho.go @@ -0,0 +1,131 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "log" + "net" + "strings" + + "../hvsock" +) + +var ( + clientStr string + serverMode bool + + svcid, _ = hvsock.GuidFromString("3049197C-9A4E-4FBF-9367-97F792F16994") +) + +func init() { + flag.StringVar(&clientStr, "c", "", "Client") + flag.BoolVar(&serverMode, "s", false, "Start as a Server") +} + +func server() { + l, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid}) + if err != nil { + log.Fatalln("Listen():", err) + } + defer func() { + l.Close() + }() + + for { + conn, err := l.Accept() + if err != nil { + log.Fatalln("Accept(): ", err) + } + fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr()) + + go handleRequest(conn) + } +} + +func handleRequest(c net.Conn) { + defer func() { + fmt.Printf("Closing\n") + err := c.Close() + if err != nil { + log.Fatalln("Close():", err) + } + }() + + n, err := io.Copy(c, c) + if err != nil { + log.Fatalln("Copy():", err) + } + fmt.Printf("Copied Bytes: %d\n", n) + + fmt.Printf("Sending BYE message\n") + // The '\n' is important as the client use ReadString() + _, err = fmt.Fprintf(c, "Got %d bytes. Bye\n", n) + if err != nil { + log.Fatalln("Failed to send: ", err) + } + fmt.Printf("Sent bye\n") +} + +func client(vmid hvsock.GUID) { + sa := hvsock.HypervAddr{VmId: vmid, ServiceId: svcid} + c, err := hvsock.Dial(sa) + if err != nil { + log.Fatalln("Failed to Dial:\n", sa.VmId.String(), sa.ServiceId.String(), err) + } + + defer func() { + fmt.Printf("Closing\n") + c.Close() + }() + + fmt.Printf("Send: hello\n") + // Note the '\n' is significant as we use ReadString below + l, err := fmt.Fprintf(c, "hello\n") + if err != nil { + log.Fatalln("Failed to send: ", err) + } + fmt.Printf("Sent: %d bytes\n", l) + + message, err := bufio.NewReader(c).ReadString('\n') + if err != nil { + log.Fatalln("Failed to receive: ", err) + } + fmt.Printf("From SVR: %s", message) + + fmt.Printf("CloseWrite()\n") + c.CloseWrite() + + fmt.Printf("Waiting for Bye message\n") + message, err = bufio.NewReader(c).ReadString('\n') + if err != nil { + log.Fatalln("Failed to receive: ", err) + } + fmt.Printf("From SVR: %s", message) +} + +func main() { + log.SetFlags(log.LstdFlags) + flag.Parse() + + if serverMode { + fmt.Printf("Starting server\n") + server() + } + + vmid := hvsock.GUID_ZERO + var err error + if strings.Contains(clientStr, "-") { + vmid, err = hvsock.GuidFromString(clientStr) + if err != nil { + log.Fatalln("Can't parse GUID: ", clientStr) + } + } else if clientStr == "parent" { + vmid = hvsock.GUID_PARENT + } else { + vmid = hvsock.GUID_LOOPBACK + } + fmt.Printf("Client connecting to %s", vmid.String()) + client(vmid) +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgostress.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgostress.go new file mode 100644 index 000000000..ddb66dc71 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/examples/hvgostress.go @@ -0,0 +1,243 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "log" + "net" + "strings" + "sync" + "time" + + "crypto/md5" + "math/rand" + "sync/atomic" + + "../hvsock" +) + +var ( + clientStr string + serverMode bool + maxDataLen int + connections int + sleepTime int + verbose int + exitOnError bool + parallel int + svcid, _ = hvsock.GuidFromString("3049197C-9A4E-4FBF-9367-97F792F16994") + + connCounter int32 +) + +func init() { + flag.StringVar(&clientStr, "c", "", "Client") + flag.BoolVar(&serverMode, "s", false, "Start as a Server") + flag.IntVar(&maxDataLen, "l", 64*1024, "Maximum Length of data") + flag.IntVar(&connections, "i", 100, "Total number of connections") + flag.IntVar(&sleepTime, "w", 0, "Sleep time in seconds between new connections") + flag.IntVar(¶llel, "p", 1, "Run n connections in parallel") + flag.BoolVar(&exitOnError, "e", false, "Exit when an error occurs") + flag.IntVar(&verbose, "v", 0, "Set the verbosity level") + + rand.Seed(time.Now().UnixNano()) +} + +func main() { + log.SetFlags(log.LstdFlags) + flag.Parse() + + if verbose > 2 { + hvsock.Debug = true + } + + if serverMode { + fmt.Printf("Starting server\n") + server() + return + } + + // Client mode + vmid := hvsock.GUID_ZERO + var err error + if strings.Contains(clientStr, "-") { + vmid, err = hvsock.GuidFromString(clientStr) + if err != nil { + log.Fatalln("Can't parse GUID: ", clientStr) + } + } else if clientStr == "parent" { + vmid = hvsock.GUID_PARENT + } else { + vmid = hvsock.GUID_LOOPBACK + } + + if parallel <= 1 { + // No parallelism, run in the main thread. + fmt.Printf("Client connecting to %s\n", vmid.String()) + for i := 0; i < connections; i++ { + client(vmid, i) + time.Sleep(time.Duration(sleepTime) * time.Second) + } + return + } + + // Parallel clients + var wg sync.WaitGroup + for i := 0; i < parallel; i++ { + wg.Add(1) + go parClient(&wg, vmid) + } + wg.Wait() +} + +func server() { + l, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid}) + if err != nil { + log.Fatalln("Listen():", err) + } + defer func() { + l.Close() + }() + + connid := 0 + + for { + conn, err := l.Accept() + if err != nil { + log.Fatalf("Accept(): %s\n", err) + } + + prDebug("[%05d] accept(): %s -> %s \n", connid, conn.RemoteAddr(), conn.LocalAddr()) + go handleRequest(conn, connid) + connid++ + } +} + +func handleRequest(c net.Conn, connid int) { + defer func() { + prDebug("[%05d] Closing\n", connid) + err := c.Close() + if err != nil { + prError("[%05d] Close(): %s\n", connid, err) + } + }() + + n, err := io.Copy(c, c) + if err != nil { + prError("[%05d] Copy(): %s", connid, err) + return + } + prInfo("[%05d] Copied Bytes: %d\n", connid, n) + + if n == 0 { + return + } + + prDebug("[%05d] Sending BYE message\n", connid) + + // The '\n' is important as the client use ReadString() + _, err = fmt.Fprintf(c, "Got %d bytes. Bye\n", n) + if err != nil { + prError("[%05d] Failed to send: %s", connid, err) + return + } + prDebug("[%05d] Sent bye\n", connid) +} + +func parClient(wg *sync.WaitGroup, vmid hvsock.GUID) { + connid := int(atomic.AddInt32(&connCounter, 1)) + for connid < connections { + client(vmid, connid) + connid = int(atomic.AddInt32(&connCounter, 1)) + time.Sleep(time.Duration(sleepTime) * time.Second) + } + + wg.Done() +} + +func client(vmid hvsock.GUID, conid int) { + sa := hvsock.HypervAddr{VmId: vmid, ServiceId: svcid} + c, err := hvsock.Dial(sa) + if err != nil { + prError("[%05d] Failed to Dial: %s:%s %s\n", conid, sa.VmId.String(), sa.ServiceId.String(), err) + } + + defer c.Close() + + // Create buffer with random data and random length. + // Make sure the buffer is not zero-length + buflen := rand.Intn(maxDataLen-1) + 1 + txbuf := randBuf(buflen) + csum0 := md5.Sum(txbuf) + + prDebug("[%05d] TX: %d bytes, md5=%02x\n", conid, buflen, csum0) + + w := make(chan int) + go func() { + l, err := c.Write(txbuf) + if err != nil { + prError("[%05d] Failed to send: %s\n", conid, err) + } + if l != buflen { + prError("[%05d] Failed to send enough data: %d\n", conid, l) + } + + // Tell the other end that we are done + c.CloseWrite() + + w <- l + }() + + rxbuf := make([]byte, buflen) + + n, err := io.ReadFull(bufio.NewReader(c), rxbuf) + if err != nil { + prError("[%05d] Failed to receive: %s\n", conid, err) + return + } + csum1 := md5.Sum(rxbuf) + + totalSent := <-w + + prInfo("[%05d] RX: %d bytes, md5=%02x (sent=%d)\n", conid, n, csum1, totalSent) + if csum0 != csum1 { + prError("[%05d] Checksums don't match", conid) + } + + // Wait for Bye message + message, err := bufio.NewReader(c).ReadString('\n') + if err != nil { + prError("[%05d] Failed to receive bye: %s\n", conid, err) + } + prDebug("[%05d] From SVR: %s", conid, message) +} + +func randBuf(n int) []byte { + b := make([]byte, n) + for i := range b { + b[i] = byte(rand.Intn(255)) + } + return b +} + +func prError(format string, args ...interface{}) { + if exitOnError { + log.Fatalf(format, args...) + } else { + log.Printf(format, args...) + } +} + +func prInfo(format string, args ...interface{}) { + if verbose > 0 { + log.Printf(format, args...) + } +} + +func prDebug(format string, args ...interface{}) { + if verbose > 1 { + log.Printf(format, args...) + } +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock.go new file mode 100644 index 000000000..d8d7a49b3 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock.go @@ -0,0 +1,412 @@ +package hvsock + +import ( + "errors" + "fmt" + "io" + "log" + "net" + "sync" + "syscall" + + "encoding/binary" +) + +// This package provides a Go interface to Hyper-V sockets both on +// Windows and on Linux (assuming the appropriate Linux kernel patches +// have been applied). +// +// Unfortunately, it is not easy/possible to extend the existing Go +// socket implementations with new Address Families, so this module +// wraps directly around system calls (and handles Windows' +// asynchronous system calls). +// +// There is an additional wrinkle. Hyper-V sockets in currently +// shipping versions of Windows don't support graceful and/or +// unidirectional shutdown(). So we turn a stream based protocol into +// message based protocol which allows to send in-line "messages" to +// the other end. We then provide a stream based interface on top of +// that. Yuk. +// +// The message interface is pretty simple. We first send a 32bit +// message containing the size of the data in the following +// message. Messages are limited to 'maxmsgsize'. Special message +// (without data), `shutdownrd` and 'shutdownwr' are used to used to +// signal a shutdown to the other end. + +const ( + maxMsgSize = 32 * 1024 // Maximum message size +) + +// Hypper-V sockets use GUIDs for addresses and "ports" +type GUID [16]byte + +// Convert a GUID into a string +func (g *GUID) String() string { + /* XXX This assume little endian */ + return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + g[3], g[2], g[1], g[0], + g[5], g[4], + g[7], g[6], + g[8], g[9], + g[10], g[11], g[12], g[13], g[14], g[15]) +} + +// Parse a GUID string +func GuidFromString(s string) (GUID, error) { + var g GUID + var err error + _, err = fmt.Sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &g[3], &g[2], &g[1], &g[0], + &g[5], &g[4], + &g[7], &g[6], + &g[8], &g[9], + &g[10], &g[11], &g[12], &g[13], &g[14], &g[15]) + return g, err +} + +type HypervAddr struct { + VmId GUID + ServiceId GUID +} + +func (a HypervAddr) Network() string { return "hvsock" } + +func (a HypervAddr) String() string { + vmid := a.VmId.String() + svc := a.ServiceId.String() + + return vmid + ":" + svc +} + +var ( + Debug = false // Set to True to enable additional debug output + + GUID_ZERO, _ = GuidFromString("00000000-0000-0000-0000-000000000000") + GUID_WILDCARD, _ = GuidFromString("00000000-0000-0000-0000-000000000000") + GUID_BROADCAST, _ = GuidFromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") + GUID_CHILDREN, _ = GuidFromString("90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd") + GUID_LOOPBACK, _ = GuidFromString("e0e16197-dd56-4a10-9195-5ee7a155a838") + GUID_PARENT, _ = GuidFromString("a42e7cda-d03f-480c-9cc2-a4de20abb878") +) + +func Dial(raddr HypervAddr) (Conn, error) { + fd, err := syscall.Socket(AF_HYPERV, syscall.SOCK_STREAM, SHV_PROTO_RAW) + if err != nil { + return nil, err + } + + err = connect(fd, &raddr) + if err != nil { + return nil, err + } + + v, err := newHVsockConn(fd, HypervAddr{VmId: GUID_ZERO, ServiceId: GUID_ZERO}, raddr) + if err != nil { + return nil, err + } + v.wrlock = &sync.Mutex{} + return v, nil +} + +func Listen(addr HypervAddr) (net.Listener, error) { + + accept_fd, err := syscall.Socket(AF_HYPERV, syscall.SOCK_STREAM, SHV_PROTO_RAW) + if err != nil { + return nil, err + } + + err = bind(accept_fd, addr) + if err != nil { + return nil, err + } + + err = syscall.Listen(accept_fd, syscall.SOMAXCONN) + if err != nil { + return nil, err + } + + return &hvsockListener{accept_fd, addr}, nil +} + +const ( + shutdownrd = 0xdeadbeef // Message for CloseRead() + shutdownwr = 0xbeefdead // Message for CloseWrite() + closemsg = 0xdeaddead // Message for Close() +) + +// Conn is a hvsock connection which support half-close. +type Conn interface { + net.Conn + CloseRead() error + CloseWrite() error +} + +func (v *hvsockListener) Accept() (net.Conn, error) { + var raddr HypervAddr + fd, err := accept(v.accept_fd, &raddr) + if err != nil { + return nil, err + } + + a, err := newHVsockConn(fd, v.laddr, raddr) + if err != nil { + return nil, err + } + a.wrlock = &sync.Mutex{} + return a, nil +} + +func (v *hvsockListener) Close() error { + // Note this won't cause the Accept to unblock. + return syscall.Close(v.accept_fd) +} + +func (v *hvsockListener) Addr() net.Addr { + return HypervAddr{VmId: v.laddr.VmId, ServiceId: v.laddr.ServiceId} +} + +/* + * A wrapper around FileConn which supports CloseRead and CloseWrite + */ + +var ( + errSocketClosed = errors.New("HvSocket has already been closed") + errSocketWriteClosed = errors.New("HvSocket has been closed for write") + errSocketReadClosed = errors.New("HvSocket has been closed for read") + errSocketMsgSize = errors.New("HvSocket message was of wrong size") + errSocketMsgWrite = errors.New("HvSocket writing message") + errSocketNotEnoughData = errors.New("HvSocket not enough data written") + errSocketUnImplemented = errors.New("Function not implemented") +) + +type HVsockConn struct { + hvsockConn + + wrlock *sync.Mutex + + writeClosed bool + readClosed bool + + bytesToRead int +} + +func (v *HVsockConn) LocalAddr() net.Addr { + return v.local +} + +func (v *HVsockConn) RemoteAddr() net.Addr { + return v.remote +} + +func (v *HVsockConn) Close() error { + prDebug("Close\n") + + v.readClosed = true + v.writeClosed = true + + prDebug("TX: Close\n") + v.wrlock.Lock() + err := v.sendMsg(closemsg) + v.wrlock.Unlock() + if err != nil { + // chances are that the other end beat us to the close + prDebug("Mmmm. %s\n", err) + return v.close() + } + + // wait for reply/ignore errors + // we may get a EOF because the other end closed, + b := make([]byte, 4) + _, _ = v.read(b) + prDebug("close\n") + return v.close() +} + +func (v *HVsockConn) CloseRead() error { + if v.readClosed { + return errSocketReadClosed + } + + prDebug("TX: Shutdown Read\n") + v.wrlock.Lock() + err := v.sendMsg(shutdownrd) + v.wrlock.Unlock() + if err != nil { + return err + } + + v.readClosed = true + return nil +} + +func (v *HVsockConn) CloseWrite() error { + if v.writeClosed { + return errSocketWriteClosed + } + + prDebug("TX: Shutdown Write\n") + v.wrlock.Lock() + err := v.sendMsg(shutdownwr) + v.wrlock.Unlock() + if err != nil { + return err + } + + v.writeClosed = true + return nil +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// Read into buffer. This function turns a stream interface into +// messages and also handles the inband control messages. +func (v *HVsockConn) Read(buf []byte) (int, error) { + if v.readClosed { + return 0, io.EOF + } + + if v.bytesToRead == 0 { + for { + // wait for next message + b := make([]byte, 4) + + n, err := v.read(b) + if err != nil { + return 0, err + } + + if n != 4 { + return n, errSocketMsgSize + } + + msg := int(binary.LittleEndian.Uint32(b)) + if msg == shutdownwr { + // The other end shutdown write. No point reading more + v.readClosed = true + prDebug("RX: ShutdownWrite\n") + return 0, io.EOF + } else if msg == shutdownrd { + // The other end shutdown read. No point writing more + v.writeClosed = true + prDebug("RX: ShutdownRead\n") + } else if msg == closemsg { + // Setting write close here forces a proper close + v.writeClosed = true + prDebug("RX: Close\n") + v.Close() + } else { + v.bytesToRead = msg + if v.bytesToRead == 0 { + // XXX Something is odd. If I don't have this here, this + // case is hit. However, with this code in place this + // case never get's hit. Suspect overly eager GC... + log.Printf("RX: Zero length %02x", b) + continue + } + break + } + } + } + + // If we get here, we know there is v.bytesToRead worth of + // data coming our way. Read it directly into to buffer passed + // in by the caller making sure we do not read mode than we + // should read by splicing the buffer. + toRead := min(len(buf), v.bytesToRead) + prDebug("READ: %d len=0x%x\n", int(v.fd), toRead) + n, err := v.read(buf[:toRead]) + if err != nil || n == 0 { + v.readClosed = true + return n, err + } + v.bytesToRead -= n + return n, nil +} + +func (v *HVsockConn) Write(buf []byte) (int, error) { + if v.writeClosed { + return 0, errSocketWriteClosed + } + + var err error + toWrite := len(buf) + written := 0 + + prDebug("WRITE: %d Total len=%x\n", int(v.fd), len(buf)) + + for toWrite > 0 { + if v.writeClosed { + return 0, errSocketWriteClosed + } + + // We write batches of MSG + data which need to be + // "atomic". We don't want to hold the lock for the + // entire Write() in case some other threads wants to + // send OOB data, e.g. for closing. + + v.wrlock.Lock() + + thisBatch := min(toWrite, maxMsgSize) + prDebug("WRITE: %d len=%x\n", int(v.fd), thisBatch) + // Write message header + err = v.sendMsg(uint32(thisBatch)) + if err != nil { + prDebug("Write MSG Error: %s\n", err) + goto ErrOut + } + + // Write data + n, err := v.write(buf[written : written+thisBatch]) + if err != nil { + prDebug("Write Error 3\n") + goto ErrOut + } + if n != thisBatch { + prDebug("Write Error 4\n") + err = errSocketNotEnoughData + goto ErrOut + } + toWrite -= n + written += n + v.wrlock.Unlock() + } + + return written, nil + +ErrOut: + v.wrlock.Unlock() + v.writeClosed = true + return 0, err +} + +// hvsockConn, SetDeadline(), SetReadDeadline(), and +// SetWriteDeadline() are OS specific. + +// Send a message to the other end +// The Lock must be held to call this functions +func (v *HVsockConn) sendMsg(msg uint32) error { + b := make([]byte, 4) + + binary.LittleEndian.PutUint32(b, msg) + n, err := v.write(b) + if err != nil { + prDebug("Write Error 1\n") + return err + } + if n != len(b) { + return errSocketMsgWrite + } + return nil +} + +func prDebug(format string, args ...interface{}) { + if Debug { + log.Printf(format, args...) + } +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_darwin.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_darwin.go new file mode 100644 index 000000000..c448fb072 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_darwin.go @@ -0,0 +1,69 @@ +// Dummy implementation to compile on Mac OSX + +package hvsock +import ( + "errors" + "time" +) + +const ( + AF_HYPERV = 42 + SHV_PROTO_RAW = 1 +) + +type hvsockListener struct { + accept_fd int + laddr HypervAddr +} + +// +// System call wrapper +// +func connect(s int, a *HypervAddr) (err error) { + return errors.New("connect() not implemented") +} + +func bind(s int, a HypervAddr) error { + return errors.New("bind() not implemented") +} + +func accept(s int, a *HypervAddr) (int, error) { + return 0, errors.New("accept() not implemented") +} + +// Internal representation. Complex mostly due to asynch send()/recv() syscalls. +type hvsockConn struct { + fd int + local HypervAddr + remote HypervAddr +} + +// Main constructor +func newHVsockConn(fd int, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { + v := &hvsockConn{local: local, remote: remote} + return &HVsockConn{hvsockConn: *v}, errors.New("newHVsockConn() not implemented") +} + +func (v *HVsockConn) close() error { + return errors.New("close() not implemented") +} + +func (v *HVsockConn) read(buf []byte) (int, error) { + return 0, errors.New("read() not implemented") +} + +func (v *HVsockConn) write(buf []byte) (int, error) { + return 0, errors.New("write() not implemented") +} + +func (v *HVsockConn) SetReadDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *HVsockConn) SetWriteDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *HVsockConn) SetDeadline(t time.Time) error { + return nil // FIXME +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_linux.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_linux.go new file mode 100644 index 000000000..53d287c6f --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_linux.go @@ -0,0 +1,147 @@ +package hvsock + +/* +#include + +struct sockaddr_hv { + unsigned short shv_family; + unsigned short reserved; + unsigned char shv_vm_id[16]; + unsigned char shv_service_id[16]; +}; +int bind_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) { + return bind(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv)); +} +int connect_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) { + return connect(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv)); +} +int accept_hv(int fd, struct sockaddr_hv *sa_hv, socklen_t *sa_hv_len) { + return accept4(fd, (struct sockaddr *)sa_hv, sa_hv_len, 0); +} +*/ +import "C" + +import ( + "errors" + "fmt" + "os" + "strconv" + "time" +) + +const ( + AF_HYPERV = 43 + SHV_PROTO_RAW = 1 +) + +type hvsockListener struct { + accept_fd int + laddr HypervAddr +} + +// +// System call wrapper +// +func connect(s int, a *HypervAddr) (err error) { + sa := C.struct_sockaddr_hv{} + sa.shv_family = AF_HYPERV + sa.reserved = 0 + + for i := 0; i < 16; i++ { + sa.shv_vm_id[i] = C.uchar(a.VmId[i]) + } + for i := 0; i < 16; i++ { + sa.shv_service_id[i] = C.uchar(a.ServiceId[i]) + } + + if ret := C.connect_sockaddr_hv(C.int(s), &sa); ret != 0 { + return errors.New("connect() returned " + strconv.Itoa(int(ret))) + } + + return nil +} + +func bind(s int, a HypervAddr) error { + sa := C.struct_sockaddr_hv{} + sa.shv_family = AF_HYPERV + sa.reserved = 0 + + for i := 0; i < 16; i++ { + // XXX this should take the address from `a` but Linux + // currently only support 0s + sa.shv_vm_id[i] = C.uchar(GUID_ZERO[i]) + } + for i := 0; i < 16; i++ { + sa.shv_service_id[i] = C.uchar(a.ServiceId[i]) + } + + if ret := C.bind_sockaddr_hv(C.int(s), &sa); ret != 0 { + return errors.New("bind() returned " + strconv.Itoa(int(ret))) + } + + return nil +} + +func accept(s int, a *HypervAddr) (int, error) { + var accept_sa C.struct_sockaddr_hv + var accept_sa_len C.socklen_t + + accept_sa_len = C.sizeof_struct_sockaddr_hv + fd, err := C.accept_hv(C.int(s), &accept_sa, &accept_sa_len) + if err != nil { + return -1, err + } + + a.VmId = guidFromC(accept_sa.shv_vm_id) + a.ServiceId = guidFromC(accept_sa.shv_service_id) + + return int(fd), nil +} + +// Internal representation. Complex mostly due to asynch send()/recv() syscalls. +type hvsockConn struct { + fd int + hvsock *os.File + local HypervAddr + remote HypervAddr +} + +// Main constructor +func newHVsockConn(fd int, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { + hvsock := os.NewFile(uintptr(fd), fmt.Sprintf("hvsock:%d", fd)) + v := &hvsockConn{fd: fd, hvsock: hvsock, local: local, remote: remote} + + return &HVsockConn{hvsockConn: *v}, nil +} + +func (v *HVsockConn) close() error { + return v.hvsock.Close() +} + +func (v *HVsockConn) read(buf []byte) (int, error) { + return v.hvsock.Read(buf) +} + +func (v *HVsockConn) write(buf []byte) (int, error) { + return v.hvsock.Write(buf) +} + +func (v *HVsockConn) SetReadDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *HVsockConn) SetWriteDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *HVsockConn) SetDeadline(t time.Time) error { + return nil // FIXME +} + +func guidFromC(cg [16]C.uchar) GUID { + var g GUID + for i := 0; i < 16; i++ { + g[i] = byte(cg[i]) + } + return g +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_windows.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_windows.go new file mode 100644 index 000000000..973f3ff16 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/hvsock_windows.go @@ -0,0 +1,309 @@ +package hvsock + +import ( + "errors" + "io" + "log" + "sync" + "syscall" + "time" + "unsafe" +) + +// Make sure Winsock2 is initialised +func init() { + e := syscall.WSAStartup(uint32(0x202), &wsaData) + if e != nil { + log.Fatal("WSAStartup", e) + } +} + +const ( + AF_HYPERV = 34 + SHV_PROTO_RAW = 1 + socket_error = uintptr(^uint32(0)) +) + +// struck sockaddr equivalent +type rawSockaddrHyperv struct { + Family uint16 + Reserved uint16 + VmId GUID + ServiceId GUID +} + +type hvsockListener struct { + accept_fd syscall.Handle + laddr HypervAddr +} + +// Internal representation. Complex mostly due to asynch send()/recv() syscalls. +type hvsockConn struct { + fd syscall.Handle + local HypervAddr + remote HypervAddr + + wg sync.WaitGroup + closing bool + readDeadline time.Time + writeDeadline time.Time +} + +// Used for async system calls +const ( + cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 + cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 +) + +var ( + errTimeout = &timeoutError{} + + wsaData syscall.WSAData +) + +type timeoutError struct{} + +func (e *timeoutError) Error() string { return "i/o timeout" } +func (e *timeoutError) Timeout() bool { return true } +func (e *timeoutError) Temporary() bool { return true } + +// Main constructor +func newHVsockConn(h syscall.Handle, local HypervAddr, remote HypervAddr) (*HVsockConn, error) { + ioInitOnce.Do(initIo) + v := &hvsockConn{fd: h, local: local, remote: remote} + + _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) + if err != nil { + return nil, err + } + err = setFileCompletionNotificationModes(h, + cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) + if err != nil { + return nil, err + } + + return &HVsockConn{hvsockConn: *v}, nil +} + +// Utility function to build a struct sockaddr for syscalls. +func (a HypervAddr) sockaddr(sa *rawSockaddrHyperv) (unsafe.Pointer, int32, error) { + sa.Family = AF_HYPERV + sa.Reserved = 0 + for i := 0; i < len(sa.VmId); i++ { + sa.VmId[i] = a.VmId[i] + } + for i := 0; i < len(sa.ServiceId); i++ { + sa.ServiceId[i] = a.ServiceId[i] + } + + return unsafe.Pointer(sa), int32(unsafe.Sizeof(*sa)), nil +} + +func connect(s syscall.Handle, a *HypervAddr) (err error) { + var sa rawSockaddrHyperv + ptr, n, err := a.sockaddr(&sa) + if err != nil { + return err + } + + return sys_connect(s, ptr, n) +} + +func bind(s syscall.Handle, a HypervAddr) error { + var sa rawSockaddrHyperv + ptr, n, err := a.sockaddr(&sa) + if err != nil { + return err + } + + return sys_bind(s, ptr, n) +} + +func accept(s syscall.Handle, a *HypervAddr) (syscall.Handle, error) { + return 0, errors.New("accept(): Unimplemented") +} + +// +// File IO/Socket interface +// +func (s *HVsockConn) close() error { + s.closeHandle() + + return nil +} + +// Underlying raw read() function. +func (v *HVsockConn) read(buf []byte) (int, error) { + var b syscall.WSABuf + var bytes uint32 + var f uint32 + + b.Len = uint32(len(buf)) + b.Buf = &buf[0] + + c, err := v.prepareIo() + if err != nil { + return 0, err + } + + err = syscall.WSARecv(v.fd, &b, 1, &bytes, &f, &c.o, nil) + n, err := v.asyncIo(c, v.readDeadline, bytes, err) + + // Handle EOF conditions. + if err == nil && n == 0 && len(buf) != 0 { + return 0, io.EOF + } + if err == syscall.ERROR_BROKEN_PIPE { + return 0, io.EOF + } + + return n, err +} + +// Underlying raw write() function. +func (v *HVsockConn) write(buf []byte) (int, error) { + var b syscall.WSABuf + var f uint32 + var bytes uint32 + + if len(buf) == 0 { + return 0, nil + } + + f = 0 + b.Len = uint32(len(buf)) + b.Buf = &buf[0] + + c, err := v.prepareIo() + if err != nil { + return 0, err + } + err = syscall.WSASend(v.fd, &b, 1, &bytes, f, &c.o, nil) + return v.asyncIo(c, v.writeDeadline, bytes, err) +} + +func (v *HVsockConn) SetReadDeadline(t time.Time) error { + v.readDeadline = t + return nil +} + +func (v *HVsockConn) SetWriteDeadline(t time.Time) error { + v.writeDeadline = t + return nil +} + +func (v *HVsockConn) SetDeadline(t time.Time) error { + v.SetReadDeadline(t) + v.SetWriteDeadline(t) + return nil +} + +// The code below here is adjusted from: +// https://github.com/Microsoft/go-winio/blob/master/file.go + +var ioInitOnce sync.Once +var ioCompletionPort syscall.Handle + +// ioResult contains the result of an asynchronous IO operation +type ioResult struct { + bytes uint32 + err error +} + +type ioOperation struct { + o syscall.Overlapped + ch chan ioResult +} + +func initIo() { + h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) + if err != nil { + panic(err) + } + ioCompletionPort = h + go ioCompletionProcessor(h) +} + +func (v *hvsockConn) closeHandle() { + if !v.closing { + // cancel all IO and wait for it to complete + v.closing = true + cancelIoEx(v.fd, nil) + v.wg.Wait() + // at this point, no new IO can start + syscall.Close(v.fd) + v.fd = 0 + } +} + +// prepareIo prepares for a new IO operation +func (s *hvsockConn) prepareIo() (*ioOperation, error) { + s.wg.Add(1) + if s.closing { + return nil, errSocketClosed + } + c := &ioOperation{} + c.ch = make(chan ioResult) + return c, nil +} + +// ioCompletionProcessor processes completed async IOs forever +func ioCompletionProcessor(h syscall.Handle) { + // Set the timer resolution to 1. This fixes a performance regression in golang 1.6. + timeBeginPeriod(1) + for { + var bytes uint32 + var key uintptr + var op *ioOperation + err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE) + if op == nil { + panic(err) + } + op.ch <- ioResult{bytes, err} + } +} + +// asyncIo processes the return value from ReadFile or WriteFile, blocking until +// the operation has actually completed. +func (v *hvsockConn) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) { + if err != syscall.ERROR_IO_PENDING { + v.wg.Done() + return int(bytes), err + } + + var r ioResult + wait := true + timedout := false + if v.closing { + cancelIoEx(v.fd, &c.o) + } else if !deadline.IsZero() { + now := time.Now() + if !deadline.After(now) { + timedout = true + } else { + timeout := time.After(deadline.Sub(now)) + select { + case r = <-c.ch: + wait = false + case <-timeout: + timedout = true + } + } + } + if timedout { + cancelIoEx(v.fd, &c.o) + } + if wait { + r = <-c.ch + } + err = r.err + if err == syscall.ERROR_OPERATION_ABORTED { + if v.closing { + err = errSocketClosed + } else if timedout { + err = errTimeout + } + } + v.wg.Done() + return int(r.bytes), err +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/zsyscall_windows.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/zsyscall_windows.go new file mode 100644 index 000000000..32fb2f253 --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/hvsock/zsyscall_windows.go @@ -0,0 +1,100 @@ +package hvsock + +import ( + "syscall" + "unsafe" +) + +var ( + modws2_32 = syscall.NewLazyDLL("ws2_32.dll") + modwinmm = syscall.NewLazyDLL("winmm.dll") + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procConnect = modws2_32.NewProc("connect") + procbind = modws2_32.NewProc("bind") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") + proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod") +) + +func sys_connect(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.Syscall(procConnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socket_error { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + + return +} + +func sys_bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socket_error { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) + newport = syscall.Handle(r0) + if newport == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { + r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func timeBeginPeriod(period uint32) (n int32) { + r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0) + n = int32(r0) + return +} diff --git a/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/vsock/vsock_linux.go b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/vsock/vsock_linux.go new file mode 100644 index 000000000..f8c38e00a --- /dev/null +++ b/alpine/packages/go/vendor/github.com/rneugeba/virtsock/go/vsock/vsock_linux.go @@ -0,0 +1,171 @@ +package vsock + +import ( + "errors" + "fmt" + "net" + "os" + "syscall" + "time" +) + +/* No way to teach net or syscall about vsock sockaddr, so go right to C */ + +/* +#include + +struct sockaddr_vm { + sa_family_t svm_family; + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + unsigned char svm_zero[sizeof(struct sockaddr) - + sizeof(sa_family_t) - sizeof(unsigned short) - + sizeof(unsigned int) - sizeof(unsigned int)]; +}; + +int bind_sockaddr_vm(int fd, const struct sockaddr_vm *sa_vm) { + return bind(fd, (const struct sockaddr*)sa_vm, sizeof(*sa_vm)); +} +int connect_sockaddr_vm(int fd, const struct sockaddr_vm *sa_vm) { + return connect(fd, (const struct sockaddr*)sa_vm, sizeof(*sa_vm)); +} +int accept_vm(int fd, struct sockaddr_vm *sa_vm, socklen_t *sa_vm_len) { + return accept4(fd, (struct sockaddr *)sa_vm, sa_vm_len, 0); +} +*/ +import "C" + +const ( + AF_VSOCK = 40 + VSOCK_CID_ANY = 4294967295 /* 2^32-1 */ + VSOCK_CID_SELF = 3 +) + +// Listen returns a net.Listener which can accept connections on the given +// vhan port. +func Listen(port uint) (net.Listener, error) { + accept_fd, err := syscall.Socket(AF_VSOCK, syscall.SOCK_STREAM, 0) + if err != nil { + return nil, err + } + + sa := C.struct_sockaddr_vm{} + sa.svm_family = AF_VSOCK + sa.svm_port = C.uint(port) + sa.svm_cid = VSOCK_CID_ANY + + if ret := C.bind_sockaddr_vm(C.int(accept_fd), &sa); ret != 0 { + return nil, errors.New(fmt.Sprintf("failed bind vsock connection to %08x.%08x, returned %d", sa.svm_cid, sa.svm_port, ret)) + } + + err = syscall.Listen(accept_fd, syscall.SOMAXCONN) + if err != nil { + return nil, err + } + return &vsockListener{accept_fd, port}, nil +} + +// Conn is a vsock connection which support half-close. +type Conn interface { + net.Conn + CloseRead() error + CloseWrite() error +} + +type vsockListener struct { + accept_fd int + port uint +} + +func (v *vsockListener) Accept() (net.Conn, error) { + var accept_sa C.struct_sockaddr_vm + var accept_sa_len C.socklen_t + + accept_sa_len = C.sizeof_struct_sockaddr_vm + fd, err := C.accept_vm(C.int(v.accept_fd), &accept_sa, &accept_sa_len) + if err != nil { + return nil, err + } + return newVsockConn(uintptr(fd), v.port) +} + +func (v *vsockListener) Close() error { + // Note this won't cause the Accept to unblock. + return syscall.Close(v.accept_fd) +} + +type VsockAddr struct { + Port uint +} + +func (a VsockAddr) Network() string { + return "vsock" +} + +func (a VsockAddr) String() string { + return fmt.Sprintf("%08x", a.Port) +} + +func (v *vsockListener) Addr() net.Addr { + return VsockAddr{Port: v.port} +} + +// a wrapper around FileConn which supports CloseRead and CloseWrite +type vsockConn struct { + vsock *os.File + fd uintptr + local VsockAddr + remote VsockAddr +} + +type VsockConn struct { + vsockConn +} + +func newVsockConn(fd uintptr, localPort uint) (*VsockConn, error) { + vsock := os.NewFile(fd, fmt.Sprintf("vsock:%d", fd)) + local := VsockAddr{Port: localPort} + remote := VsockAddr{Port: uint(0)} // FIXME + return &VsockConn{vsockConn{vsock: vsock, fd: fd, local: local, remote: remote}}, nil +} + +func (v *VsockConn) LocalAddr() net.Addr { + return v.local +} + +func (v *VsockConn) RemoteAddr() net.Addr { + return v.remote +} + +func (v *VsockConn) CloseRead() error { + return syscall.Shutdown(int(v.fd), syscall.SHUT_RD) +} + +func (v *VsockConn) CloseWrite() error { + return syscall.Shutdown(int(v.fd), syscall.SHUT_WR) +} + +func (v *VsockConn) Close() error { + return v.vsock.Close() +} + +func (v *VsockConn) Read(buf []byte) (int, error) { + return v.vsock.Read(buf) +} + +func (v *VsockConn) Write(buf []byte) (int, error) { + return v.vsock.Write(buf) +} + +func (v *VsockConn) SetDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *VsockConn) SetReadDeadline(t time.Time) error { + return nil // FIXME +} + +func (v *VsockConn) SetWriteDeadline(t time.Time) error { + return nil // FIXME +} diff --git a/alpine/packages/go/vendor/manifest b/alpine/packages/go/vendor/manifest new file mode 100644 index 000000000..2c83cb3d2 --- /dev/null +++ b/alpine/packages/go/vendor/manifest @@ -0,0 +1,14 @@ +{ + "version": 0, + "dependencies": [ + { + "importpath": "github.com/rneugeba/virtsock/go", + "repository": "https://github.com/rneugeba/virtsock", + "vcs": "git", + "revision": "359bc27daab86588bfc7a304e78c6758b098d8e5", + "branch": "master", + "path": "/go", + "notests": true + } + ] +} \ No newline at end of file