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//go:build unix || js || wasip1 || 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, 0)
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 (sd *sysDialer) dialIP(ctx context.Context, laddr, raddr *IPAddr) (*IPConn, error) {
116	network, proto, err := parseNetwork(ctx, sd.network, true)
117	if err != nil {
118		return nil, err
119	}
120	switch network {
121	case "ip", "ip4", "ip6":
122	default:
123		return nil, UnknownNetworkError(sd.network)
124	}
125	ctrlCtxFn := sd.Dialer.ControlContext
126	if ctrlCtxFn == nil && sd.Dialer.Control != nil {
127		ctrlCtxFn = func(ctx context.Context, network, address string, c syscall.RawConn) error {
128			return sd.Dialer.Control(network, address, c)
129		}
130	}
131	fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial", ctrlCtxFn)
132	if err != nil {
133		return nil, err
134	}
135	return newIPConn(fd), nil
136}
137
138func (sl *sysListener) listenIP(ctx context.Context, laddr *IPAddr) (*IPConn, error) {
139	network, proto, err := parseNetwork(ctx, sl.network, true)
140	if err != nil {
141		return nil, err
142	}
143	switch network {
144	case "ip", "ip4", "ip6":
145	default:
146		return nil, UnknownNetworkError(sl.network)
147	}
148	var ctrlCtxFn func(ctx context.Context, network, address string, c syscall.RawConn) error
149	if sl.ListenConfig.Control != nil {
150		ctrlCtxFn = func(ctx context.Context, network, address string, c syscall.RawConn) error {
151			return sl.ListenConfig.Control(network, address, c)
152		}
153	}
154	fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen", ctrlCtxFn)
155	if err != nil {
156		return nil, err
157	}
158	return newIPConn(fd), nil
159}
160