package awgserver
import (
"net"
"time"
)
// FilteredConn implements net.PacketConn. It receives only non-AWG packets
// from MuxBind via a channel, and writes QUIC responses through the shared
// UDP socket.
type FilteredConn struct {
quicCh chan quicPacket
udpConn *net.UDPConn
closed chan struct{}
readDeadline time.Time
writeDeadline time.Time
}
func newFilteredConn(quicCh chan quicPacket, udpConn *net.UDPConn) *FilteredConn {
return &FilteredConn{
quicCh: quicCh,
udpConn: udpConn,
closed: make(chan struct{}),
}
}
// ReadFrom blocks until a non-AWG packet arrives from MuxBind.
func (fc *FilteredConn) ReadFrom(p []byte) (int, net.Addr, error) {
var timer <-chan time.Time
if !fc.readDeadline.IsZero() {
d := time.Until(fc.readDeadline)
if d <= 0 {
return 0, nil, &net.OpError{Op: "read", Err: errTimeout}
}
t := time.NewTimer(d)
defer t.Stop()
timer = t.C
}
select {
case pkt := <-fc.quicCh:
n := copy(p, pkt.data)
return n, pkt.addr, nil
case <-timer:
return 0, nil, &net.OpError{Op: "read", Err: errTimeout}
case <-fc.closed:
return 0, nil, net.ErrClosed
}
}
// WriteTo sends QUIC responses through the shared socket.
func (fc *FilteredConn) WriteTo(p []byte, addr net.Addr) (int, error) {
udpAddr, ok := addr.(*net.UDPAddr)
if !ok {
return 0, &net.OpError{Op: "write", Err: net.ErrClosed}
}
return fc.udpConn.WriteToUDP(p, udpAddr)
}
func (fc *FilteredConn) Close() error {
select {
case <-fc.closed:
default:
close(fc.closed)
}
return nil
}
func (fc *FilteredConn) LocalAddr() net.Addr {
return fc.udpConn.LocalAddr()
}
func (fc *FilteredConn) SetDeadline(t time.Time) error {
fc.readDeadline = t
fc.writeDeadline = t
return nil
}
func (fc *FilteredConn) SetReadDeadline(t time.Time) error {
fc.readDeadline = t
return nil
}
func (fc *FilteredConn) SetWriteDeadline(t time.Time) error {
fc.writeDeadline = t
return nil
}
// SyscallConn is required by quic-go's OOBCapablePacketConn check.
func (fc *FilteredConn) SyscallConn() (interface{}, error) {
return nil, &net.OpError{Op: "syscall", Err: net.ErrClosed}
}
type timeoutError struct{}
func (timeoutError) Error() string { return "i/o timeout" }
func (timeoutError) Timeout() bool { return true }
func (timeoutError) Temporary() bool { return true }
var errTimeout = timeoutError{}