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{}