diff --git a/msg.go b/msg.go index e04fb5d77..cb3b1eba0 100644 --- a/msg.go +++ b/msg.go @@ -15,10 +15,9 @@ import ( "encoding/binary" "fmt" "math/big" - "math/rand" + mrand "math/rand" "strconv" "strings" - "sync" ) const ( @@ -74,52 +73,26 @@ var ( ) // Id by default, returns a 16 bits random number to be used as a -// message id. The random provided should be good enough. This being a -// variable the function can be reassigned to a custom function. -// For instance, to make it return a static value: +// message id. This being a variable the function can be reassigned +// to a custom function. For instance, to make it return a static value: // // dns.Id = func() uint16 { return 3 } +// +// Randomness comes from crypto/rand. In the rare case that there is an error +// getting bytes from that source, this function will silently fall back to +// the predictable math/rand. If your program has more stringent requirements, +// you should override Id to do something different (e.g., terminate). var Id = id -var ( - idLock sync.Mutex - idRand *rand.Rand -) - // id returns a 16 bits random number to be used as a // message id. The random provided should be good enough. func id() uint16 { - idLock.Lock() - - if idRand == nil { - // This (partially) works around - // https://github.com/golang/go/issues/11833 by only - // seeding idRand upon the first call to id. - - var seed int64 - var buf [8]byte - - if _, err := crand.Read(buf[:]); err == nil { - seed = int64(binary.LittleEndian.Uint64(buf[:])) - } else { - seed = rand.Int63() - } - - idRand = rand.New(rand.NewSource(seed)) + var v uint16 + err := binary.Read(crand.Reader, binary.BigEndian, &v) + if err != nil { + v = uint16(mrand.Uint32()) } - - // The call to idRand.Uint32 must be within the - // mutex lock because *rand.Rand is not safe for - // concurrent use. - // - // There is no added performance overhead to calling - // idRand.Uint32 inside a mutex lock over just - // calling rand.Uint32 as the global math/rand rng - // is internally protected by a sync.Mutex. - id := uint16(idRand.Uint32()) - - idLock.Unlock() - return id + return v } // MsgHdr is a a manually-unpacked version of (id, bits).