ipc: use simplified fork of winio
This commit is contained in:
		
							parent
							
								
									dc9bbec9db
								
							
						
					
					
						commit
						a74a29bc93
					
				
					 8 changed files with 1162 additions and 20 deletions
				
			
		
							
								
								
									
										10
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -3,11 +3,7 @@ module golang.zx2c4.com/wireguard | ||||||
| go 1.12 | go 1.12 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	github.com/Microsoft/go-winio v0.4.12 | 	golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f | ||||||
| 	github.com/pkg/errors v0.8.1 // indirect | 	golang.org/x/net v0.0.0-20190522155817-f3200d17e092 | ||||||
| 	golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 | 	golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 | ||||||
| 	golang.org/x/net v0.0.0-20190502183928-7f726cade0ab |  | ||||||
| 	golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 |  | ||||||
| ) | ) | ||||||
| 
 |  | ||||||
| replace github.com/Microsoft/go-winio => golang.zx2c4.com/wireguard/windows v0.0.0-20190429060359-b01600290cd4 |  | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								go.sum
									
									
									
									
									
								
							|  | @ -1,15 +1,11 @@ | ||||||
| github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= |  | ||||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |  | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= | ||||||
| golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk= | golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= | ||||||
| golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w= | golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 h1:f005F/Jl5JLP036x7QIvUVhNTqxvSYwFIiyOh2q12iU= | ||||||
| golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| golang.zx2c4.com/wireguard/windows v0.0.0-20190429060359-b01600290cd4 h1:wueYNew2pMLl/LcKqX4PAzc+zV4suK9+DJaZ8yIEHkM= |  | ||||||
| golang.zx2c4.com/wireguard/windows v0.0.0-20190429060359-b01600290cd4/go.mod h1:Y+FYqVFaQO6a+1uigm0N0GiuaZrLEaBxEiJ8tfH9sMQ= |  | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ package ipc | ||||||
| import ( | import ( | ||||||
| 	"net" | 	"net" | ||||||
| 
 | 
 | ||||||
| 	"github.com/Microsoft/go-winio" | 	"golang.zx2c4.com/wireguard/ipc/winpipe" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| //TODO: replace these with actual standard windows error numbers from the win package
 | //TODO: replace these with actual standard windows error numbers from the win package
 | ||||||
|  | @ -59,10 +59,10 @@ func GetSystemSecurityDescriptor() string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func UAPIListen(name string) (net.Listener, error) { | func UAPIListen(name string) (net.Listener, error) { | ||||||
| 	config := winio.PipeConfig{ | 	config := winpipe.PipeConfig{ | ||||||
| 		SecurityDescriptor: GetSystemSecurityDescriptor(), | 		SecurityDescriptor: GetSystemSecurityDescriptor(), | ||||||
| 	} | 	} | ||||||
| 	listener, err := winio.ListenPipe("\\\\.\\pipe\\WireGuard\\"+name, &config) | 	listener, err := winpipe.ListenPipe("\\\\.\\pipe\\WireGuard\\"+name, &config) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										322
									
								
								ipc/winpipe/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								ipc/winpipe/file.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,322 @@ | ||||||
|  | // +build windows
 | ||||||
|  | 
 | ||||||
|  | /* SPDX-License-Identifier: MIT | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2005 Microsoft | ||||||
|  |  * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. | ||||||
|  |  */ | ||||||
|  | package winpipe | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"io" | ||||||
|  | 	"runtime" | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"syscall" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | //sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
 | ||||||
|  | //sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
 | ||||||
|  | //sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
 | ||||||
|  | //sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
 | ||||||
|  | //sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
 | ||||||
|  | 
 | ||||||
|  | type atomicBool int32 | ||||||
|  | 
 | ||||||
|  | func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } | ||||||
|  | func (b *atomicBool) setFalse()   { atomic.StoreInt32((*int32)(b), 0) } | ||||||
|  | func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) } | ||||||
|  | func (b *atomicBool) swap(new bool) bool { | ||||||
|  | 	var newInt int32 | ||||||
|  | 	if new { | ||||||
|  | 		newInt = 1 | ||||||
|  | 	} | ||||||
|  | 	return atomic.SwapInt32((*int32)(b), newInt) == 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 | ||||||
|  | 	cFILE_SKIP_SET_EVENT_ON_HANDLE        = 2 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrFileClosed = errors.New("file has already been closed") | ||||||
|  | 	ErrTimeout    = &timeoutError{} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 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 } | ||||||
|  | 
 | ||||||
|  | type timeoutChan chan struct{} | ||||||
|  | 
 | ||||||
|  | var ioInitOnce sync.Once | ||||||
|  | var ioCompletionPort syscall.Handle | ||||||
|  | 
 | ||||||
|  | // ioResult contains the result of an asynchronous IO operation
 | ||||||
|  | type ioResult struct { | ||||||
|  | 	bytes uint32 | ||||||
|  | 	err   error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ioOperation represents an outstanding asynchronous Win32 IO
 | ||||||
|  | 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) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
 | ||||||
|  | // It takes ownership of this handle and will close it if it is garbage collected.
 | ||||||
|  | type win32File struct { | ||||||
|  | 	handle        syscall.Handle | ||||||
|  | 	wg            sync.WaitGroup | ||||||
|  | 	wgLock        sync.RWMutex | ||||||
|  | 	closing       atomicBool | ||||||
|  | 	socket        bool | ||||||
|  | 	readDeadline  deadlineHandler | ||||||
|  | 	writeDeadline deadlineHandler | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type deadlineHandler struct { | ||||||
|  | 	setLock     sync.Mutex | ||||||
|  | 	channel     timeoutChan | ||||||
|  | 	channelLock sync.RWMutex | ||||||
|  | 	timer       *time.Timer | ||||||
|  | 	timedout    atomicBool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // makeWin32File makes a new win32File from an existing file handle
 | ||||||
|  | func makeWin32File(h syscall.Handle) (*win32File, error) { | ||||||
|  | 	f := &win32File{handle: h} | ||||||
|  | 	ioInitOnce.Do(initIo) | ||||||
|  | 	_, 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 | ||||||
|  | 	} | ||||||
|  | 	f.readDeadline.channel = make(timeoutChan) | ||||||
|  | 	f.writeDeadline.channel = make(timeoutChan) | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { | ||||||
|  | 	return makeWin32File(h) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // closeHandle closes the resources associated with a Win32 handle
 | ||||||
|  | func (f *win32File) closeHandle() { | ||||||
|  | 	f.wgLock.Lock() | ||||||
|  | 	// Atomically set that we are closing, releasing the resources only once.
 | ||||||
|  | 	if !f.closing.swap(true) { | ||||||
|  | 		f.wgLock.Unlock() | ||||||
|  | 		// cancel all IO and wait for it to complete
 | ||||||
|  | 		cancelIoEx(f.handle, nil) | ||||||
|  | 		f.wg.Wait() | ||||||
|  | 		// at this point, no new IO can start
 | ||||||
|  | 		syscall.Close(f.handle) | ||||||
|  | 		f.handle = 0 | ||||||
|  | 	} else { | ||||||
|  | 		f.wgLock.Unlock() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Close closes a win32File.
 | ||||||
|  | func (f *win32File) Close() error { | ||||||
|  | 	f.closeHandle() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // prepareIo prepares for a new IO operation.
 | ||||||
|  | // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
 | ||||||
|  | func (f *win32File) prepareIo() (*ioOperation, error) { | ||||||
|  | 	f.wgLock.RLock() | ||||||
|  | 	if f.closing.isSet() { | ||||||
|  | 		f.wgLock.RUnlock() | ||||||
|  | 		return nil, ErrFileClosed | ||||||
|  | 	} | ||||||
|  | 	f.wg.Add(1) | ||||||
|  | 	f.wgLock.RUnlock() | ||||||
|  | 	c := &ioOperation{} | ||||||
|  | 	c.ch = make(chan ioResult) | ||||||
|  | 	return c, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ioCompletionProcessor processes completed async IOs forever
 | ||||||
|  | func ioCompletionProcessor(h syscall.Handle) { | ||||||
|  | 	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 (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { | ||||||
|  | 	if err != syscall.ERROR_IO_PENDING { | ||||||
|  | 		return int(bytes), err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if f.closing.isSet() { | ||||||
|  | 		cancelIoEx(f.handle, &c.o) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var timeout timeoutChan | ||||||
|  | 	if d != nil { | ||||||
|  | 		d.channelLock.Lock() | ||||||
|  | 		timeout = d.channel | ||||||
|  | 		d.channelLock.Unlock() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var r ioResult | ||||||
|  | 	select { | ||||||
|  | 	case r = <-c.ch: | ||||||
|  | 		err = r.err | ||||||
|  | 		if err == syscall.ERROR_OPERATION_ABORTED { | ||||||
|  | 			if f.closing.isSet() { | ||||||
|  | 				err = ErrFileClosed | ||||||
|  | 			} | ||||||
|  | 		} else if err != nil && f.socket { | ||||||
|  | 			// err is from Win32. Query the overlapped structure to get the winsock error.
 | ||||||
|  | 			var bytes, flags uint32 | ||||||
|  | 			err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) | ||||||
|  | 		} | ||||||
|  | 	case <-timeout: | ||||||
|  | 		cancelIoEx(f.handle, &c.o) | ||||||
|  | 		r = <-c.ch | ||||||
|  | 		err = r.err | ||||||
|  | 		if err == syscall.ERROR_OPERATION_ABORTED { | ||||||
|  | 			err = ErrTimeout | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// runtime.KeepAlive is needed, as c is passed via native
 | ||||||
|  | 	// code to ioCompletionProcessor, c must remain alive
 | ||||||
|  | 	// until the channel read is complete.
 | ||||||
|  | 	runtime.KeepAlive(c) | ||||||
|  | 	return int(r.bytes), err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read reads from a file handle.
 | ||||||
|  | func (f *win32File) Read(b []byte) (int, error) { | ||||||
|  | 	c, err := f.prepareIo() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	defer f.wg.Done() | ||||||
|  | 
 | ||||||
|  | 	if f.readDeadline.timedout.isSet() { | ||||||
|  | 		return 0, ErrTimeout | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var bytes uint32 | ||||||
|  | 	err = syscall.ReadFile(f.handle, b, &bytes, &c.o) | ||||||
|  | 	n, err := f.asyncIo(c, &f.readDeadline, bytes, err) | ||||||
|  | 	runtime.KeepAlive(b) | ||||||
|  | 
 | ||||||
|  | 	// Handle EOF conditions.
 | ||||||
|  | 	if err == nil && n == 0 && len(b) != 0 { | ||||||
|  | 		return 0, io.EOF | ||||||
|  | 	} else if err == syscall.ERROR_BROKEN_PIPE { | ||||||
|  | 		return 0, io.EOF | ||||||
|  | 	} else { | ||||||
|  | 		return n, err | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Write writes to a file handle.
 | ||||||
|  | func (f *win32File) Write(b []byte) (int, error) { | ||||||
|  | 	c, err := f.prepareIo() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	defer f.wg.Done() | ||||||
|  | 
 | ||||||
|  | 	if f.writeDeadline.timedout.isSet() { | ||||||
|  | 		return 0, ErrTimeout | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var bytes uint32 | ||||||
|  | 	err = syscall.WriteFile(f.handle, b, &bytes, &c.o) | ||||||
|  | 	n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) | ||||||
|  | 	runtime.KeepAlive(b) | ||||||
|  | 	return n, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32File) SetReadDeadline(deadline time.Time) error { | ||||||
|  | 	return f.readDeadline.set(deadline) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32File) SetWriteDeadline(deadline time.Time) error { | ||||||
|  | 	return f.writeDeadline.set(deadline) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32File) Flush() error { | ||||||
|  | 	return syscall.FlushFileBuffers(f.handle) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32File) Fd() uintptr { | ||||||
|  | 	return uintptr(f.handle) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d *deadlineHandler) set(deadline time.Time) error { | ||||||
|  | 	d.setLock.Lock() | ||||||
|  | 	defer d.setLock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	if d.timer != nil { | ||||||
|  | 		if !d.timer.Stop() { | ||||||
|  | 			<-d.channel | ||||||
|  | 		} | ||||||
|  | 		d.timer = nil | ||||||
|  | 	} | ||||||
|  | 	d.timedout.setFalse() | ||||||
|  | 
 | ||||||
|  | 	select { | ||||||
|  | 	case <-d.channel: | ||||||
|  | 		d.channelLock.Lock() | ||||||
|  | 		d.channel = make(chan struct{}) | ||||||
|  | 		d.channelLock.Unlock() | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if deadline.IsZero() { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	timeoutIO := func() { | ||||||
|  | 		d.timedout.setTrue() | ||||||
|  | 		close(d.channel) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	now := time.Now() | ||||||
|  | 	duration := deadline.Sub(now) | ||||||
|  | 	if deadline.After(now) { | ||||||
|  | 		// Deadline is in the future, set a timer to wait
 | ||||||
|  | 		d.timer = time.AfterFunc(duration, timeoutIO) | ||||||
|  | 	} else { | ||||||
|  | 		// Deadline is in the past. Cancel all pending IO now.
 | ||||||
|  | 		timeoutIO() | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								ipc/winpipe/mksyscall.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ipc/winpipe/mksyscall.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | /* SPDX-License-Identifier: MIT | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2005 Microsoft | ||||||
|  |  * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package winpipe | ||||||
|  | 
 | ||||||
|  | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go pipe.go sd.go file.go
 | ||||||
							
								
								
									
										516
									
								
								ipc/winpipe/pipe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								ipc/winpipe/pipe.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,516 @@ | ||||||
|  | // +build windows
 | ||||||
|  | 
 | ||||||
|  | /* SPDX-License-Identifier: MIT | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2005 Microsoft | ||||||
|  |  * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package winpipe | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"runtime" | ||||||
|  | 	"syscall" | ||||||
|  | 	"time" | ||||||
|  | 	"unsafe" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
 | ||||||
|  | //sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error)  [failretval==syscall.InvalidHandle] = CreateNamedPipeW
 | ||||||
|  | //sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
 | ||||||
|  | //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
 | ||||||
|  | //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
 | ||||||
|  | //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
 | ||||||
|  | //sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
 | ||||||
|  | //sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
 | ||||||
|  | //sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
 | ||||||
|  | //sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
 | ||||||
|  | 
 | ||||||
|  | type ioStatusBlock struct { | ||||||
|  | 	Status, Information uintptr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type objectAttributes struct { | ||||||
|  | 	Length             uintptr | ||||||
|  | 	RootDirectory      uintptr | ||||||
|  | 	ObjectName         *unicodeString | ||||||
|  | 	Attributes         uintptr | ||||||
|  | 	SecurityDescriptor *securityDescriptor | ||||||
|  | 	SecurityQoS        uintptr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type unicodeString struct { | ||||||
|  | 	Length        uint16 | ||||||
|  | 	MaximumLength uint16 | ||||||
|  | 	Buffer        uintptr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type securityDescriptor struct { | ||||||
|  | 	Revision byte | ||||||
|  | 	Sbz1     byte | ||||||
|  | 	Control  uint16 | ||||||
|  | 	Owner    uintptr | ||||||
|  | 	Group    uintptr | ||||||
|  | 	Sacl     uintptr | ||||||
|  | 	Dacl     uintptr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ntstatus int32 | ||||||
|  | 
 | ||||||
|  | func (status ntstatus) Err() error { | ||||||
|  | 	if status >= 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return rtlNtStatusToDosError(status) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	cERROR_PIPE_BUSY      = syscall.Errno(231) | ||||||
|  | 	cERROR_NO_DATA        = syscall.Errno(232) | ||||||
|  | 	cERROR_PIPE_CONNECTED = syscall.Errno(535) | ||||||
|  | 	cERROR_SEM_TIMEOUT    = syscall.Errno(121) | ||||||
|  | 
 | ||||||
|  | 	cSECURITY_SQOS_PRESENT = 0x100000 | ||||||
|  | 	cSECURITY_ANONYMOUS    = 0 | ||||||
|  | 
 | ||||||
|  | 	cPIPE_TYPE_MESSAGE = 4 | ||||||
|  | 
 | ||||||
|  | 	cPIPE_READMODE_MESSAGE = 2 | ||||||
|  | 
 | ||||||
|  | 	cFILE_OPEN   = 1 | ||||||
|  | 	cFILE_CREATE = 2 | ||||||
|  | 
 | ||||||
|  | 	cFILE_PIPE_MESSAGE_TYPE          = 1 | ||||||
|  | 	cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 | ||||||
|  | 
 | ||||||
|  | 	cSE_DACL_PRESENT = 4 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
 | ||||||
|  | 	// This error should match net.errClosing since docker takes a dependency on its text.
 | ||||||
|  | 	ErrPipeListenerClosed = errors.New("use of closed network connection") | ||||||
|  | 
 | ||||||
|  | 	errPipeWriteClosed = errors.New("pipe has been closed for write") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type win32Pipe struct { | ||||||
|  | 	*win32File | ||||||
|  | 	path string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type win32MessageBytePipe struct { | ||||||
|  | 	win32Pipe | ||||||
|  | 	writeClosed bool | ||||||
|  | 	readEOF     bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type pipeAddress string | ||||||
|  | 
 | ||||||
|  | func (f *win32Pipe) LocalAddr() net.Addr { | ||||||
|  | 	return pipeAddress(f.path) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32Pipe) RemoteAddr() net.Addr { | ||||||
|  | 	return pipeAddress(f.path) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *win32Pipe) SetDeadline(t time.Time) error { | ||||||
|  | 	f.SetReadDeadline(t) | ||||||
|  | 	f.SetWriteDeadline(t) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CloseWrite closes the write side of a message pipe in byte mode.
 | ||||||
|  | func (f *win32MessageBytePipe) CloseWrite() error { | ||||||
|  | 	if f.writeClosed { | ||||||
|  | 		return errPipeWriteClosed | ||||||
|  | 	} | ||||||
|  | 	err := f.win32File.Flush() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	_, err = f.win32File.Write(nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	f.writeClosed = true | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
 | ||||||
|  | // they are used to implement CloseWrite().
 | ||||||
|  | func (f *win32MessageBytePipe) Write(b []byte) (int, error) { | ||||||
|  | 	if f.writeClosed { | ||||||
|  | 		return 0, errPipeWriteClosed | ||||||
|  | 	} | ||||||
|  | 	if len(b) == 0 { | ||||||
|  | 		return 0, nil | ||||||
|  | 	} | ||||||
|  | 	return f.win32File.Write(b) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
 | ||||||
|  | // mode pipe will return io.EOF, as will all subsequent reads.
 | ||||||
|  | func (f *win32MessageBytePipe) Read(b []byte) (int, error) { | ||||||
|  | 	if f.readEOF { | ||||||
|  | 		return 0, io.EOF | ||||||
|  | 	} | ||||||
|  | 	n, err := f.win32File.Read(b) | ||||||
|  | 	if err == io.EOF { | ||||||
|  | 		// If this was the result of a zero-byte read, then
 | ||||||
|  | 		// it is possible that the read was due to a zero-size
 | ||||||
|  | 		// message. Since we are simulating CloseWrite with a
 | ||||||
|  | 		// zero-byte message, ensure that all future Read() calls
 | ||||||
|  | 		// also return EOF.
 | ||||||
|  | 		f.readEOF = true | ||||||
|  | 	} else if err == syscall.ERROR_MORE_DATA { | ||||||
|  | 		// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
 | ||||||
|  | 		// and the message still has more bytes. Treat this as a success, since
 | ||||||
|  | 		// this package presents all named pipes as byte streams.
 | ||||||
|  | 		err = nil | ||||||
|  | 	} | ||||||
|  | 	return n, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s pipeAddress) Network() string { | ||||||
|  | 	return "pipe" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s pipeAddress) String() string { | ||||||
|  | 	return string(s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
 | ||||||
|  | func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-ctx.Done(): | ||||||
|  | 			return syscall.Handle(0), ctx.Err() | ||||||
|  | 		default: | ||||||
|  | 			h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) | ||||||
|  | 			if err == nil { | ||||||
|  | 				return h, nil | ||||||
|  | 			} | ||||||
|  | 			if err != cERROR_PIPE_BUSY { | ||||||
|  | 				return h, &os.PathError{Err: err, Op: "open", Path: *path} | ||||||
|  | 			} | ||||||
|  | 			// Wait 10 msec and try again. This is a rather simplistic
 | ||||||
|  | 			// view, as we always try each 10 milliseconds.
 | ||||||
|  | 			time.Sleep(time.Millisecond * 10) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DialPipe connects to a named pipe by path, timing out if the connection
 | ||||||
|  | // takes longer than the specified duration. If timeout is nil, then we use
 | ||||||
|  | // a default timeout of 2 seconds.  (We do not use WaitNamedPipe.)
 | ||||||
|  | func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { | ||||||
|  | 	var absTimeout time.Time | ||||||
|  | 	if timeout != nil { | ||||||
|  | 		absTimeout = time.Now().Add(*timeout) | ||||||
|  | 	} else { | ||||||
|  | 		absTimeout = time.Now().Add(time.Second * 2) | ||||||
|  | 	} | ||||||
|  | 	ctx, _ := context.WithDeadline(context.Background(), absTimeout) | ||||||
|  | 	conn, err := DialPipeContext(ctx, path) | ||||||
|  | 	if err == context.DeadlineExceeded { | ||||||
|  | 		return nil, ErrTimeout | ||||||
|  | 	} | ||||||
|  | 	return conn, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
 | ||||||
|  | // cancellation or timeout.
 | ||||||
|  | func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { | ||||||
|  | 	var err error | ||||||
|  | 	var h syscall.Handle | ||||||
|  | 	h, err = tryDialPipe(ctx, &path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var flags uint32 | ||||||
|  | 	err = getNamedPipeInfo(h, &flags, nil, nil, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	f, err := makeWin32File(h) | ||||||
|  | 	if err != nil { | ||||||
|  | 		syscall.Close(h) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If the pipe is in message mode, return a message byte pipe, which
 | ||||||
|  | 	// supports CloseWrite().
 | ||||||
|  | 	if flags&cPIPE_TYPE_MESSAGE != 0 { | ||||||
|  | 		return &win32MessageBytePipe{ | ||||||
|  | 			win32Pipe: win32Pipe{win32File: f, path: path}, | ||||||
|  | 		}, nil | ||||||
|  | 	} | ||||||
|  | 	return &win32Pipe{win32File: f, path: path}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type acceptResponse struct { | ||||||
|  | 	f   *win32File | ||||||
|  | 	err error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type win32PipeListener struct { | ||||||
|  | 	firstHandle syscall.Handle | ||||||
|  | 	path        string | ||||||
|  | 	config      PipeConfig | ||||||
|  | 	acceptCh    chan (chan acceptResponse) | ||||||
|  | 	closeCh     chan int | ||||||
|  | 	doneCh      chan int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) { | ||||||
|  | 	path16, err := syscall.UTF16FromString(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, &os.PathError{Op: "open", Path: path, Err: err} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var oa objectAttributes | ||||||
|  | 	oa.Length = unsafe.Sizeof(oa) | ||||||
|  | 
 | ||||||
|  | 	var ntPath unicodeString | ||||||
|  | 	if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { | ||||||
|  | 		return 0, &os.PathError{Op: "open", Path: path, Err: err} | ||||||
|  | 	} | ||||||
|  | 	defer localFree(ntPath.Buffer) | ||||||
|  | 	oa.ObjectName = &ntPath | ||||||
|  | 
 | ||||||
|  | 	// The security descriptor is only needed for the first pipe.
 | ||||||
|  | 	if first { | ||||||
|  | 		if sd != nil { | ||||||
|  | 			len := uint32(len(sd)) | ||||||
|  | 			sdb := localAlloc(0, len) | ||||||
|  | 			defer localFree(sdb) | ||||||
|  | 			copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) | ||||||
|  | 			oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) | ||||||
|  | 		} else { | ||||||
|  | 			// Construct the default named pipe security descriptor.
 | ||||||
|  | 			var dacl uintptr | ||||||
|  | 			if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { | ||||||
|  | 				return 0, fmt.Errorf("getting default named pipe ACL: %s", err) | ||||||
|  | 			} | ||||||
|  | 			defer localFree(dacl) | ||||||
|  | 
 | ||||||
|  | 			sdb := &securityDescriptor{ | ||||||
|  | 				Revision: 1, | ||||||
|  | 				Control:  cSE_DACL_PRESENT, | ||||||
|  | 				Dacl:     dacl, | ||||||
|  | 			} | ||||||
|  | 			oa.SecurityDescriptor = sdb | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) | ||||||
|  | 	if c.MessageMode { | ||||||
|  | 		typ |= cFILE_PIPE_MESSAGE_TYPE | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	disposition := uint32(cFILE_OPEN) | ||||||
|  | 	access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) | ||||||
|  | 	if first { | ||||||
|  | 		disposition = cFILE_CREATE | ||||||
|  | 		// By not asking for read or write access, the named pipe file system
 | ||||||
|  | 		// will put this pipe into an initially disconnected state, blocking
 | ||||||
|  | 		// client connections until the next call with first == false.
 | ||||||
|  | 		access = syscall.SYNCHRONIZE | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	timeout := int64(-50 * 10000) // 50ms
 | ||||||
|  | 
 | ||||||
|  | 	var ( | ||||||
|  | 		h    syscall.Handle | ||||||
|  | 		iosb ioStatusBlock | ||||||
|  | 	) | ||||||
|  | 	err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, &os.PathError{Op: "open", Path: path, Err: err} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	runtime.KeepAlive(ntPath) | ||||||
|  | 	return h, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) makeServerPipe() (*win32File, error) { | ||||||
|  | 	h, err := makeServerPipeHandle(l.path, nil, &l.config, false) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	f, err := makeWin32File(h) | ||||||
|  | 	if err != nil { | ||||||
|  | 		syscall.Close(h) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { | ||||||
|  | 	p, err := l.makeServerPipe() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Wait for the client to connect.
 | ||||||
|  | 	ch := make(chan error) | ||||||
|  | 	go func(p *win32File) { | ||||||
|  | 		ch <- connectPipe(p) | ||||||
|  | 	}(p) | ||||||
|  | 
 | ||||||
|  | 	select { | ||||||
|  | 	case err = <-ch: | ||||||
|  | 		if err != nil { | ||||||
|  | 			p.Close() | ||||||
|  | 			p = nil | ||||||
|  | 		} | ||||||
|  | 	case <-l.closeCh: | ||||||
|  | 		// Abort the connect request by closing the handle.
 | ||||||
|  | 		p.Close() | ||||||
|  | 		p = nil | ||||||
|  | 		err = <-ch | ||||||
|  | 		if err == nil || err == ErrFileClosed { | ||||||
|  | 			err = ErrPipeListenerClosed | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return p, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) listenerRoutine() { | ||||||
|  | 	closed := false | ||||||
|  | 	for !closed { | ||||||
|  | 		select { | ||||||
|  | 		case <-l.closeCh: | ||||||
|  | 			closed = true | ||||||
|  | 		case responseCh := <-l.acceptCh: | ||||||
|  | 			var ( | ||||||
|  | 				p   *win32File | ||||||
|  | 				err error | ||||||
|  | 			) | ||||||
|  | 			for { | ||||||
|  | 				p, err = l.makeConnectedServerPipe() | ||||||
|  | 				// If the connection was immediately closed by the client, try
 | ||||||
|  | 				// again.
 | ||||||
|  | 				if err != cERROR_NO_DATA { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			responseCh <- acceptResponse{p, err} | ||||||
|  | 			closed = err == ErrPipeListenerClosed | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	syscall.Close(l.firstHandle) | ||||||
|  | 	l.firstHandle = 0 | ||||||
|  | 	// Notify Close() and Accept() callers that the handle has been closed.
 | ||||||
|  | 	close(l.doneCh) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // PipeConfig contain configuration for the pipe listener.
 | ||||||
|  | type PipeConfig struct { | ||||||
|  | 	// SecurityDescriptor contains a Windows security descriptor in SDDL format.
 | ||||||
|  | 	SecurityDescriptor string | ||||||
|  | 
 | ||||||
|  | 	// MessageMode determines whether the pipe is in byte or message mode. In either
 | ||||||
|  | 	// case the pipe is read in byte mode by default. The only practical difference in
 | ||||||
|  | 	// this implementation is that CloseWrite() is only supported for message mode pipes;
 | ||||||
|  | 	// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
 | ||||||
|  | 	// transferred to the reader (and returned as io.EOF in this implementation)
 | ||||||
|  | 	// when the pipe is in message mode.
 | ||||||
|  | 	MessageMode bool | ||||||
|  | 
 | ||||||
|  | 	// InputBufferSize specifies the size the input buffer, in bytes.
 | ||||||
|  | 	InputBufferSize int32 | ||||||
|  | 
 | ||||||
|  | 	// OutputBufferSize specifies the size the input buffer, in bytes.
 | ||||||
|  | 	OutputBufferSize int32 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
 | ||||||
|  | // The pipe must not already exist.
 | ||||||
|  | func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { | ||||||
|  | 	var ( | ||||||
|  | 		sd  []byte | ||||||
|  | 		err error | ||||||
|  | 	) | ||||||
|  | 	if c == nil { | ||||||
|  | 		c = &PipeConfig{} | ||||||
|  | 	} | ||||||
|  | 	if c.SecurityDescriptor != "" { | ||||||
|  | 		sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	h, err := makeServerPipeHandle(path, sd, c, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	l := &win32PipeListener{ | ||||||
|  | 		firstHandle: h, | ||||||
|  | 		path:        path, | ||||||
|  | 		config:      *c, | ||||||
|  | 		acceptCh:    make(chan (chan acceptResponse)), | ||||||
|  | 		closeCh:     make(chan int), | ||||||
|  | 		doneCh:      make(chan int), | ||||||
|  | 	} | ||||||
|  | 	go l.listenerRoutine() | ||||||
|  | 	return l, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func connectPipe(p *win32File) error { | ||||||
|  | 	c, err := p.prepareIo() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer p.wg.Done() | ||||||
|  | 
 | ||||||
|  | 	err = connectNamedPipe(p.handle, &c.o) | ||||||
|  | 	_, err = p.asyncIo(c, nil, 0, err) | ||||||
|  | 	if err != nil && err != cERROR_PIPE_CONNECTED { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) Accept() (net.Conn, error) { | ||||||
|  | 	ch := make(chan acceptResponse) | ||||||
|  | 	select { | ||||||
|  | 	case l.acceptCh <- ch: | ||||||
|  | 		response := <-ch | ||||||
|  | 		err := response.err | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if l.config.MessageMode { | ||||||
|  | 			return &win32MessageBytePipe{ | ||||||
|  | 				win32Pipe: win32Pipe{win32File: response.f, path: l.path}, | ||||||
|  | 			}, nil | ||||||
|  | 		} | ||||||
|  | 		return &win32Pipe{win32File: response.f, path: l.path}, nil | ||||||
|  | 	case <-l.doneCh: | ||||||
|  | 		return nil, ErrPipeListenerClosed | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) Close() error { | ||||||
|  | 	select { | ||||||
|  | 	case l.closeCh <- 1: | ||||||
|  | 		<-l.doneCh | ||||||
|  | 	case <-l.doneCh: | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *win32PipeListener) Addr() net.Addr { | ||||||
|  | 	return pipeAddress(l.path) | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								ipc/winpipe/sd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								ipc/winpipe/sd.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | // +build windows
 | ||||||
|  | 
 | ||||||
|  | /* SPDX-License-Identifier: MIT | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2005 Microsoft | ||||||
|  |  * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package winpipe | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"unsafe" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
 | ||||||
|  | //sys localFree(mem uintptr) = LocalFree
 | ||||||
|  | //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
 | ||||||
|  | 
 | ||||||
|  | func SddlToSecurityDescriptor(sddl string) ([]byte, error) { | ||||||
|  | 	var sdBuffer uintptr | ||||||
|  | 	err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer localFree(sdBuffer) | ||||||
|  | 	sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) | ||||||
|  | 	copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) | ||||||
|  | 	return sd, nil | ||||||
|  | } | ||||||
							
								
								
									
										274
									
								
								ipc/winpipe/zsyscall_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								ipc/winpipe/zsyscall_windows.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | // Code generated by 'go generate'; DO NOT EDIT.
 | ||||||
|  | 
 | ||||||
|  | package winpipe | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"syscall" | ||||||
|  | 	"unsafe" | ||||||
|  | 
 | ||||||
|  | 	"golang.org/x/sys/windows" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var _ unsafe.Pointer | ||||||
|  | 
 | ||||||
|  | // Do the interface allocations only once for common
 | ||||||
|  | // Errno values.
 | ||||||
|  | const ( | ||||||
|  | 	errnoERROR_IO_PENDING = 997 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // errnoErr returns common boxed Errno values, to prevent
 | ||||||
|  | // allocations at runtime.
 | ||||||
|  | func errnoErr(e syscall.Errno) error { | ||||||
|  | 	switch e { | ||||||
|  | 	case 0: | ||||||
|  | 		return nil | ||||||
|  | 	case errnoERROR_IO_PENDING: | ||||||
|  | 		return errERROR_IO_PENDING | ||||||
|  | 	} | ||||||
|  | 	// TODO: add more here, after collecting data on the common
 | ||||||
|  | 	// error values see on Windows. (perhaps when running
 | ||||||
|  | 	// all.bat?)
 | ||||||
|  | 	return e | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	modkernel32 = windows.NewLazySystemDLL("kernel32.dll") | ||||||
|  | 	modntdll    = windows.NewLazySystemDLL("ntdll.dll") | ||||||
|  | 	modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") | ||||||
|  | 	modws2_32   = windows.NewLazySystemDLL("ws2_32.dll") | ||||||
|  | 
 | ||||||
|  | 	procConnectNamedPipe                                     = modkernel32.NewProc("ConnectNamedPipe") | ||||||
|  | 	procCreateNamedPipeW                                     = modkernel32.NewProc("CreateNamedPipeW") | ||||||
|  | 	procCreateFileW                                          = modkernel32.NewProc("CreateFileW") | ||||||
|  | 	procGetNamedPipeInfo                                     = modkernel32.NewProc("GetNamedPipeInfo") | ||||||
|  | 	procGetNamedPipeHandleStateW                             = modkernel32.NewProc("GetNamedPipeHandleStateW") | ||||||
|  | 	procLocalAlloc                                           = modkernel32.NewProc("LocalAlloc") | ||||||
|  | 	procNtCreateNamedPipeFile                                = modntdll.NewProc("NtCreateNamedPipeFile") | ||||||
|  | 	procRtlNtStatusToDosErrorNoTeb                           = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") | ||||||
|  | 	procRtlDosPathNameToNtPathName_U                         = modntdll.NewProc("RtlDosPathNameToNtPathName_U") | ||||||
|  | 	procRtlDefaultNpAcl                                      = modntdll.NewProc("RtlDefaultNpAcl") | ||||||
|  | 	procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") | ||||||
|  | 	procLocalFree                                            = modkernel32.NewProc("LocalFree") | ||||||
|  | 	procGetSecurityDescriptorLength                          = modadvapi32.NewProc("GetSecurityDescriptorLength") | ||||||
|  | 	procCancelIoEx                                           = modkernel32.NewProc("CancelIoEx") | ||||||
|  | 	procCreateIoCompletionPort                               = modkernel32.NewProc("CreateIoCompletionPort") | ||||||
|  | 	procGetQueuedCompletionStatus                            = modkernel32.NewProc("GetQueuedCompletionStatus") | ||||||
|  | 	procSetFileCompletionNotificationModes                   = modkernel32.NewProc("SetFileCompletionNotificationModes") | ||||||
|  | 	procWSAGetOverlappedResult                               = modws2_32.NewProc("WSAGetOverlappedResult") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { | ||||||
|  | 	r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) | ||||||
|  | 	if r1 == 0 { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { | ||||||
|  | 	var _p0 *uint16 | ||||||
|  | 	_p0, err = syscall.UTF16PtrFromString(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { | ||||||
|  | 	r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) | ||||||
|  | 	handle = syscall.Handle(r0) | ||||||
|  | 	if handle == syscall.InvalidHandle { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { | ||||||
|  | 	var _p0 *uint16 | ||||||
|  | 	_p0, err = syscall.UTF16PtrFromString(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { | ||||||
|  | 	r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) | ||||||
|  | 	handle = syscall.Handle(r0) | ||||||
|  | 	if handle == syscall.InvalidHandle { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { | ||||||
|  | 	r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) | ||||||
|  | 	if r1 == 0 { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { | ||||||
|  | 	r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) | ||||||
|  | 	if r1 == 0 { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { | ||||||
|  | 	r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) | ||||||
|  | 	ptr = uintptr(r0) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { | ||||||
|  | 	r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) | ||||||
|  | 	status = ntstatus(r0) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func rtlNtStatusToDosError(status ntstatus) (winerr error) { | ||||||
|  | 	r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) | ||||||
|  | 	if r0 != 0 { | ||||||
|  | 		winerr = syscall.Errno(r0) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { | ||||||
|  | 	r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) | ||||||
|  | 	status = ntstatus(r0) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { | ||||||
|  | 	r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) | ||||||
|  | 	status = ntstatus(r0) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { | ||||||
|  | 	var _p0 *uint16 | ||||||
|  | 	_p0, err = syscall.UTF16PtrFromString(str) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { | ||||||
|  | 	r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) | ||||||
|  | 	if r1 == 0 { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func localFree(mem uintptr) { | ||||||
|  | 	syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getSecurityDescriptorLength(sd uintptr) (len uint32) { | ||||||
|  | 	r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) | ||||||
|  | 	len = uint32(r0) | ||||||
|  | 	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 = errnoErr(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 = errnoErr(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 = errnoErr(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 = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) { | ||||||
|  | 	var _p0 uint32 | ||||||
|  | 	if wait { | ||||||
|  | 		_p0 = 1 | ||||||
|  | 	} else { | ||||||
|  | 		_p0 = 0 | ||||||
|  | 	} | ||||||
|  | 	r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) | ||||||
|  | 	if r1 == 0 { | ||||||
|  | 		if e1 != 0 { | ||||||
|  | 			err = errnoErr(e1) | ||||||
|  | 		} else { | ||||||
|  | 			err = syscall.EINVAL | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue