1// Copyright 2010 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
5// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
6
7package net
8
9import (
10	"context"
11	"syscall"
12)
13
14func sockaddrToIP(sa syscall.Sockaddr) Addr {
15	switch sa := sa.(type) {
16	case *syscall.SockaddrInet4:
17		return &IPAddr{IP: sa.Addr[0:]}
18	case *syscall.SockaddrInet6:
19		return &IPAddr{IP: sa.Addr[0:], Zone: zoneCache.name(int(sa.ZoneId))}
20	}
21	return nil
22}
23
24func (a *IPAddr) family() int {
25	if a == nil || len(a.IP) <= IPv4len {
26		return syscall.AF_INET
27	}
28	if a.IP.To4() != nil {
29		return syscall.AF_INET
30	}
31	return syscall.AF_INET6
32}
33
34func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
35	if a == nil {
36		return nil, nil
37	}
38	return ipToSockaddr(family, a.IP, 0, a.Zone)
39}
40
41func (a *IPAddr) toLocal(net string) sockaddr {
42	return &IPAddr{loopbackIP(net), a.Zone}
43}
44
45func (c *IPConn) readFrom(b []byte) (int, *IPAddr, error) {
46	// TODO(cw,rsc): consider using readv if we know the family
47	// type to avoid the header trim/copy
48	var addr *IPAddr
49	n, sa, err := c.fd.readFrom(b)
50	switch sa := sa.(type) {
51	case *syscall.SockaddrInet4:
52		addr = &IPAddr{IP: sa.Addr[0:]}
53		n = stripIPv4Header(n, b)
54	case *syscall.SockaddrInet6:
55		addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneCache.name(int(sa.ZoneId))}
56	}
57	return n, addr, err
58}
59
60func stripIPv4Header(n int, b []byte) int {
61	if len(b) < 20 {
62		return n
63	}
64	l := int(b[0]&0x0f) << 2
65	if 20 > l || l > len(b) {
66		return n
67	}
68	if b[0]>>4 != 4 {
69		return n
70	}
71	copy(b, b[l:])
72	return n - l
73}
74
75func (c *IPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
76	var sa syscall.Sockaddr
77	n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
78	switch sa := sa.(type) {
79	case *syscall.SockaddrInet4:
80		addr = &IPAddr{IP: sa.Addr[0:]}
81	case *syscall.SockaddrInet6:
82		addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneCache.name(int(sa.ZoneId))}
83	}
84	return
85}
86
87func (c *IPConn) writeTo(b []byte, addr *IPAddr) (int, error) {
88	if c.fd.isConnected {
89		return 0, ErrWriteToConnected
90	}
91	if addr == nil {
92		return 0, errMissingAddress
93	}
94	sa, err := addr.sockaddr(c.fd.family)
95	if err != nil {
96		return 0, err
97	}
98	return c.fd.writeTo(b, sa)
99}
100
101func (c *IPConn) writeMsg(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
102	if c.fd.isConnected {
103		return 0, 0, ErrWriteToConnected
104	}
105	if addr == nil {
106		return 0, 0, errMissingAddress
107	}
108	sa, err := addr.sockaddr(c.fd.family)
109	if err != nil {
110		return 0, 0, err
111	}
112	return c.fd.writeMsg(b, oob, sa)
113}
114
115func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
116	network, proto, err := parseNetwork(ctx, netProto, true)
117	if err != nil {
118		return nil, err
119	}
120	switch network {
121	case "ip", "ip4", "ip6":
122	default:
123		return nil, UnknownNetworkError(netProto)
124	}
125	if raddr == nil {
126		return nil, errMissingAddress
127	}
128	fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial")
129	if err != nil {
130		return nil, err
131	}
132	return newIPConn(fd), nil
133}
134
135func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) {
136	network, proto, err := parseNetwork(ctx, netProto, true)
137	if err != nil {
138		return nil, err
139	}
140	switch network {
141	case "ip", "ip4", "ip6":
142	default:
143		return nil, UnknownNetworkError(netProto)
144	}
145	fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen")
146	if err != nil {
147		return nil, err
148	}
149	return newIPConn(fd), nil
150}
151