package socketimport ()// maxDatagramSize bounds a single read from the UDP socket. QUIC packets never// exceed this; larger reads would be truncated by quic-go anyway.const maxDatagramSize = 1452 + 512// generous: max QUIC packet plus headroomvar ipRecvPool = make(chan []byte, 1024)// IpTransport is the direct-UDP transport: it reads datagrams from a// net.PacketConn and forwards them to the [MagicConn]'s recv channel, and sends// datagrams the magic socket routes to it. It is the Go analog of the Rust// IpTransport (iroh/src/socket/transports/ip.rs).//// Create one with [NewIpTransport] and start its recv loop with [IpTransport.Serve].typeIpTransportstruct { conn *net.UDPConn recvCh chan<- recvBatch}// NewIpTransport returns an IpTransport over conn that delivers received// datagrams to recvCh. The transport does not take ownership of conn; the caller// closes it.func ( *net.UDPConn, chan<- recvBatch) *IpTransport {return &IpTransport{conn: , recvCh: }}// LocalAddr returns the bound local address of the underlying socket.func ( *IpTransport) () net.Addr { return .conn.LocalAddr() }// Serve runs the receive loop until ctx is cancelled or the socket is closed.// Each datagram is delivered to the recv channel tagged with its real remote IP// address (canonicalized: an IPv4-mapped IPv6 source becomes plain IPv4, to// match iroh/src/socket/transports/ip.rs:221 to_canonical). Empty datagrams and// transient errors are skipped; a closed socket ends the loop cleanly.func ( *IpTransport) ( context.Context) {for {if .Err() != nil {return } := getIPRecvBuffer() , , := .conn.ReadFromUDPAddrPort()if != nil {putIPRecvBuffer()iferrors.Is(, net.ErrClosed) || .Err() != nil {return }// Transient read error (e.g. ICMP-driven recv error on some // platforms): keep serving.continue }if == 0 {putIPRecvBuffer()// Timeout or platform quirk; nothing to deliver.continue }recordUDPReceive(1, false)// The transport address is internal to iroh and is always the canonical // (unmapped) form. iroh/src/socket/transports/ip.rs:219. := canonicalAddrPort() := recvBatch{data: [:], ip: , releaseIP: true}if !.enqueue(, ) {return } }}func ( *IpTransport) ( context.Context, recvBatch) bool {select {case .recvCh<- :returntruedefault: }select {case .recvCh<- :returntruecase<-.Done(): .release()returnfalse }}func getIPRecvBuffer() []byte {select {case := <-ipRecvPool:returndefault:returnmake([]byte, maxDatagramSize) }}func putIPRecvBuffer( []byte) {ifcap() != maxDatagramSize {return }select {caseipRecvPool<- [:maxDatagramSize]:default: }}// send writes p to the IP destination dst. The destination is canonicalized so// an IPv4-mapped IPv6 address is sent as plain IPv4, matching// iroh/src/socket/transports/ip.rs:310 canonical_addr. It reports the number of// bytes written.func ( *IpTransport) ( []byte, netip.AddrPort) (int, error) { = canonicalAddrPort() , := .conn.WriteToUDPAddrPort(, )return , }func canonicalAddrPort( netip.AddrPort) netip.AddrPort { := .Addr()if !.Is4In6() {return }returnnetip.AddrPortFrom(.Unmap(), .Port())}func udpAddrFromAddrPort( netip.AddrPort) *net.UDPAddr {returnnet.UDPAddrFromAddrPort(canonicalAddrPort())}func addrPortFromUDPAddr( *net.UDPAddr) netip.AddrPort {returncanonicalAddrPort(.AddrPort())}// addrPort extracts a netip.AddrPort from a net.Addr, handling the *net.UDPAddr// that net.PacketConn.ReadFrom returns as well as anything already carrying an// AddrPort.func addrPort( net.Addr) (netip.AddrPort, bool) {switch v := .(type) {case *net.UDPAddr:returnaddrPortFromUDPAddr(), truecaseinterface{ () netip.AddrPort }:returncanonicalAddrPort(.()), truedefault: , := netip.ParseAddrPort(.String())if != nil {returnnetip.AddrPort{}, false }returncanonicalAddrPort(), true }}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.