| // 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 net | |
| import ( | |
| "errors" | |
| "syscall" | |
| "unsafe" | |
| ) | |
| func readRawConn(c syscall.RawConn, b []byte) (int, error) { | |
| var operr error | |
| var n int | |
| err := c.Read(func(s uintptr) bool { | |
| var read uint32 | |
| var flags uint32 | |
| var buf syscall.WSABuf | |
| buf.Buf = &b[0] | |
| buf.Len = uint32(len(b)) | |
| operr = syscall.WSARecv(syscall.Handle(s), &buf, 1, &read, &flags, nil, nil) | |
| n = int(read) | |
| return true | |
| }) | |
| if err != nil { | |
| return n, err | |
| } | |
| return n, operr | |
| } | |
| func writeRawConn(c syscall.RawConn, b []byte) error { | |
| var operr error | |
| err := c.Write(func(s uintptr) bool { | |
| var written uint32 | |
| var buf syscall.WSABuf | |
| buf.Buf = &b[0] | |
| buf.Len = uint32(len(b)) | |
| operr = syscall.WSASend(syscall.Handle(s), &buf, 1, &written, 0, nil, nil) | |
| return true | |
| }) | |
| if err != nil { | |
| return err | |
| } | |
| return operr | |
| } | |
| func controlRawConn(c syscall.RawConn, addr Addr) error { | |
| var operr error | |
| fn := func(s uintptr) { | |
| var v, l int32 | |
| l = int32(unsafe.Sizeof(v)) | |
| operr = syscall.Getsockopt(syscall.Handle(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, (*byte)(unsafe.Pointer(&v)), &l) | |
| if operr != nil { | |
| return | |
| } | |
| switch addr := addr.(type) { | |
| case *TCPAddr: | |
| // There's no guarantee that IP-level socket | |
| // options work well with dual stack sockets. | |
| // A simple solution would be to take a look | |
| // at the bound address to the raw connection | |
| // and to classify the address family of the | |
| // underlying socket by the bound address: | |
| // | |
| // - When IP.To16() != nil and IP.To4() == nil, | |
| // we can assume that the raw connection | |
| // consists of an IPv6 socket using only | |
| // IPv6 addresses. | |
| // | |
| // - When IP.To16() == nil and IP.To4() != nil, | |
| // the raw connection consists of an IPv4 | |
| // socket using only IPv4 addresses. | |
| // | |
| // - Otherwise, the raw connection is a dual | |
| // stack socket, an IPv6 socket using IPv6 | |
| // addresses including IPv4-mapped or | |
| // IPv4-embedded IPv6 addresses. | |
| if addr.IP.To16() != nil && addr.IP.To4() == nil { | |
| operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) | |
| } else if addr.IP.To16() == nil && addr.IP.To4() != nil { | |
| operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) | |
| } | |
| } | |
| } | |
| if err := c.Control(fn); err != nil { | |
| return err | |
| } | |
| return operr | |
| } | |
| func controlOnConnSetup(network string, address string, c syscall.RawConn) error { | |
| var operr error | |
| var fn func(uintptr) | |
| switch network { | |
| case "tcp", "udp", "ip": | |
| return errors.New("ambiguous network: " + network) | |
| default: | |
| switch network[len(network)-1] { | |
| case '4': | |
| fn = func(s uintptr) { | |
| operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) | |
| } | |
| case '6': | |
| fn = func(s uintptr) { | |
| operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) | |
| } | |
| default: | |
| return errors.New("unknown network: " + network) | |
| } | |
| } | |
| if err := c.Control(fn); err != nil { | |
| return err | |
| } | |
| return operr | |
| } |