| // 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. |
| |
| // +build darwin dragonfly freebsd linux netbsd openbsd solaris |
| |
| package net |
| |
| import ( |
| "errors" |
| "syscall" |
| ) |
| |
| func readRawConn(c syscall.RawConn, b []byte) (int, error) { |
| var operr error |
| var n int |
| err := c.Read(func(s uintptr) bool { |
| n, operr = syscall.Read(int(s), b) |
| if operr == syscall.EAGAIN { |
| return false |
| } |
| return true |
| }) |
| if err != nil { |
| return n, err |
| } |
| if operr != nil { |
| return n, operr |
| } |
| return n, nil |
| } |
| |
| func writeRawConn(c syscall.RawConn, b []byte) error { |
| var operr error |
| err := c.Write(func(s uintptr) bool { |
| _, operr = syscall.Write(int(s), b) |
| if operr == syscall.EAGAIN { |
| return false |
| } |
| return true |
| }) |
| if err != nil { |
| return err |
| } |
| if operr != nil { |
| return operr |
| } |
| return nil |
| } |
| |
| func controlRawConn(c syscall.RawConn, addr Addr) error { |
| var operr error |
| fn := func(s uintptr) { |
| _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR) |
| 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(int(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) |
| } else if addr.IP.To16() == nil && addr.IP.To4() != nil { |
| operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) |
| } |
| } |
| } |
| if err := c.Control(fn); err != nil { |
| return err |
| } |
| if operr != nil { |
| return operr |
| } |
| return nil |
| } |
| |
| 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) |
| case "unix", "unixpacket", "unixgram": |
| fn = func(s uintptr) { |
| _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_ERROR) |
| } |
| default: |
| switch network[len(network)-1] { |
| case '4': |
| fn = func(s uintptr) { |
| operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) |
| } |
| case '6': |
| fn = func(s uintptr) { |
| operr = syscall.SetsockoptInt(int(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 |
| } |
| if operr != nil { |
| return operr |
| } |
| return nil |
| } |