github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/integrationtests/tools/proxy/proxy.go (about) 1 package quicproxy 2 3 import ( 4 "net" 5 "sort" 6 "sync" 7 "time" 8 9 "github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol" 10 "github.com/danielpfeifer02/quic-go-prio-packs/internal/utils" 11 ) 12 13 // Connection is a UDP connection 14 type connection struct { 15 ClientAddr *net.UDPAddr // Address of the client 16 ServerConn *net.UDPConn // UDP connection to server 17 18 incomingPackets chan packetEntry 19 20 Incoming *queue 21 Outgoing *queue 22 } 23 24 func (c *connection) queuePacket(t time.Time, b []byte) { 25 c.incomingPackets <- packetEntry{Time: t, Raw: b} 26 } 27 28 // Direction is the direction a packet is sent. 29 type Direction int 30 31 const ( 32 // DirectionIncoming is the direction from the client to the server. 33 DirectionIncoming Direction = iota 34 // DirectionOutgoing is the direction from the server to the client. 35 DirectionOutgoing 36 // DirectionBoth is both incoming and outgoing 37 DirectionBoth 38 ) 39 40 type packetEntry struct { 41 Time time.Time 42 Raw []byte 43 } 44 45 type packetEntries []packetEntry 46 47 func (e packetEntries) Len() int { return len(e) } 48 func (e packetEntries) Less(i, j int) bool { return e[i].Time.Before(e[j].Time) } 49 func (e packetEntries) Swap(i, j int) { e[i], e[j] = e[j], e[i] } 50 51 type queue struct { 52 sync.Mutex 53 54 timer *utils.Timer 55 Packets packetEntries 56 } 57 58 func newQueue() *queue { 59 return &queue{timer: utils.NewTimer()} 60 } 61 62 func (q *queue) Add(e packetEntry) { 63 q.Lock() 64 q.Packets = append(q.Packets, e) 65 if len(q.Packets) > 1 { 66 lastIndex := len(q.Packets) - 1 67 if q.Packets[lastIndex].Time.Before(q.Packets[lastIndex-1].Time) { 68 sort.Stable(q.Packets) 69 } 70 } 71 q.timer.Reset(q.Packets[0].Time) 72 q.Unlock() 73 } 74 75 func (q *queue) Get() []byte { 76 q.Lock() 77 raw := q.Packets[0].Raw 78 q.Packets = q.Packets[1:] 79 if len(q.Packets) > 0 { 80 q.timer.Reset(q.Packets[0].Time) 81 } 82 q.Unlock() 83 return raw 84 } 85 86 func (q *queue) Timer() <-chan time.Time { return q.timer.Chan() } 87 func (q *queue) SetTimerRead() { q.timer.SetRead() } 88 89 func (q *queue) Close() { q.timer.Stop() } 90 91 func (d Direction) String() string { 92 switch d { 93 case DirectionIncoming: 94 return "Incoming" 95 case DirectionOutgoing: 96 return "Outgoing" 97 case DirectionBoth: 98 return "both" 99 default: 100 panic("unknown direction") 101 } 102 } 103 104 // Is says if one direction matches another direction. 105 // For example, incoming matches both incoming and both, but not outgoing. 106 func (d Direction) Is(dir Direction) bool { 107 if d == DirectionBoth || dir == DirectionBoth { 108 return true 109 } 110 return d == dir 111 } 112 113 // DropCallback is a callback that determines which packet gets dropped. 114 type DropCallback func(dir Direction, packet []byte) bool 115 116 // NoDropper doesn't drop packets. 117 var NoDropper DropCallback = func(Direction, []byte) bool { 118 return false 119 } 120 121 // DelayCallback is a callback that determines how much delay to apply to a packet. 122 type DelayCallback func(dir Direction, packet []byte) time.Duration 123 124 // NoDelay doesn't apply a delay. 125 var NoDelay DelayCallback = func(Direction, []byte) time.Duration { 126 return 0 127 } 128 129 // Opts are proxy options. 130 type Opts struct { 131 // The address this proxy proxies packets to. 132 RemoteAddr string 133 // DropPacket determines whether a packet gets dropped. 134 DropPacket DropCallback 135 // DelayPacket determines how long a packet gets delayed. This allows 136 // simulating a connection with non-zero RTTs. 137 // Note that the RTT is the sum of the delay for the incoming and the outgoing packet. 138 DelayPacket DelayCallback 139 } 140 141 // QuicProxy is a QUIC proxy that can drop and delay packets. 142 type QuicProxy struct { 143 mutex sync.Mutex 144 145 closeChan chan struct{} 146 147 conn *net.UDPConn 148 serverAddr *net.UDPAddr 149 150 dropPacket DropCallback 151 delayPacket DelayCallback 152 153 // Mapping from client addresses (as host:port) to connection 154 clientDict map[string]*connection 155 156 logger utils.Logger 157 } 158 159 // NewQuicProxy creates a new UDP proxy 160 func NewQuicProxy(local string, opts *Opts) (*QuicProxy, error) { 161 if opts == nil { 162 opts = &Opts{} 163 } 164 laddr, err := net.ResolveUDPAddr("udp", local) 165 if err != nil { 166 return nil, err 167 } 168 conn, err := net.ListenUDP("udp", laddr) 169 if err != nil { 170 return nil, err 171 } 172 if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { 173 return nil, err 174 } 175 if err := conn.SetWriteBuffer(protocol.DesiredSendBufferSize); err != nil { 176 return nil, err 177 } 178 raddr, err := net.ResolveUDPAddr("udp", opts.RemoteAddr) 179 if err != nil { 180 return nil, err 181 } 182 183 packetDropper := NoDropper 184 if opts.DropPacket != nil { 185 packetDropper = opts.DropPacket 186 } 187 188 packetDelayer := NoDelay 189 if opts.DelayPacket != nil { 190 packetDelayer = opts.DelayPacket 191 } 192 193 p := QuicProxy{ 194 clientDict: make(map[string]*connection), 195 conn: conn, 196 closeChan: make(chan struct{}), 197 serverAddr: raddr, 198 dropPacket: packetDropper, 199 delayPacket: packetDelayer, 200 logger: utils.DefaultLogger.WithPrefix("proxy"), 201 } 202 203 p.logger.Debugf("Starting UDP Proxy %s <-> %s", conn.LocalAddr(), raddr) 204 go p.runProxy() 205 return &p, nil 206 } 207 208 // Close stops the UDP Proxy 209 func (p *QuicProxy) Close() error { 210 p.mutex.Lock() 211 defer p.mutex.Unlock() 212 close(p.closeChan) 213 for _, c := range p.clientDict { 214 if err := c.ServerConn.Close(); err != nil { 215 return err 216 } 217 c.Incoming.Close() 218 c.Outgoing.Close() 219 } 220 return p.conn.Close() 221 } 222 223 // LocalAddr is the address the proxy is listening on. 224 func (p *QuicProxy) LocalAddr() net.Addr { 225 return p.conn.LocalAddr() 226 } 227 228 // LocalPort is the UDP port number the proxy is listening on. 229 func (p *QuicProxy) LocalPort() int { 230 return p.conn.LocalAddr().(*net.UDPAddr).Port 231 } 232 233 func (p *QuicProxy) newConnection(cliAddr *net.UDPAddr) (*connection, error) { 234 conn, err := net.DialUDP("udp", nil, p.serverAddr) 235 if err != nil { 236 return nil, err 237 } 238 if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { 239 return nil, err 240 } 241 if err := conn.SetWriteBuffer(protocol.DesiredSendBufferSize); err != nil { 242 return nil, err 243 } 244 return &connection{ 245 ClientAddr: cliAddr, 246 ServerConn: conn, 247 incomingPackets: make(chan packetEntry, 10), 248 Incoming: newQueue(), 249 Outgoing: newQueue(), 250 }, nil 251 } 252 253 // runProxy listens on the proxy address and handles incoming packets. 254 func (p *QuicProxy) runProxy() error { 255 for { 256 buffer := make([]byte, protocol.MaxPacketBufferSize) 257 n, cliaddr, err := p.conn.ReadFromUDP(buffer) 258 if err != nil { 259 return err 260 } 261 raw := buffer[0:n] 262 263 saddr := cliaddr.String() 264 p.mutex.Lock() 265 conn, ok := p.clientDict[saddr] 266 267 if !ok { 268 conn, err = p.newConnection(cliaddr) 269 if err != nil { 270 p.mutex.Unlock() 271 return err 272 } 273 p.clientDict[saddr] = conn 274 go p.runIncomingConnection(conn) 275 go p.runOutgoingConnection(conn) 276 } 277 p.mutex.Unlock() 278 279 if p.dropPacket(DirectionIncoming, raw) { 280 if p.logger.Debug() { 281 p.logger.Debugf("dropping incoming packet(%d bytes)", n) 282 } 283 continue 284 } 285 286 delay := p.delayPacket(DirectionIncoming, raw) 287 if delay == 0 { 288 if p.logger.Debug() { 289 p.logger.Debugf("forwarding incoming packet (%d bytes) to %s", len(raw), conn.ServerConn.RemoteAddr()) 290 } 291 if _, err := conn.ServerConn.Write(raw); err != nil { 292 return err 293 } 294 } else { 295 now := time.Now() 296 if p.logger.Debug() { 297 p.logger.Debugf("delaying incoming packet (%d bytes) to %s by %s", len(raw), conn.ServerConn.RemoteAddr(), delay) 298 } 299 conn.queuePacket(now.Add(delay), raw) 300 } 301 } 302 } 303 304 // runConnection handles packets from server to a single client 305 func (p *QuicProxy) runOutgoingConnection(conn *connection) error { 306 outgoingPackets := make(chan packetEntry, 10) 307 go func() { 308 for { 309 buffer := make([]byte, protocol.MaxPacketBufferSize) 310 n, err := conn.ServerConn.Read(buffer) 311 if err != nil { 312 return 313 } 314 raw := buffer[0:n] 315 316 if p.dropPacket(DirectionOutgoing, raw) { 317 if p.logger.Debug() { 318 p.logger.Debugf("dropping outgoing packet(%d bytes)", n) 319 } 320 continue 321 } 322 323 delay := p.delayPacket(DirectionOutgoing, raw) 324 if delay == 0 { 325 if p.logger.Debug() { 326 p.logger.Debugf("forwarding outgoing packet (%d bytes) to %s", len(raw), conn.ClientAddr) 327 } 328 if _, err := p.conn.WriteToUDP(raw, conn.ClientAddr); err != nil { 329 return 330 } 331 } else { 332 now := time.Now() 333 if p.logger.Debug() { 334 p.logger.Debugf("delaying outgoing packet (%d bytes) to %s by %s", len(raw), conn.ClientAddr, delay) 335 } 336 outgoingPackets <- packetEntry{Time: now.Add(delay), Raw: raw} 337 } 338 } 339 }() 340 341 for { 342 select { 343 case <-p.closeChan: 344 return nil 345 case e := <-outgoingPackets: 346 conn.Outgoing.Add(e) 347 case <-conn.Outgoing.Timer(): 348 conn.Outgoing.SetTimerRead() 349 if _, err := p.conn.WriteTo(conn.Outgoing.Get(), conn.ClientAddr); err != nil { 350 return err 351 } 352 } 353 } 354 } 355 356 func (p *QuicProxy) runIncomingConnection(conn *connection) error { 357 for { 358 select { 359 case <-p.closeChan: 360 return nil 361 case e := <-conn.incomingPackets: 362 // Send the packet to the server 363 conn.Incoming.Add(e) 364 case <-conn.Incoming.Timer(): 365 conn.Incoming.SetTimerRead() 366 if _, err := conn.ServerConn.Write(conn.Incoming.Get()); err != nil { 367 return err 368 } 369 } 370 } 371 }