@ -830,8 +830,7 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
go func ( ) {
go func ( ) {
// Read a packet, and process any STUN packets before returning.
// Read a packet, and process any STUN packets before returning.
for {
for {
var pAddr net . Addr
n , pAddr , err := c . pconn . ReadFrom ( b )
n , pAddr , err = c . pconn . ReadFrom ( b )
if err != nil {
if err != nil {
select {
select {
case c . udpRecvCh <- udpReadResult { err : err } :
case c . udpRecvCh <- udpReadResult { err : err } :
@ -854,6 +853,10 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
}
}
} ( )
} ( )
// Once the above goroutine has started, it owns b until it writes
// to udpRecvCh. The code below must not access b until it's
// completed a successful receive on udpRecvCh.
var addrSet * AddrSet
var addrSet * AddrSet
select {
select {
@ -863,13 +866,18 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
select {
select {
case <- c . udpRecvCh :
case <- c . udpRecvCh :
// It's likely an error, since we just canceled the read.
// It's likely an error, since we just canceled the read.
// But there's a small window where the pconn.ReadFrom could've
// But there's a small window where the pconn.ReadFrom
// succeeded but not yet sent, and we got into the derp recv path
// could've succeeded but not yet sent, and we got into
// first. In that case this udpReadResult is a real non-err packet
// the derp recv path first. In that case this
// and we need to choose which to use. Currently, arbitrarily, we currently
// udpReadResult is a real non-err packet and we need to
// select DERP and discard this result entirely.
// choose which to use. Currently, arbitrarily, we
// The main point of this receive, though, is to make sure that the goroutine
// currently select DERP and discard this result entirely.
// is done with our b []byte buf.
//
// TODO(danderson): don't just discard packets here, it
// makes the stack unreliable and harder to test.
//
// The main point of this receive, though, is to make sure
// that the goroutine is done with our b []byte buf.
c . pconn . SetReadDeadline ( time . Time { } )
c . pconn . SetReadDeadline ( time . Time { } )
case <- c . donec ( ) :
case <- c . donec ( ) :
return 0 , nil , nil , errors . New ( "Conn closed" )
return 0 , nil , nil , errors . New ( "Conn closed" )