Compare commits

..

4 commits

Author SHA1 Message Date
8faac3d101
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
2024-10-07 15:12:46 +02:00
5874911c91
Simplify Message-ID generation
Updated the SetMessageID method to generate a "Message-ID" using a
single randomly generated string combined with the hostname, replacing
the prior complex format that included process ID and multiple random
numbers. This change simplifies the code of the generated IDs.
2024-10-07 15:04:04 +02:00
5c8b2fc371
Update character set and bit size for secure string generation
Revised the character set to include a larger variety of symbols and adjusted the bit size calculations to correspond with the new set size. This ensures more efficient and secure random string generation by effectively utilizing the bitmask.
2024-10-07 15:03:07 +02:00
cdb9463ec8
Delete redundant random number generation methods
Removed `randNum` functions and associated tests from `random_119.go`, `random_121.go`, and `random_122.go`. These functions are no longer necessary. Additionally, replaced `TestRandomNum` and `TestRandomNumZero` with benchmarking for `randomStringSecure`.
2024-10-07 15:02:49 +02:00
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)
}
} }