// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package net implements DTLS specific networking primitives. // NOTE: this package is an adaption of pion/transport/packetio that allows for // storing a remote address alongside each packet in the buffer and implements // relevant methods of net.PacketConn. If possible, the updates made in this // repository will be reflected back upstream. If not, it is likely that this // will be moved to a public package in this repository. // // This package was migrated from pion/transport/packetio at // https://github.com/pion/transport/commit/6890c795c807a617c054149eee40a69d7fdfbfdb
package net import ( ) // ErrTimeout indicates that deadline was reached before operation could be // completed. var ErrTimeout = errors.New("buffer: i/o timeout") // AddrPacket is a packet payload and the associated remote address from which // it was received. type AddrPacket struct { addr net.Addr data bytes.Buffer } // PacketBuffer is a circular buffer for network packets. Each slot in the // buffer contains the remote address from which the packet was received, as // well as the packet data. type PacketBuffer struct { mutex sync.Mutex packets []AddrPacket write, read int // full indicates whether the buffer is full, which is needed to distinguish // when the write pointer and read pointer are at the same index. full bool notify chan struct{} closed bool readDeadline *deadline.Deadline } // NewPacketBuffer creates a new PacketBuffer. func () *PacketBuffer { return &PacketBuffer{ readDeadline: deadline.New(), // In the narrow context in which this package is currently used, there // will always be at least one packet written to the buffer. Therefore, // we opt to allocate with size of 1 during construction, rather than // waiting until that first packet is written. packets: make([]AddrPacket, 1), full: false, } } // WriteTo writes a single packet to the buffer. The supplied address will // remain associated with the packet. func ( *PacketBuffer) ( []byte, net.Addr) (int, error) { .mutex.Lock() if .closed { .mutex.Unlock() return 0, io.ErrClosedPipe } var chan struct{} if .notify != nil { = .notify .notify = nil } // Check to see if we are full. if .full { // If so, grow AddrPacket buffer. var int if len(.packets) < 128 { // Double the number of packets. = len(.packets) * 2 } else { // Increase the number of packets by 25%. = 5 * len(.packets) / 4 } := make([]AddrPacket, ) var int if .read < .write { = copy(, .packets[.read:.write]) } else { = copy(, .packets[.read:]) += copy([:], .packets[:.write]) } .packets = // Update write pointer to point to new location and mark buffer as not // full. .write = .full = false } // Store the packet at the write pointer. := &.packets[.write] .data.Reset() , := .data.Write() if != nil { .mutex.Unlock() return , } .addr = // Increment write pointer. .write++ // If the write pointer is equal to the length of the buffer, wrap around. if len(.packets) == .write { .write = 0 } // If a write resulted in making write and read pointers equivalent, then we // are full. if .write == .read { .full = true } .mutex.Unlock() if != nil { close() } return , nil } // ReadFrom reads a single packet from the buffer, or blocks until one is // available. func ( *PacketBuffer) ( []byte) ( int, net.Addr, error) { //nolint:cyclop select { case <-.readDeadline.Done(): return 0, nil, ErrTimeout default: } for { .mutex.Lock() if .read != .write || .full { := .packets[.read] if len() < .data.Len() { .mutex.Unlock() return 0, nil, io.ErrShortBuffer } // Copy packet data from buffer. , := .data.Read() if != nil { .mutex.Unlock() return , nil, } // Advance read pointer. .read++ if len(.packets) == .read { .read = 0 } // If we were full before reading and have successfully read, we are // no longer full. if .full { .full = false } .mutex.Unlock() return , .addr, nil } if .closed { .mutex.Unlock() return 0, nil, io.EOF } if .notify == nil { .notify = make(chan struct{}) } := .notify .mutex.Unlock() select { case <-.readDeadline.Done(): return 0, nil, ErrTimeout case <-: } } } // Close closes the buffer, allowing unread packets to be read, but erroring on // any new writes. func ( *PacketBuffer) () ( error) { .mutex.Lock() if .closed { .mutex.Unlock() return nil } := .notify .notify = nil .closed = true .mutex.Unlock() if != nil { close() } return nil } // SetReadDeadline sets the read deadline for the buffer. func ( *PacketBuffer) ( time.Time) error { .readDeadline.Set() return nil }