Merge pull request #329 from wneessen/feature/327_simplify-message-id-generation-and-get-rid-of-randnum

Simplify message id generation and get rid of randnum
This commit is contained in:
Winni Neessen 2024-10-07 15:12:46 +02:00 committed by GitHub
commit 8faac3d101
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 20 additions and 137 deletions

18
msg.go
View file

@ -972,12 +972,12 @@ func (m *Msg) Subject(subj string) {
// SetMessageID generates and sets a unique "Message-ID" header for the Msg. // SetMessageID generates and sets a unique "Message-ID" header for the Msg.
// //
// This method creates a "Message-ID" string using the current process ID, random numbers, and the hostname // This method creates a "Message-ID" string using a randomly generated string and the hostname of the machine.
// of the machine. The generated ID helps uniquely identify the message in email systems, facilitating tracking // The generated ID helps uniquely identify the message in email systems, facilitating tracking and preventing
// and preventing duplication. If the hostname cannot be retrieved, it defaults to "localhost.localdomain". // duplication. If the hostname cannot be retrieved, it defaults to "localhost.localdomain".
// //
// The generated Message-ID follows the format // The generated Message-ID follows the format
// "<processID.randomNumberPrimary.randomNumberSecondary.randomString@hostname>". // "<randomString@hostname>".
// //
// References: // References:
// - https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4 // - https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4
@ -986,13 +986,9 @@ func (m *Msg) SetMessageID() {
if err != nil { if err != nil {
hostname = "localhost.localdomain" hostname = "localhost.localdomain"
} }
randNumPrimary := randNum(100000000) // We have 64 possible characters, which for a 22 character string, provides approx. 132 bits of entropy.
randNumSecondary := randNum(10000) randString, _ := randomStringSecure(22)
randString, _ := randomStringSecure(17) m.SetMessageIDWithValue(fmt.Sprintf("%s@%s", randString, hostname))
procID := os.Getpid() * randNumSecondary
messageID := fmt.Sprintf("%d.%d%d.%s@%s", procID, randNumPrimary, randNumSecondary,
randString, hostname)
m.SetMessageIDWithValue(messageID)
} }
// GetMessageID retrieves the "Message-ID" header from the Msg. // GetMessageID retrieves the "Message-ID" header from the Msg.

View file

@ -786,8 +786,8 @@ func TestMsg_SetMessageIDWithValue(t *testing.T) {
// TestMsg_SetMessageIDRandomness tests the randomness of Msg.SetMessageID methods // TestMsg_SetMessageIDRandomness tests the randomness of Msg.SetMessageID methods
func TestMsg_SetMessageIDRandomness(t *testing.T) { func TestMsg_SetMessageIDRandomness(t *testing.T) {
var mids []string var mids []string
for i := 0; i < 50_000; i++ {
m := NewMsg() m := NewMsg()
for i := 0; i < 50_000; i++ {
m.SetMessageID() m.SetMessageID()
mid := m.GetMessageID() mid := m.GetMessageID()
mids = append(mids, mid) mids = append(mids, mid)

View file

@ -11,16 +11,17 @@ import (
) )
// Range of characters for the secure string generation // Range of characters for the secure string generation
const cr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" const cr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"
// Bitmask sizes for the string generators (based on 93 chars total) // Bitmask sizes for the string generators (based on 93 chars total)
// //
// These constants define bitmask-related values used for efficient random string generation. // These constants define bitmask-related values used for efficient random string generation.
// The bitmask operates over 93 possible characters, and the constants help determine the // The bitmask operates over 66 possible characters, and the constants help determine the
// number of bits and indices used in the process. // number of bits and indices used in the process.
const ( const (
// letterIdxBits: Number of bits (7) needed to represent a letter index. // letterIdxBits: Number of bits needed to represent a letter index. We have 64 possible characters
letterIdxBits = 7 // which fit into 6 bits.
letterIdxBits = 6
// letterIdxMask: Bitmask to extract letter indices (all 1-bits for letterIdxBits). // letterIdxMask: Bitmask to extract letter indices (all 1-bits for letterIdxBits).
letterIdxMask = 1<<letterIdxBits - 1 letterIdxMask = 1<<letterIdxBits - 1
// letterIdxMax: The maximum number of letter indices that fit in 63 bits. // letterIdxMax: The maximum number of letter indices that fit in 63 bits.

View file

@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
//
// SPDX-License-Identifier: MIT
//go:build !go1.20
// +build !go1.20
package mail
import (
"math/rand"
"time"
)
// randNum returns a random number with a maximum value of maxval.
//
// This function generates a random integer between 0 and maxval (exclusive). It seeds the
// random number generator with the current time in nanoseconds to ensure different results
// each time the function is called.
//
// Parameters:
// - maxval: The upper bound for the random number generation (exclusive).
//
// Returns:
// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0.
func randNum(maxval int) int {
if maxval <= 0 {
return 0
}
rand.Seed(time.Now().UnixNano())
return rand.Intn(maxval)
}

View file

@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
//
// SPDX-License-Identifier: MIT
//go:build go1.20 && !go1.22
// +build go1.20,!go1.22
package mail
import (
"math/rand"
)
// randNum returns a random number with a maximum value of maxval.
//
// This function generates a random integer between 0 and maxval (exclusive). If maxval is less
// than or equal to 0, it returns 0. The random number generator uses the default seed provided
// by the rand package.
//
// Parameters:
// - maxval: The upper bound for the random number generation (exclusive).
//
// Returns:
// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0.
func randNum(maxval int) int {
if maxval <= 0 {
return 0
}
return rand.Intn(maxval)
}

View file

@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
//
// SPDX-License-Identifier: MIT
//go:build go1.22
// +build go1.22
package mail
import (
"math/rand/v2"
)
// randNum returns a random number with a maximum value of maxval.
//
// This function generates a random integer between 0 and maxval (exclusive). It utilizes
// the math/rand/v2 interface for Go 1.22+ and will default to math/rand for older Go versions.
// If maxval is less than or equal to 0, it returns 0.
//
// Parameters:
// - maxval: The upper bound for the random number generation (exclusive).
//
// Returns:
// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0.
func randNum(maxval int) int {
if maxval <= 0 {
return 0
}
return rand.IntN(maxval)
}

View file

@ -38,34 +38,12 @@ func TestRandomStringSecure(t *testing.T) {
} }
} }
// TestRandomNum tests the randomNum method func BenchmarkGenerator_RandomStringSecure(b *testing.B) {
func TestRandomNum(t *testing.T) { b.ReportAllocs()
tt := []struct { for i := 0; i < b.N; i++ {
testName string _, err := randomStringSecure(22)
max int if err != nil {
}{ b.Errorf("RandomStringFromCharRange() failed: %s", err)
{"Max: 1", 1},
{"Max: 20", 20},
{"Max: 50", 50},
{"Max: 100", 100},
{"Max: 1000", 1000},
{"Max: 10000", 10000},
{"Max: 100000000", 100000000},
}
for _, tc := range tt {
t.Run(tc.testName, func(t *testing.T) {
rn := randNum(tc.max)
if rn > tc.max {
t.Errorf("random number generation failed: %d is bigger than given value %d", rn, tc.max)
}
})
} }
} }
func TestRandomNumZero(t *testing.T) {
rn := randNum(0)
if rn != 0 {
t.Errorf("random number generation failed: %d is not zero", rn)
}
} }