1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package net
6
7import (
8	"errors"
9	"syscall"
10	"unsafe"
11)
12
13func readRawConn(c syscall.RawConn, b []byte) (int, error) {
14	var operr error
15	var n int
16	err := c.Read(func(s uintptr) bool {
17		var read uint32
18		var flags uint32
19		var buf syscall.WSABuf
20		buf.Buf = &b[0]
21		buf.Len = uint32(len(b))
22		operr = syscall.WSARecv(syscall.Handle(s), &buf, 1, &read, &flags, nil, nil)
23		n = int(read)
24		return true
25	})
26	if err != nil {
27		return n, err
28	}
29	return n, operr
30}
31
32func writeRawConn(c syscall.RawConn, b []byte) error {
33	var operr error
34	err := c.Write(func(s uintptr) bool {
35		var written uint32
36		var buf syscall.WSABuf
37		buf.Buf = &b[0]
38		buf.Len = uint32(len(b))
39		operr = syscall.WSASend(syscall.Handle(s), &buf, 1, &written, 0, nil, nil)
40		return true
41	})
42	if err != nil {
43		return err
44	}
45	return operr
46}
47
48func controlRawConn(c syscall.RawConn, addr Addr) error {
49	var operr error
50	fn := func(s uintptr) {
51		var v, l int32
52		l = int32(unsafe.Sizeof(v))
53		operr = syscall.Getsockopt(syscall.Handle(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, (*byte)(unsafe.Pointer(&v)), &l)
54		if operr != nil {
55			return
56		}
57		switch addr := addr.(type) {
58		case *TCPAddr:
59			// There's no guarantee that IP-level socket
60			// options work well with dual stack sockets.
61			// A simple solution would be to take a look
62			// at the bound address to the raw connection
63			// and to classify the address family of the
64			// underlying socket by the bound address:
65			//
66			// - When IP.To16() != nil and IP.To4() == nil,
67			//   we can assume that the raw connection
68			//   consists of an IPv6 socket using only
69			//   IPv6 addresses.
70			//
71			// - When IP.To16() == nil and IP.To4() != nil,
72			//   the raw connection consists of an IPv4
73			//   socket using only IPv4 addresses.
74			//
75			// - Otherwise, the raw connection is a dual
76			//   stack socket, an IPv6 socket using IPv6
77			//   addresses including IPv4-mapped or
78			//   IPv4-embedded IPv6 addresses.
79			if addr.IP.To16() != nil && addr.IP.To4() == nil {
80				operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1)
81			} else if addr.IP.To16() == nil && addr.IP.To4() != nil {
82				operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1)
83			}
84		}
85	}
86	if err := c.Control(fn); err != nil {
87		return err
88	}
89	return operr
90}
91
92func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
93	var operr error
94	var fn func(uintptr)
95	switch network {
96	case "tcp", "udp", "ip":
97		return errors.New("ambiguous network: " + network)
98	default:
99		switch network[len(network)-1] {
100		case '4':
101			fn = func(s uintptr) {
102				operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1)
103			}
104		case '6':
105			fn = func(s uintptr) {
106				operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1)
107			}
108		default:
109			return errors.New("unknown network: " + network)
110		}
111	}
112	if err := c.Control(fn); err != nil {
113		return err
114	}
115	return operr
116}
117