| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package poll | |
| import ( | |
| "errors" | |
| "internal/race" | |
| "internal/syscall/windows" | |
| "io" | |
| "sync" | |
| "syscall" | |
| "unicode/utf16" | |
| "unicode/utf8" | |
| "unsafe" | |
| ) | |
| var ( | |
| initErr error | |
| ioSync uint64 | |
| ) | |
| // This package uses the SetFileCompletionNotificationModes Windows | |
| // API to skip calling GetQueuedCompletionStatus if an IO operation | |
| // completes synchronously. There is a known bug where | |
| // SetFileCompletionNotificationModes crashes on some systems (see | |
| // https://support.microsoft.com/kb/2568167 for details). | |
| var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use | |
| // checkSetFileCompletionNotificationModes verifies that | |
| // SetFileCompletionNotificationModes Windows API is present | |
| // on the system and is safe to use. | |
| // See https://support.microsoft.com/kb/2568167 for details. | |
| func checkSetFileCompletionNotificationModes() { | |
| err := syscall.LoadSetFileCompletionNotificationModes() | |
| if err != nil { | |
| return | |
| } | |
| protos := [2]int32{syscall.IPPROTO_TCP, 0} | |
| var buf [32]syscall.WSAProtocolInfo | |
| len := uint32(unsafe.Sizeof(buf)) | |
| n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len) | |
| if err != nil { | |
| return | |
| } | |
| for i := int32(0); i < n; i++ { | |
| if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 { | |
| return | |
| } | |
| } | |
| useSetFileCompletionNotificationModes = true | |
| } | |
| func init() { | |
| var d syscall.WSAData | |
| e := syscall.WSAStartup(uint32(0x202), &d) | |
| if e != nil { | |
| initErr = e | |
| } | |
| checkSetFileCompletionNotificationModes() | |
| } | |
| // operation contains superset of data necessary to perform all async IO. | |
| type operation struct { | |
| // Used by IOCP interface, it must be first field | |
| // of the struct, as our code rely on it. | |
| o syscall.Overlapped | |
| // fields used by runtime.netpoll | |
| runtimeCtx uintptr | |
| mode int32 | |
| errno int32 | |
| qty uint32 | |
| // fields used only by net package | |
| fd *FD | |
| buf syscall.WSABuf | |
| msg windows.WSAMsg | |
| sa syscall.Sockaddr | |
| rsa *syscall.RawSockaddrAny | |
| rsan int32 | |
| handle syscall.Handle | |
| flags uint32 | |
| bufs []syscall.WSABuf | |
| } | |
| func (o *operation) InitBuf(buf []byte) { | |
| o.buf.Len = uint32(len(buf)) | |
| o.buf.Buf = nil | |
| if len(buf) != 0 { | |
| o.buf.Buf = &buf[0] | |
| } | |
| } | |
| func (o *operation) InitBufs(buf *[][]byte) { | |
| if o.bufs == nil { | |
| o.bufs = make([]syscall.WSABuf, 0, len(*buf)) | |
| } else { | |
| o.bufs = o.bufs[:0] | |
| } | |
| for _, b := range *buf { | |
| if len(b) == 0 { | |
| o.bufs = append(o.bufs, syscall.WSABuf{}) | |
| continue | |
| } | |
| for len(b) > maxRW { | |
| o.bufs = append(o.bufs, syscall.WSABuf{Len: maxRW, Buf: &b[0]}) | |
| b = b[maxRW:] | |
| } | |
| if len(b) > 0 { | |
| o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: &b[0]}) | |
| } | |
| } | |
| } | |
| // ClearBufs clears all pointers to Buffers parameter captured | |
| // by InitBufs, so it can be released by garbage collector. | |
| func (o *operation) ClearBufs() { | |
| for i := range o.bufs { | |
| o.bufs[i].Buf = nil | |
| } | |
| o.bufs = o.bufs[:0] | |
| } | |
| func (o *operation) InitMsg(p []byte, oob []byte) { | |
| o.InitBuf(p) | |
| o.msg.Buffers = &o.buf | |
| o.msg.BufferCount = 1 | |
| o.msg.Name = nil | |
| o.msg.Namelen = 0 | |
| o.msg.Flags = 0 | |
| o.msg.Control.Len = uint32(len(oob)) | |
| o.msg.Control.Buf = nil | |
| if len(oob) != 0 { | |
| o.msg.Control.Buf = &oob[0] | |
| } | |
| } | |
| // execIO executes a single IO operation o. It submits and cancels | |
| // IO in the current thread for systems where Windows CancelIoEx API | |
| // is available. Alternatively, it passes the request onto | |
| // runtime netpoll and waits for completion or cancels request. | |
| func execIO(o *operation, submit func(o *operation) error) (int, error) { | |
| if o.fd.pd.runtimeCtx == 0 { | |
| return 0, errors.New("internal error: polling on unsupported descriptor type") | |
| } | |
| fd := o.fd | |
| // Notify runtime netpoll about starting IO. | |
| err := fd.pd.prepare(int(o.mode), fd.isFile) | |
| if err != nil { | |
| return 0, err | |
| } | |
| // Start IO. | |
| err = submit(o) | |
| switch err { | |
| case nil: | |
| // IO completed immediately | |
| if o.fd.skipSyncNotif { | |
| // No completion message will follow, so return immediately. | |
| return int(o.qty), nil | |
| } | |
| // Need to get our completion message anyway. | |
| case syscall.ERROR_IO_PENDING: | |
| // IO started, and we have to wait for its completion. | |
| err = nil | |
| default: | |
| return 0, err | |
| } | |
| // Wait for our request to complete. | |
| err = fd.pd.wait(int(o.mode), fd.isFile) | |
| if err == nil { | |
| // All is good. Extract our IO results and return. | |
| if o.errno != 0 { | |
| err = syscall.Errno(o.errno) | |
| // More data available. Return back the size of received data. | |
| if err == syscall.ERROR_MORE_DATA || err == windows.WSAEMSGSIZE { | |
| return int(o.qty), err | |
| } | |
| return 0, err | |
| } | |
| return int(o.qty), nil | |
| } | |
| // IO is interrupted by "close" or "timeout" | |
| netpollErr := err | |
| switch netpollErr { | |
| case ErrNetClosing, ErrFileClosing, ErrDeadlineExceeded: | |
| // will deal with those. | |
| default: | |
| panic("unexpected runtime.netpoll error: " + netpollErr.Error()) | |
| } | |
| // Cancel our request. | |
| err = syscall.CancelIoEx(fd.Sysfd, &o.o) | |
| // Assuming ERROR_NOT_FOUND is returned, if IO is completed. | |
| if err != nil && err != syscall.ERROR_NOT_FOUND { | |
| // TODO(brainman): maybe do something else, but panic. | |
| panic(err) | |
| } | |
| // Wait for cancellation to complete. | |
| fd.pd.waitCanceled(int(o.mode)) | |
| if o.errno != 0 { | |
| err = syscall.Errno(o.errno) | |
| if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled | |
| err = netpollErr | |
| } | |
| return 0, err | |
| } | |
| // We issued a cancellation request. But, it seems, IO operation succeeded | |
| // before the cancellation request run. We need to treat the IO operation as | |
| // succeeded (the bytes are actually sent/recv from network). | |
| return int(o.qty), nil | |
| } | |
| // FD is a file descriptor. The net and os packages embed this type in | |
| // a larger type representing a network connection or OS file. | |
| type FD struct { | |
| // Lock sysfd and serialize access to Read and Write methods. | |
| fdmu fdMutex | |
| // System file descriptor. Immutable until Close. | |
| Sysfd syscall.Handle | |
| // Read operation. | |
| rop operation | |
| // Write operation. | |
| wop operation | |
| // I/O poller. | |
| pd pollDesc | |
| // Used to implement pread/pwrite. | |
| l sync.Mutex | |
| // For console I/O. | |
| lastbits []byte // first few bytes of the last incomplete rune in last write | |
| readuint16 []uint16 // buffer to hold uint16s obtained with ReadConsole | |
| readbyte []byte // buffer to hold decoding of readuint16 from utf16 to utf8 | |
| readbyteOffset int // readbyte[readOffset:] is yet to be consumed with file.Read | |
| // Semaphore signaled when file is closed. | |
| csema uint32 | |
| skipSyncNotif bool | |
| // Whether this is a streaming descriptor, as opposed to a | |
| // packet-based descriptor like a UDP socket. | |
| IsStream bool | |
| // Whether a zero byte read indicates EOF. This is false for a | |
| // message based socket connection. | |
| ZeroReadIsEOF bool | |
| // Whether this is a file rather than a network socket. | |
| isFile bool | |
| // The kind of this file. | |
| kind fileKind | |
| } | |
| // fileKind describes the kind of file. | |
| type fileKind byte | |
| const ( | |
| kindNet fileKind = iota | |
| kindFile | |
| kindConsole | |
| kindPipe | |
| ) | |
| // logInitFD is set by tests to enable file descriptor initialization logging. | |
| var logInitFD func(net string, fd *FD, err error) | |
| // Init initializes the FD. The Sysfd field should already be set. | |
| // This can be called multiple times on a single FD. | |
| // The net argument is a network name from the net package (e.g., "tcp"), | |
| // or "file" or "console" or "dir". | |
| // Set pollable to true if fd should be managed by runtime netpoll. | |
| func (fd *FD) Init(net string, pollable bool) (string, error) { | |
| if initErr != nil { | |
| return "", initErr | |
| } | |
| switch net { | |
| case "file", "dir": | |
| fd.kind = kindFile | |
| case "console": | |
| fd.kind = kindConsole | |
| case "pipe": | |
| fd.kind = kindPipe | |
| case "tcp", "tcp4", "tcp6", | |
| "udp", "udp4", "udp6", | |
| "ip", "ip4", "ip6", | |
| "unix", "unixgram", "unixpacket": | |
| fd.kind = kindNet | |
| default: | |
| return "", errors.New("internal error: unknown network type " + net) | |
| } | |
| fd.isFile = fd.kind != kindNet | |
| var err error | |
| if pollable { | |
| // Only call init for a network socket. | |
| // This means that we don't add files to the runtime poller. | |
| // Adding files to the runtime poller can confuse matters | |
| // if the user is doing their own overlapped I/O. | |
| // See issue #21172. | |
| // | |
| // In general the code below avoids calling the execIO | |
| // function for non-network sockets. If some method does | |
| // somehow call execIO, then execIO, and therefore the | |
| // calling method, will return an error, because | |
| // fd.pd.runtimeCtx will be 0. | |
| err = fd.pd.init(fd) | |
| } | |
| if logInitFD != nil { | |
| logInitFD(net, fd, err) | |
| } | |
| if err != nil { | |
| return "", err | |
| } | |
| if pollable && useSetFileCompletionNotificationModes { | |
| // We do not use events, so we can skip them always. | |
| flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) | |
| switch net { | |
| case "tcp", "tcp4", "tcp6", | |
| "udp", "udp4", "udp6": | |
| flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | |
| } | |
| err := syscall.SetFileCompletionNotificationModes(fd.Sysfd, flags) | |
| if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 { | |
| fd.skipSyncNotif = true | |
| } | |
| } | |
| // Disable SIO_UDP_CONNRESET behavior. | |
| // http://support.microsoft.com/kb/263823 | |
| switch net { | |
| case "udp", "udp4", "udp6": | |
| ret := uint32(0) | |
| flag := uint32(0) | |
| size := uint32(unsafe.Sizeof(flag)) | |
| err := syscall.WSAIoctl(fd.Sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0) | |
| if err != nil { | |
| return "wsaioctl", err | |
| } | |
| } | |
| fd.rop.mode = 'r' | |
| fd.wop.mode = 'w' | |
| fd.rop.fd = fd | |
| fd.wop.fd = fd | |
| fd.rop.runtimeCtx = fd.pd.runtimeCtx | |
| fd.wop.runtimeCtx = fd.pd.runtimeCtx | |
| return "", nil | |
| } | |
| func (fd *FD) destroy() error { | |
| if fd.Sysfd == syscall.InvalidHandle { | |
| return syscall.EINVAL | |
| } | |
| // Poller may want to unregister fd in readiness notification mechanism, | |
| // so this must be executed before fd.CloseFunc. | |
| fd.pd.close() | |
| var err error | |
| switch fd.kind { | |
| case kindNet: | |
| // The net package uses the CloseFunc variable for testing. | |
| err = CloseFunc(fd.Sysfd) | |
| default: | |
| err = syscall.CloseHandle(fd.Sysfd) | |
| } | |
| fd.Sysfd = syscall.InvalidHandle | |
| runtime_Semrelease(&fd.csema) | |
| return err | |
| } | |
| // Close closes the FD. The underlying file descriptor is closed by | |
| // the destroy method when there are no remaining references. | |
| func (fd *FD) Close() error { | |
| if !fd.fdmu.increfAndClose() { | |
| return errClosing(fd.isFile) | |
| } | |
| if fd.kind == kindPipe { | |
| syscall.CancelIoEx(fd.Sysfd, nil) | |
| } | |
| // unblock pending reader and writer | |
| fd.pd.evict() | |
| err := fd.decref() | |
| // Wait until the descriptor is closed. If this was the only | |
| // reference, it is already closed. | |
| runtime_Semacquire(&fd.csema) | |
| return err | |
| } | |
| // Windows ReadFile and WSARecv use DWORD (uint32) parameter to pass buffer length. | |
| // This prevents us reading blocks larger than 4GB. | |
| // See golang.org/issue/26923. | |
| const maxRW = 1 << 30 // 1GB is large enough and keeps subsequent reads aligned | |
| // Read implements io.Reader. | |
| func (fd *FD) Read(buf []byte) (int, error) { | |
| if err := fd.readLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.readUnlock() | |
| if len(buf) > maxRW { | |
| buf = buf[:maxRW] | |
| } | |
| var n int | |
| var err error | |
| if fd.isFile { | |
| fd.l.Lock() | |
| defer fd.l.Unlock() | |
| switch fd.kind { | |
| case kindConsole: | |
| n, err = fd.readConsole(buf) | |
| default: | |
| n, err = syscall.Read(fd.Sysfd, buf) | |
| if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED { | |
| // Close uses CancelIoEx to interrupt concurrent I/O for pipes. | |
| // If the fd is a pipe and the Read was interrupted by CancelIoEx, | |
| // we assume it is interrupted by Close. | |
| err = ErrFileClosing | |
| } | |
| } | |
| if err != nil { | |
| n = 0 | |
| } | |
| } else { | |
| o := &fd.rop | |
| o.InitBuf(buf) | |
| n, err = execIO(o, func(o *operation) error { | |
| return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) | |
| }) | |
| if race.Enabled { | |
| race.Acquire(unsafe.Pointer(&ioSync)) | |
| } | |
| } | |
| if len(buf) != 0 { | |
| err = fd.eofError(n, err) | |
| } | |
| return n, err | |
| } | |
| var ReadConsole = syscall.ReadConsole // changed for testing | |
| // readConsole reads utf16 characters from console File, | |
| // encodes them into utf8 and stores them in buffer b. | |
| // It returns the number of utf8 bytes read and an error, if any. | |
| func (fd *FD) readConsole(b []byte) (int, error) { | |
| if len(b) == 0 { | |
| return 0, nil | |
| } | |
| if fd.readuint16 == nil { | |
| // Note: syscall.ReadConsole fails for very large buffers. | |
| // The limit is somewhere around (but not exactly) 16384. | |
| // Stay well below. | |
| fd.readuint16 = make([]uint16, 0, 10000) | |
| fd.readbyte = make([]byte, 0, 4*cap(fd.readuint16)) | |
| } | |
| for fd.readbyteOffset >= len(fd.readbyte) { | |
| n := cap(fd.readuint16) - len(fd.readuint16) | |
| if n > len(b) { | |
| n = len(b) | |
| } | |
| var nw uint32 | |
| err := ReadConsole(fd.Sysfd, &fd.readuint16[:len(fd.readuint16)+1][len(fd.readuint16)], uint32(n), &nw, nil) | |
| if err != nil { | |
| return 0, err | |
| } | |
| uint16s := fd.readuint16[:len(fd.readuint16)+int(nw)] | |
| fd.readuint16 = fd.readuint16[:0] | |
| buf := fd.readbyte[:0] | |
| for i := 0; i < len(uint16s); i++ { | |
| r := rune(uint16s[i]) | |
| if utf16.IsSurrogate(r) { | |
| if i+1 == len(uint16s) { | |
| if nw > 0 { | |
| // Save half surrogate pair for next time. | |
| fd.readuint16 = fd.readuint16[:1] | |
| fd.readuint16[0] = uint16(r) | |
| break | |
| } | |
| r = utf8.RuneError | |
| } else { | |
| r = utf16.DecodeRune(r, rune(uint16s[i+1])) | |
| if r != utf8.RuneError { | |
| i++ | |
| } | |
| } | |
| } | |
| buf = utf8.AppendRune(buf, r) | |
| } | |
| fd.readbyte = buf | |
| fd.readbyteOffset = 0 | |
| if nw == 0 { | |
| break | |
| } | |
| } | |
| src := fd.readbyte[fd.readbyteOffset:] | |
| var i int | |
| for i = 0; i < len(src) && i < len(b); i++ { | |
| x := src[i] | |
| if x == 0x1A { // Ctrl-Z | |
| if i == 0 { | |
| fd.readbyteOffset++ | |
| } | |
| break | |
| } | |
| b[i] = x | |
| } | |
| fd.readbyteOffset += i | |
| return i, nil | |
| } | |
| // Pread emulates the Unix pread system call. | |
| func (fd *FD) Pread(b []byte, off int64) (int, error) { | |
| if fd.kind == kindPipe { | |
| // Pread does not work with pipes | |
| return 0, syscall.ESPIPE | |
| } | |
| // Call incref, not readLock, because since pread specifies the | |
| // offset it is independent from other reads. | |
| if err := fd.incref(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.decref() | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| fd.l.Lock() | |
| defer fd.l.Unlock() | |
| curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) | |
| if e != nil { | |
| return 0, e | |
| } | |
| defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) | |
| o := syscall.Overlapped{ | |
| OffsetHigh: uint32(off >> 32), | |
| Offset: uint32(off), | |
| } | |
| var done uint32 | |
| e = syscall.ReadFile(fd.Sysfd, b, &done, &o) | |
| if e != nil { | |
| done = 0 | |
| if e == syscall.ERROR_HANDLE_EOF { | |
| e = io.EOF | |
| } | |
| } | |
| if len(b) != 0 { | |
| e = fd.eofError(int(done), e) | |
| } | |
| return int(done), e | |
| } | |
| // ReadFrom wraps the recvfrom network call. | |
| func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) { | |
| if len(buf) == 0 { | |
| return 0, nil, nil | |
| } | |
| if len(buf) > maxRW { | |
| buf = buf[:maxRW] | |
| } | |
| if err := fd.readLock(); err != nil { | |
| return 0, nil, err | |
| } | |
| defer fd.readUnlock() | |
| o := &fd.rop | |
| o.InitBuf(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.rsan = int32(unsafe.Sizeof(*o.rsa)) | |
| return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| if err != nil { | |
| return n, nil, err | |
| } | |
| sa, _ := o.rsa.Sockaddr() | |
| return n, sa, nil | |
| } | |
| // ReadFromInet4 wraps the recvfrom network call for IPv4. | |
| func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { | |
| if len(buf) == 0 { | |
| return 0, nil | |
| } | |
| if len(buf) > maxRW { | |
| buf = buf[:maxRW] | |
| } | |
| if err := fd.readLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.readUnlock() | |
| o := &fd.rop | |
| o.InitBuf(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.rsan = int32(unsafe.Sizeof(*o.rsa)) | |
| return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| if err != nil { | |
| return n, err | |
| } | |
| rawToSockaddrInet4(o.rsa, sa4) | |
| return n, err | |
| } | |
| // ReadFromInet6 wraps the recvfrom network call for IPv6. | |
| func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { | |
| if len(buf) == 0 { | |
| return 0, nil | |
| } | |
| if len(buf) > maxRW { | |
| buf = buf[:maxRW] | |
| } | |
| if err := fd.readLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.readUnlock() | |
| o := &fd.rop | |
| o.InitBuf(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.rsan = int32(unsafe.Sizeof(*o.rsa)) | |
| return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| if err != nil { | |
| return n, err | |
| } | |
| rawToSockaddrInet6(o.rsa, sa6) | |
| return n, err | |
| } | |
| // Write implements io.Writer. | |
| func (fd *FD) Write(buf []byte) (int, error) { | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if fd.isFile { | |
| fd.l.Lock() | |
| defer fd.l.Unlock() | |
| } | |
| ntotal := 0 | |
| for len(buf) > 0 { | |
| b := buf | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| var n int | |
| var err error | |
| if fd.isFile { | |
| switch fd.kind { | |
| case kindConsole: | |
| n, err = fd.writeConsole(b) | |
| default: | |
| n, err = syscall.Write(fd.Sysfd, b) | |
| if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED { | |
| // Close uses CancelIoEx to interrupt concurrent I/O for pipes. | |
| // If the fd is a pipe and the Write was interrupted by CancelIoEx, | |
| // we assume it is interrupted by Close. | |
| err = ErrFileClosing | |
| } | |
| } | |
| if err != nil { | |
| n = 0 | |
| } | |
| } else { | |
| if race.Enabled { | |
| race.ReleaseMerge(unsafe.Pointer(&ioSync)) | |
| } | |
| o := &fd.wop | |
| o.InitBuf(b) | |
| n, err = execIO(o, func(o *operation) error { | |
| return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil) | |
| }) | |
| } | |
| ntotal += n | |
| if err != nil { | |
| return ntotal, err | |
| } | |
| buf = buf[n:] | |
| } | |
| return ntotal, nil | |
| } | |
| // writeConsole writes len(b) bytes to the console File. | |
| // It returns the number of bytes written and an error, if any. | |
| func (fd *FD) writeConsole(b []byte) (int, error) { | |
| n := len(b) | |
| runes := make([]rune, 0, 256) | |
| if len(fd.lastbits) > 0 { | |
| b = append(fd.lastbits, b...) | |
| fd.lastbits = nil | |
| } | |
| for len(b) >= utf8.UTFMax || utf8.FullRune(b) { | |
| r, l := utf8.DecodeRune(b) | |
| runes = append(runes, r) | |
| b = b[l:] | |
| } | |
| if len(b) > 0 { | |
| fd.lastbits = make([]byte, len(b)) | |
| copy(fd.lastbits, b) | |
| } | |
| // syscall.WriteConsole seems to fail, if given large buffer. | |
| // So limit the buffer to 16000 characters. This number was | |
| // discovered by experimenting with syscall.WriteConsole. | |
| const maxWrite = 16000 | |
| for len(runes) > 0 { | |
| m := len(runes) | |
| if m > maxWrite { | |
| m = maxWrite | |
| } | |
| chunk := runes[:m] | |
| runes = runes[m:] | |
| uint16s := utf16.Encode(chunk) | |
| for len(uint16s) > 0 { | |
| var written uint32 | |
| err := syscall.WriteConsole(fd.Sysfd, &uint16s[0], uint32(len(uint16s)), &written, nil) | |
| if err != nil { | |
| return 0, err | |
| } | |
| uint16s = uint16s[written:] | |
| } | |
| } | |
| return n, nil | |
| } | |
| // Pwrite emulates the Unix pwrite system call. | |
| func (fd *FD) Pwrite(buf []byte, off int64) (int, error) { | |
| if fd.kind == kindPipe { | |
| // Pwrite does not work with pipes | |
| return 0, syscall.ESPIPE | |
| } | |
| // Call incref, not writeLock, because since pwrite specifies the | |
| // offset it is independent from other writes. | |
| if err := fd.incref(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.decref() | |
| fd.l.Lock() | |
| defer fd.l.Unlock() | |
| curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) | |
| if e != nil { | |
| return 0, e | |
| } | |
| defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) | |
| ntotal := 0 | |
| for len(buf) > 0 { | |
| b := buf | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| var n uint32 | |
| o := syscall.Overlapped{ | |
| OffsetHigh: uint32(off >> 32), | |
| Offset: uint32(off), | |
| } | |
| e = syscall.WriteFile(fd.Sysfd, b, &n, &o) | |
| ntotal += int(n) | |
| if e != nil { | |
| return ntotal, e | |
| } | |
| buf = buf[n:] | |
| off += int64(n) | |
| } | |
| return ntotal, nil | |
| } | |
| // Writev emulates the Unix writev system call. | |
| func (fd *FD) Writev(buf *[][]byte) (int64, error) { | |
| if len(*buf) == 0 { | |
| return 0, nil | |
| } | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if race.Enabled { | |
| race.ReleaseMerge(unsafe.Pointer(&ioSync)) | |
| } | |
| o := &fd.wop | |
| o.InitBufs(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| return syscall.WSASend(o.fd.Sysfd, &o.bufs[0], uint32(len(o.bufs)), &o.qty, 0, &o.o, nil) | |
| }) | |
| o.ClearBufs() | |
| TestHookDidWritev(n) | |
| consume(buf, int64(n)) | |
| return int64(n), err | |
| } | |
| // WriteTo wraps the sendto network call. | |
| func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if len(buf) == 0 { | |
| // handle zero-byte payload | |
| o := &fd.wop | |
| o.InitBuf(buf) | |
| o.sa = sa | |
| n, err := execIO(o, func(o *operation) error { | |
| return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) | |
| }) | |
| return n, err | |
| } | |
| ntotal := 0 | |
| for len(buf) > 0 { | |
| b := buf | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| o := &fd.wop | |
| o.InitBuf(b) | |
| o.sa = sa | |
| n, err := execIO(o, func(o *operation) error { | |
| return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) | |
| }) | |
| ntotal += int(n) | |
| if err != nil { | |
| return ntotal, err | |
| } | |
| buf = buf[n:] | |
| } | |
| return ntotal, nil | |
| } | |
| // WriteToInet4 is WriteTo, specialized for syscall.SockaddrInet4. | |
| func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if len(buf) == 0 { | |
| // handle zero-byte payload | |
| o := &fd.wop | |
| o.InitBuf(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) | |
| }) | |
| return n, err | |
| } | |
| ntotal := 0 | |
| for len(buf) > 0 { | |
| b := buf | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| o := &fd.wop | |
| o.InitBuf(b) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) | |
| }) | |
| ntotal += int(n) | |
| if err != nil { | |
| return ntotal, err | |
| } | |
| buf = buf[n:] | |
| } | |
| return ntotal, nil | |
| } | |
| // WriteToInet6 is WriteTo, specialized for syscall.SockaddrInet6. | |
| func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if len(buf) == 0 { | |
| // handle zero-byte payload | |
| o := &fd.wop | |
| o.InitBuf(buf) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) | |
| }) | |
| return n, err | |
| } | |
| ntotal := 0 | |
| for len(buf) > 0 { | |
| b := buf | |
| if len(b) > maxRW { | |
| b = b[:maxRW] | |
| } | |
| o := &fd.wop | |
| o.InitBuf(b) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) | |
| }) | |
| ntotal += int(n) | |
| if err != nil { | |
| return ntotal, err | |
| } | |
| buf = buf[n:] | |
| } | |
| return ntotal, nil | |
| } | |
| // Call ConnectEx. This doesn't need any locking, since it is only | |
| // called when the descriptor is first created. This is here rather | |
| // than in the net package so that it can use fd.wop. | |
| func (fd *FD) ConnectEx(ra syscall.Sockaddr) error { | |
| o := &fd.wop | |
| o.sa = ra | |
| _, err := execIO(o, func(o *operation) error { | |
| return ConnectExFunc(o.fd.Sysfd, o.sa, nil, 0, nil, &o.o) | |
| }) | |
| return err | |
| } | |
| func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny, o *operation) (string, error) { | |
| // Submit accept request. | |
| o.handle = s | |
| o.rsan = int32(unsafe.Sizeof(rawsa[0])) | |
| _, err := execIO(o, func(o *operation) error { | |
| return AcceptFunc(o.fd.Sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) | |
| }) | |
| if err != nil { | |
| CloseFunc(s) | |
| return "acceptex", err | |
| } | |
| // Inherit properties of the listening socket. | |
| err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.Sysfd)), int32(unsafe.Sizeof(fd.Sysfd))) | |
| if err != nil { | |
| CloseFunc(s) | |
| return "setsockopt", err | |
| } | |
| return "", nil | |
| } | |
| // Accept handles accepting a socket. The sysSocket parameter is used | |
| // to allocate the net socket. | |
| func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle, []syscall.RawSockaddrAny, uint32, string, error) { | |
| if err := fd.readLock(); err != nil { | |
| return syscall.InvalidHandle, nil, 0, "", err | |
| } | |
| defer fd.readUnlock() | |
| o := &fd.rop | |
| var rawsa [2]syscall.RawSockaddrAny | |
| for { | |
| s, err := sysSocket() | |
| if err != nil { | |
| return syscall.InvalidHandle, nil, 0, "", err | |
| } | |
| errcall, err := fd.acceptOne(s, rawsa[:], o) | |
| if err == nil { | |
| return s, rawsa[:], uint32(o.rsan), "", nil | |
| } | |
| // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is | |
| // returned here. These happen if connection reset is received | |
| // before AcceptEx could complete. These errors relate to new | |
| // connection, not to AcceptEx, so ignore broken connection and | |
| // try AcceptEx again for more connections. | |
| errno, ok := err.(syscall.Errno) | |
| if !ok { | |
| return syscall.InvalidHandle, nil, 0, errcall, err | |
| } | |
| switch errno { | |
| case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET: | |
| // ignore these and try again | |
| default: | |
| return syscall.InvalidHandle, nil, 0, errcall, err | |
| } | |
| } | |
| } | |
| // Seek wraps syscall.Seek. | |
| func (fd *FD) Seek(offset int64, whence int) (int64, error) { | |
| if fd.kind == kindPipe { | |
| return 0, syscall.ESPIPE | |
| } | |
| if err := fd.incref(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.decref() | |
| fd.l.Lock() | |
| defer fd.l.Unlock() | |
| return syscall.Seek(fd.Sysfd, offset, whence) | |
| } | |
| // Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed. | |
| func (fd *FD) Fchmod(mode uint32) error { | |
| if err := fd.incref(); err != nil { | |
| return err | |
| } | |
| defer fd.decref() | |
| var d syscall.ByHandleFileInformation | |
| if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil { | |
| return err | |
| } | |
| attrs := d.FileAttributes | |
| if mode&syscall.S_IWRITE != 0 { | |
| attrs &^= syscall.FILE_ATTRIBUTE_READONLY | |
| } else { | |
| attrs |= syscall.FILE_ATTRIBUTE_READONLY | |
| } | |
| if attrs == d.FileAttributes { | |
| return nil | |
| } | |
| var du windows.FILE_BASIC_INFO | |
| du.FileAttributes = attrs | |
| return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, unsafe.Pointer(&du), uint32(unsafe.Sizeof(du))) | |
| } | |
| // Fchdir wraps syscall.Fchdir. | |
| func (fd *FD) Fchdir() error { | |
| if err := fd.incref(); err != nil { | |
| return err | |
| } | |
| defer fd.decref() | |
| return syscall.Fchdir(fd.Sysfd) | |
| } | |
| // GetFileType wraps syscall.GetFileType. | |
| func (fd *FD) GetFileType() (uint32, error) { | |
| if err := fd.incref(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.decref() | |
| return syscall.GetFileType(fd.Sysfd) | |
| } | |
| // GetFileInformationByHandle wraps GetFileInformationByHandle. | |
| func (fd *FD) GetFileInformationByHandle(data *syscall.ByHandleFileInformation) error { | |
| if err := fd.incref(); err != nil { | |
| return err | |
| } | |
| defer fd.decref() | |
| return syscall.GetFileInformationByHandle(fd.Sysfd, data) | |
| } | |
| // RawRead invokes the user-defined function f for a read operation. | |
| func (fd *FD) RawRead(f func(uintptr) bool) error { | |
| if err := fd.readLock(); err != nil { | |
| return err | |
| } | |
| defer fd.readUnlock() | |
| for { | |
| if f(uintptr(fd.Sysfd)) { | |
| return nil | |
| } | |
| // Use a zero-byte read as a way to get notified when this | |
| // socket is readable. h/t https://stackoverflow.com/a/42019668/332798 | |
| o := &fd.rop | |
| o.InitBuf(nil) | |
| if !fd.IsStream { | |
| o.flags |= windows.MSG_PEEK | |
| } | |
| _, err := execIO(o, func(o *operation) error { | |
| return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) | |
| }) | |
| if err == windows.WSAEMSGSIZE { | |
| // expected with a 0-byte peek, ignore. | |
| } else if err != nil { | |
| return err | |
| } | |
| } | |
| } | |
| // RawWrite invokes the user-defined function f for a write operation. | |
| func (fd *FD) RawWrite(f func(uintptr) bool) error { | |
| if err := fd.writeLock(); err != nil { | |
| return err | |
| } | |
| defer fd.writeUnlock() | |
| if f(uintptr(fd.Sysfd)) { | |
| return nil | |
| } | |
| // TODO(tmm1): find a way to detect socket writability | |
| return syscall.EWINDOWS | |
| } | |
| func sockaddrInet4ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) int32 { | |
| *rsa = syscall.RawSockaddrAny{} | |
| raw := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) | |
| raw.Family = syscall.AF_INET | |
| p := (*[2]byte)(unsafe.Pointer(&raw.Port)) | |
| p[0] = byte(sa.Port >> 8) | |
| p[1] = byte(sa.Port) | |
| raw.Addr = sa.Addr | |
| return int32(unsafe.Sizeof(*raw)) | |
| } | |
| func sockaddrInet6ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) int32 { | |
| *rsa = syscall.RawSockaddrAny{} | |
| raw := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) | |
| raw.Family = syscall.AF_INET6 | |
| p := (*[2]byte)(unsafe.Pointer(&raw.Port)) | |
| p[0] = byte(sa.Port >> 8) | |
| p[1] = byte(sa.Port) | |
| raw.Scope_id = sa.ZoneId | |
| raw.Addr = sa.Addr | |
| return int32(unsafe.Sizeof(*raw)) | |
| } | |
| func rawToSockaddrInet4(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) { | |
| pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) | |
| p := (*[2]byte)(unsafe.Pointer(&pp.Port)) | |
| sa.Port = int(p[0])<<8 + int(p[1]) | |
| sa.Addr = pp.Addr | |
| } | |
| func rawToSockaddrInet6(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) { | |
| pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) | |
| p := (*[2]byte)(unsafe.Pointer(&pp.Port)) | |
| sa.Port = int(p[0])<<8 + int(p[1]) | |
| sa.ZoneId = pp.Scope_id | |
| sa.Addr = pp.Addr | |
| } | |
| func sockaddrToRaw(rsa *syscall.RawSockaddrAny, sa syscall.Sockaddr) (int32, error) { | |
| switch sa := sa.(type) { | |
| case *syscall.SockaddrInet4: | |
| sz := sockaddrInet4ToRaw(rsa, sa) | |
| return sz, nil | |
| case *syscall.SockaddrInet6: | |
| sz := sockaddrInet6ToRaw(rsa, sa) | |
| return sz, nil | |
| default: | |
| return 0, syscall.EWINDOWS | |
| } | |
| } | |
| // ReadMsg wraps the WSARecvMsg network call. | |
| func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.Sockaddr, error) { | |
| if err := fd.readLock(); err != nil { | |
| return 0, 0, 0, nil, err | |
| } | |
| defer fd.readUnlock() | |
| if len(p) > maxRW { | |
| p = p[:maxRW] | |
| } | |
| o := &fd.rop | |
| o.InitMsg(p, oob) | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) | |
| o.msg.Flags = uint32(flags) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| var sa syscall.Sockaddr | |
| if err == nil { | |
| sa, err = o.rsa.Sockaddr() | |
| } | |
| return n, int(o.msg.Control.Len), int(o.msg.Flags), sa, err | |
| } | |
| // ReadMsgInet4 is ReadMsg, but specialized to return a syscall.SockaddrInet4. | |
| func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) { | |
| if err := fd.readLock(); err != nil { | |
| return 0, 0, 0, err | |
| } | |
| defer fd.readUnlock() | |
| if len(p) > maxRW { | |
| p = p[:maxRW] | |
| } | |
| o := &fd.rop | |
| o.InitMsg(p, oob) | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) | |
| o.msg.Flags = uint32(flags) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| if err == nil { | |
| rawToSockaddrInet4(o.rsa, sa4) | |
| } | |
| return n, int(o.msg.Control.Len), int(o.msg.Flags), err | |
| } | |
| // ReadMsgInet6 is ReadMsg, but specialized to return a syscall.SockaddrInet6. | |
| func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) { | |
| if err := fd.readLock(); err != nil { | |
| return 0, 0, 0, err | |
| } | |
| defer fd.readUnlock() | |
| if len(p) > maxRW { | |
| p = p[:maxRW] | |
| } | |
| o := &fd.rop | |
| o.InitMsg(p, oob) | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) | |
| o.msg.Flags = uint32(flags) | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) | |
| }) | |
| err = fd.eofError(n, err) | |
| if err == nil { | |
| rawToSockaddrInet6(o.rsa, sa6) | |
| } | |
| return n, int(o.msg.Control.Len), int(o.msg.Flags), err | |
| } | |
| // WriteMsg wraps the WSASendMsg network call. | |
| func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) { | |
| if len(p) > maxRW { | |
| return 0, 0, errors.New("packet is too large (only 1GB is allowed)") | |
| } | |
| if err := fd.writeLock(); err != nil { | |
| return 0, 0, err | |
| } | |
| defer fd.writeUnlock() | |
| o := &fd.wop | |
| o.InitMsg(p, oob) | |
| if sa != nil { | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| len, err := sockaddrToRaw(o.rsa, sa) | |
| if err != nil { | |
| return 0, 0, err | |
| } | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = len | |
| } | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) | |
| }) | |
| return n, int(o.msg.Control.Len), err | |
| } | |
| // WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4. | |
| func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) { | |
| if len(p) > maxRW { | |
| return 0, 0, errors.New("packet is too large (only 1GB is allowed)") | |
| } | |
| if err := fd.writeLock(); err != nil { | |
| return 0, 0, err | |
| } | |
| defer fd.writeUnlock() | |
| o := &fd.wop | |
| o.InitMsg(p, oob) | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| len := sockaddrInet4ToRaw(o.rsa, sa) | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = len | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) | |
| }) | |
| return n, int(o.msg.Control.Len), err | |
| } | |
| // WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6. | |
| func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) { | |
| if len(p) > maxRW { | |
| return 0, 0, errors.New("packet is too large (only 1GB is allowed)") | |
| } | |
| if err := fd.writeLock(); err != nil { | |
| return 0, 0, err | |
| } | |
| defer fd.writeUnlock() | |
| o := &fd.wop | |
| o.InitMsg(p, oob) | |
| if o.rsa == nil { | |
| o.rsa = new(syscall.RawSockaddrAny) | |
| } | |
| len := sockaddrInet6ToRaw(o.rsa, sa) | |
| o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) | |
| o.msg.Namelen = len | |
| n, err := execIO(o, func(o *operation) error { | |
| return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) | |
| }) | |
| return n, int(o.msg.Control.Len), err | |
| } |