mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 18:23:25 +00:00
efc99a068c
Several dependencies can't be updated due to breakages (etcd and grpc for example). Also updated ttlmap usage since their API changed.
306 lines
7.4 KiB
Go
306 lines
7.4 KiB
Go
package winio
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/Microsoft/go-winio/pkg/guid"
|
|
)
|
|
|
|
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
|
|
|
const (
|
|
afHvSock = 34 // AF_HYPERV
|
|
|
|
socketError = ^uintptr(0)
|
|
)
|
|
|
|
// An HvsockAddr is an address for a AF_HYPERV socket.
|
|
type HvsockAddr struct {
|
|
VMID guid.GUID
|
|
ServiceID guid.GUID
|
|
}
|
|
|
|
type rawHvsockAddr struct {
|
|
Family uint16
|
|
_ uint16
|
|
VMID guid.GUID
|
|
ServiceID guid.GUID
|
|
}
|
|
|
|
// Network returns the address's network name, "hvsock".
|
|
func (addr *HvsockAddr) Network() string {
|
|
return "hvsock"
|
|
}
|
|
|
|
func (addr *HvsockAddr) String() string {
|
|
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
|
|
}
|
|
|
|
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
|
func VsockServiceID(port uint32) guid.GUID {
|
|
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
|
|
g.Data1 = port
|
|
return g
|
|
}
|
|
|
|
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
|
return rawHvsockAddr{
|
|
Family: afHvSock,
|
|
VMID: addr.VMID,
|
|
ServiceID: addr.ServiceID,
|
|
}
|
|
}
|
|
|
|
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
|
|
addr.VMID = raw.VMID
|
|
addr.ServiceID = raw.ServiceID
|
|
}
|
|
|
|
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
|
type HvsockListener struct {
|
|
sock *win32File
|
|
addr HvsockAddr
|
|
}
|
|
|
|
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
|
type HvsockConn struct {
|
|
sock *win32File
|
|
local, remote HvsockAddr
|
|
}
|
|
|
|
func newHvSocket() (*win32File, error) {
|
|
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
|
|
if err != nil {
|
|
return nil, os.NewSyscallError("socket", err)
|
|
}
|
|
f, err := makeWin32File(fd)
|
|
if err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
f.socket = true
|
|
return f, nil
|
|
}
|
|
|
|
// ListenHvsock listens for connections on the specified hvsock address.
|
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
|
l := &HvsockListener{addr: *addr}
|
|
sock, err := newHvSocket()
|
|
if err != nil {
|
|
return nil, l.opErr("listen", err)
|
|
}
|
|
sa := addr.raw()
|
|
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
|
|
if err != nil {
|
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
|
}
|
|
err = syscall.Listen(sock.handle, 16)
|
|
if err != nil {
|
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
|
}
|
|
return &HvsockListener{sock: sock, addr: *addr}, nil
|
|
}
|
|
|
|
func (l *HvsockListener) opErr(op string, err error) error {
|
|
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
|
|
}
|
|
|
|
// Addr returns the listener's network address.
|
|
func (l *HvsockListener) Addr() net.Addr {
|
|
return &l.addr
|
|
}
|
|
|
|
// Accept waits for the next connection and returns it.
|
|
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|
sock, err := newHvSocket()
|
|
if err != nil {
|
|
return nil, l.opErr("accept", err)
|
|
}
|
|
defer func() {
|
|
if sock != nil {
|
|
sock.Close()
|
|
}
|
|
}()
|
|
c, err := l.sock.prepareIo()
|
|
if err != nil {
|
|
return nil, l.opErr("accept", err)
|
|
}
|
|
defer l.sock.wg.Done()
|
|
|
|
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
|
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
|
var addrbuf [addrlen * 2]byte
|
|
|
|
var bytes uint32
|
|
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
|
|
_, err = l.sock.asyncIo(c, nil, bytes, err)
|
|
if err != nil {
|
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
|
}
|
|
conn := &HvsockConn{
|
|
sock: sock,
|
|
}
|
|
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
|
sock = nil
|
|
return conn, nil
|
|
}
|
|
|
|
// Close closes the listener, causing any pending Accept calls to fail.
|
|
func (l *HvsockListener) Close() error {
|
|
return l.sock.Close()
|
|
}
|
|
|
|
/* Need to finish ConnectEx handling
|
|
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
|
|
sock, err := newHvSocket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if sock != nil {
|
|
sock.Close()
|
|
}
|
|
}()
|
|
c, err := sock.prepareIo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer sock.wg.Done()
|
|
var bytes uint32
|
|
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
|
|
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn := &HvsockConn{
|
|
sock: sock,
|
|
remote: *addr,
|
|
}
|
|
sock = nil
|
|
return conn, nil
|
|
}
|
|
*/
|
|
|
|
func (conn *HvsockConn) opErr(op string, err error) error {
|
|
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
|
}
|
|
|
|
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
|
c, err := conn.sock.prepareIo()
|
|
if err != nil {
|
|
return 0, conn.opErr("read", err)
|
|
}
|
|
defer conn.sock.wg.Done()
|
|
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
|
var flags, bytes uint32
|
|
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
|
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
|
|
if err != nil {
|
|
if _, ok := err.(syscall.Errno); ok {
|
|
err = os.NewSyscallError("wsarecv", err)
|
|
}
|
|
return 0, conn.opErr("read", err)
|
|
} else if n == 0 {
|
|
err = io.EOF
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func (conn *HvsockConn) Write(b []byte) (int, error) {
|
|
t := 0
|
|
for len(b) != 0 {
|
|
n, err := conn.write(b)
|
|
if err != nil {
|
|
return t + n, err
|
|
}
|
|
t += n
|
|
b = b[n:]
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func (conn *HvsockConn) write(b []byte) (int, error) {
|
|
c, err := conn.sock.prepareIo()
|
|
if err != nil {
|
|
return 0, conn.opErr("write", err)
|
|
}
|
|
defer conn.sock.wg.Done()
|
|
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
|
var bytes uint32
|
|
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
|
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
|
|
if err != nil {
|
|
if _, ok := err.(syscall.Errno); ok {
|
|
err = os.NewSyscallError("wsasend", err)
|
|
}
|
|
return 0, conn.opErr("write", err)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// Close closes the socket connection, failing any pending read or write calls.
|
|
func (conn *HvsockConn) Close() error {
|
|
return conn.sock.Close()
|
|
}
|
|
|
|
func (conn *HvsockConn) shutdown(how int) error {
|
|
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
|
|
if err != nil {
|
|
return os.NewSyscallError("shutdown", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CloseRead shuts down the read end of the socket.
|
|
func (conn *HvsockConn) CloseRead() error {
|
|
err := conn.shutdown(syscall.SHUT_RD)
|
|
if err != nil {
|
|
return conn.opErr("close", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
|
|
// no more data will be written.
|
|
func (conn *HvsockConn) CloseWrite() error {
|
|
err := conn.shutdown(syscall.SHUT_WR)
|
|
if err != nil {
|
|
return conn.opErr("close", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LocalAddr returns the local address of the connection.
|
|
func (conn *HvsockConn) LocalAddr() net.Addr {
|
|
return &conn.local
|
|
}
|
|
|
|
// RemoteAddr returns the remote address of the connection.
|
|
func (conn *HvsockConn) RemoteAddr() net.Addr {
|
|
return &conn.remote
|
|
}
|
|
|
|
// SetDeadline implements the net.Conn SetDeadline method.
|
|
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
|
conn.SetReadDeadline(t)
|
|
conn.SetWriteDeadline(t)
|
|
return nil
|
|
}
|
|
|
|
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
|
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
|
|
return conn.sock.SetReadDeadline(t)
|
|
}
|
|
|
|
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
|
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
|
|
return conn.sock.SetWriteDeadline(t)
|
|
}
|