net/mail: parse ipv6 in addresses according to RFC 5321. The new domain-literal support missed that IPv6 addresses need to be prefixed with "IPv6:". The IPv6-address-literal Specification, defined in RFC 5321 Section 4.1.3, outlines the format for IPv6 address literals: https://datatracker.ietf.org/doc/html/rfc5321#section-4.1.3 Good news, the IANA has registered no other Address Literal Tags. Updates #60206 Change-Id: I7796b0f78e386a6509e793a9db98404934797722 GitHub-Last-Rev: b65fdec3e96da76f16b42eb761d6aafd3b476377 GitHub-Pull-Request: golang/go#69461 Reviewed-on: https://go-review.googlesource.com/c/go/+/613335 Reviewed-by: Sean Liao <sean@liao.dev> Reviewed-by: Junyang Shao <shaojunyang@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/src/net/mail/message.go b/src/net/mail/message.go index 7c18c28..81acf01 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go
@@ -764,7 +764,12 @@ } // Check if the domain literal is an IP address - if net.ParseIP(dtext) == nil { + if addr, ok := strings.CutPrefix(dtext, "IPv6:"); ok { + if len(net.ParseIP(addr)) != net.IPv6len { + return "", fmt.Errorf("mail: invalid IPv6 address in domain-literal: %q", dtext) + } + + } else if net.ParseIP(dtext).To4() == nil { return "", fmt.Errorf("mail: invalid IP address in domain-literal: %q", dtext) }
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go index 80a4b2d..2c1198a 100644 --- a/src/net/mail/message_test.go +++ b/src/net/mail/message_test.go
@@ -396,6 +396,7 @@ 22: {"<jdoe@[[192.168.0.1]>", "bad character in domain-literal"}, 23: {"<jdoe@[192.168.0.1>", "unclosed domain-literal"}, 24: {"<jdoe@[256.0.0.1]>", "invalid IP address in domain-literal"}, + 25: {"<jdoe@[fd42::de:ad:be:ef]>", "invalid IP address in domain-literal"}, } for i, tc := range mustErrTestCases { @@ -826,6 +827,20 @@ Address: "jdoe@[192.168.0.1]", }}, }, + // IPv6 Domain-literal + { + `jdoe@[IPv6:fd42::dead:beef:1234]`, + []*Address{{ + Address: "jdoe@[IPv6:fd42::dead:beef:1234]", + }}, + }, + { + `John Doe <jdoe@[IPv6:fd42::dead:beef:1234]>`, + []*Address{{ + Name: "John Doe", + Address: "jdoe@[IPv6:fd42::dead:beef:1234]", + }}, + }, } for _, test := range tests { if len(test.exp) == 1 { @@ -990,6 +1005,20 @@ Address: "jdoe@[192.168.0.1]", }}, }, + // IPv6 Domain-literal + { + `jdoe@[IPv6:fd42::dead:beef:1234]`, + []*Address{{ + Address: "jdoe@[IPv6:fd42::dead:beef:1234]", + }}, + }, + { + `John Doe <jdoe@[IPv6:fd42::dead:beef:1234]>`, + []*Address{{ + Name: "John Doe", + Address: "jdoe@[IPv6:fd42::dead:beef:1234]", + }}, + }, } ap := AddressParser{WordDecoder: &mime.WordDecoder{ @@ -1105,6 +1134,15 @@ &Address{Name: "Bob", Address: "bob@[192.168.0.1]"}, `"Bob" <bob@[192.168.0.1]>`, }, + // IPv6 Domain-literal + { + &Address{Address: "bob@[IPv6:fd42::dead:beef:1234]"}, + "<bob@[IPv6:fd42::dead:beef:1234]>", + }, + { + &Address{Name: "Bob", Address: "bob@[IPv6:fd42::dead:beef:1234]"}, + `"Bob" <bob@[IPv6:fd42::dead:beef:1234]>`, + }, } for _, test := range tests { s := test.addr.String()