-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconn_ipv4.go
More file actions
93 lines (77 loc) · 2.68 KB
/
conn_ipv4.go
File metadata and controls
93 lines (77 loc) · 2.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package zeroconf
import (
"fmt"
"net"
"runtime"
"syscall"
"github.com/enbility/zeroconf/v3/api"
"golang.org/x/net/ipv4"
)
// ipv4PacketConn wraps ipv4.PacketConn to implement api.PacketConn interface.
// This adapter is needed because ipv4.PacketConn uses ControlMessage for
// interface selection, but we only need the IfIndex field.
type ipv4PacketConn struct {
conn *ipv4.PacketConn
}
// Compile-time interface check
var _ api.PacketConn = (*ipv4PacketConn)(nil)
// newIPv4PacketConn creates a new IPv4 PacketConn wrapper.
func newIPv4PacketConn(conn *ipv4.PacketConn) *ipv4PacketConn {
return &ipv4PacketConn{conn: conn}
}
func (c *ipv4PacketConn) ReadFrom(b []byte) (n int, ifIndex int, src net.Addr, err error) {
n, cm, src, err := c.conn.ReadFrom(b)
if cm != nil {
ifIndex = cm.IfIndex
}
return
}
func (c *ipv4PacketConn) WriteTo(b []byte, ifIndex int, dst net.Addr) (n int, err error) {
// See https://pkg.go.dev/golang.org/x/net/ipv4#pkg-note-BUG
// On Windows, the ControlMessage for WriteTo is not implemented.
// Use SetMulticastInterface as fallback.
var cm *ipv4.ControlMessage
if ifIndex != 0 {
switch runtime.GOOS {
case "darwin", "ios", "linux":
cm = &ipv4.ControlMessage{IfIndex: ifIndex}
default:
// Windows and other platforms: validate and set interface.
// CRITICAL: Return errors instead of logging them. The caller
// (via InterfaceManager.MarkFailed) handles removal and backoff.
iface, err := net.InterfaceByIndex(ifIndex)
if err != nil {
// Interface gone - wrap with ENXIO so isInterfaceGone() detects it
return 0, fmt.Errorf("interface index %d: %w", ifIndex, syscall.ENXIO)
}
// Verify interface is actually up
if iface.Flags&net.FlagUp == 0 {
return 0, fmt.Errorf("interface %s is down: %w", iface.Name, syscall.ENETDOWN)
}
if err := c.conn.SetMulticastInterface(iface); err != nil {
// Return the actual error - may contain WSAENETDOWN or similar
return 0, fmt.Errorf("set multicast interface %s: %w", iface.Name, err)
}
}
}
return c.conn.WriteTo(b, cm, dst)
}
func (c *ipv4PacketConn) Close() error {
return c.conn.Close()
}
func (c *ipv4PacketConn) JoinGroup(ifi *net.Interface, group net.Addr) error {
return c.conn.JoinGroup(ifi, group)
}
func (c *ipv4PacketConn) LeaveGroup(ifi *net.Interface, group net.Addr) error {
return c.conn.LeaveGroup(ifi, group)
}
func (c *ipv4PacketConn) SetMulticastTTL(ttl int) error {
return c.conn.SetMulticastTTL(ttl)
}
func (c *ipv4PacketConn) SetMulticastHopLimit(hopLimit int) error {
// IPv4 doesn't have hop limit, this is a no-op
return nil
}
func (c *ipv4PacketConn) SetMulticastInterface(ifi *net.Interface) error {
return c.conn.SetMulticastInterface(ifi)
}