diff --git a/README.md b/README.md index e621d74..6d1e0a0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ packages in a TinyGo application. ## "net" Package -The "net" package is ported from Go 1.20.5. The tree listings below shows the +The "net" package is ported from Go 1.21.4. The tree listings below shows the files copied. If the file is marked with an '\*', it is copied _and_ modified to work with netdev. If the file is marked with an '+', the file is new. If there is no mark, it is a straight copy. diff --git a/dial.go b/dial.go index 940552c..df6a43c 100644 --- a/dial.go +++ b/dial.go @@ -1,9 +1,10 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: Omit DualStack support // TINYGO: Omit Fast Fallback support // TINYGO: Don't allow alternate resolver // TINYGO: Omit DialTimeout +// TINYGO: Omit Multipath TCP // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -17,9 +18,9 @@ import ( "time" ) -// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times -// See golang.org/issue/31510 const ( + // defaultTCPKeepAlive is a default constant value for TCPKeepAlive times + // See go.dev/issue/31510 defaultTCPKeepAlive = 15 * time.Second ) diff --git a/http/client.go b/http/client.go index 2863c83..8f45c89 100644 --- a/http/client.go +++ b/http/client.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/clone.go b/http/clone.go index 4980b8d..2d96896 100644 --- a/http/clone.go +++ b/http/clone.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/cookie.go b/http/cookie.go index 70e1eb3..9428cc7 100644 --- a/http/cookie.go +++ b/http/cookie.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/fs.go b/http/fs.go index ed8250c..d5adcde 100644 --- a/http/fs.go +++ b/http/fs.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/header.go b/http/header.go index ac3d0bb..34a984b 100644 --- a/http/header.go +++ b/http/header.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: Removed trace stuff diff --git a/http/http.go b/http/http.go index 900d5a9..cc190c2 100644 --- a/http/http.go +++ b/http/http.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -88,14 +88,20 @@ func hexEscapeNonASCII(s string) string { return s } b := make([]byte, 0, newLen) + var pos int for i := 0; i < len(s); i++ { if s[i] >= utf8.RuneSelf { + if pos < i { + b = append(b, s[pos:i]...) + } b = append(b, '%') b = strconv.AppendInt(b, int64(s[i]), 16) - } else { - b = append(b, s[i]) + pos = i + 1 } } + if pos < len(s) { + b = append(b, s[pos:]...) + } return string(b) } diff --git a/http/internal/ascii/print.go b/http/internal/ascii/print.go index b71ed91..cfd2748 100644 --- a/http/internal/ascii/print.go +++ b/http/internal/ascii/print.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/internal/ascii/print_test.go b/http/internal/ascii/print_test.go index fb84a2e..882b8e2 100644 --- a/http/internal/ascii/print_test.go +++ b/http/internal/ascii/print_test.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/internal/chunked.go b/http/internal/chunked.go index f363820..8087a52 100644 --- a/http/internal/chunked.go +++ b/http/internal/chunked.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/internal/chunked_test.go b/http/internal/chunked_test.go index 002e74d..6844c0c 100644 --- a/http/internal/chunked_test.go +++ b/http/internal/chunked_test.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/jar.go b/http/jar.go index 6be849e..ba3adf1 100644 --- a/http/jar.go +++ b/http/jar.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/method.go b/http/method.go index 5b09e98..da3cbc0 100644 --- a/http/method.go +++ b/http/method.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/request.go b/http/request.go index 897918a..146f783 100644 --- a/http/request.go +++ b/http/request.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: Removed multipart stuff // TINYGO: Removed trace stuff @@ -22,7 +22,6 @@ import ( "io" "mime" "mime/multipart" - "net" "net/http/internal/ascii" "net/textproto" "net/url" @@ -30,6 +29,8 @@ import ( "strconv" "strings" "sync" + + "golang.org/x/net/http/httpguts" ) const ( @@ -50,6 +51,11 @@ type ProtocolError struct { func (pe *ProtocolError) Error() string { return pe.ErrorString } +// Is lets http.ErrNotSupported match errors.ErrUnsupported. +func (pe *ProtocolError) Is(err error) bool { + return pe == ErrNotSupported && err == errors.ErrUnsupported +} + var ( // ErrNotSupported indicates that a feature is not supported. // @@ -573,12 +579,40 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF // is not given, use the host from the request URL. // // Clean the host, in case it arrives with unexpected stuff in it. - host := cleanHost(r.Host) + host := r.Host if host == "" { if r.URL == nil { return errMissingHost } - host = cleanHost(r.URL.Host) + host = r.URL.Host + } + host, err = httpguts.PunycodeHostPort(host) + if err != nil { + return err + } + // Validate that the Host header is a valid header in general, + // but don't validate the host itself. This is sufficient to avoid + // header or request smuggling via the Host field. + // The server can (and will, if it's a net/http server) reject + // the request if it doesn't consider the host valid. + if !httpguts.ValidHostHeader(host) { + // Historically, we would truncate the Host header after '/' or ' '. + // Some users have relied on this truncation to convert a network + // address such as Unix domain socket path into a valid, ignored + // Host header (see https://go.dev/issue/61431). + // + // We don't preserve the truncation, because sending an altered + // header field opens a smuggling vector. Instead, zero out the + // Host header entirely if it isn't valid. (An empty Host is valid; + // see RFC 9112 Section 3.2.) + // + // Return an error if we're sending to a proxy, since the proxy + // probably can't do anything useful with an empty Host header. + if !usingProxy { + host = "" + } else { + return errors.New("http: invalid Host header") + } } // According to RFC 6874, an HTTP client, proxy, or other @@ -706,35 +740,6 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF // This error type should not escape the net/http package to users. type requestBodyReadError struct{ error } -// cleanHost cleans up the host sent in request's Host header. -// -// It both strips anything after '/' or ' ', and puts the value -// into Punycode form, if necessary. -// -// Ideally we'd clean the Host header according to the spec: -// -// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]") -// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host) -// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host) -// -// But practically, what we are trying to avoid is the situation in -// issue 11206, where a malformed Host header used in the proxy context -// would create a bad request. So it is enough to just truncate at the -// first offending character. - -// TINYGO: Removed IDNA checks...it doubled the binary size - -func cleanHost(in string) string { - if i := strings.IndexAny(in, " /"); i != -1 { - in = in[:i] - } - host, port, err := net.SplitHostPort(in) - if err != nil { // input was just a host - return in - } - return net.JoinHostPort(host, port) -} - // removeZone removes IPv6 zone identifier from host. // E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" func removeZone(host string) string { diff --git a/http/response.go b/http/response.go index 30ffe7e..656c9ec 100644 --- a/http/response.go +++ b/http/response.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: Removed TLS connection state // TINYGO: Added onEOF hook to get callback when response has been read diff --git a/http/server.go b/http/server.go index 3992765..d108ae2 100644 --- a/http/server.go +++ b/http/server.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: atomic.Pointer and atomic.Uint64 are added in Go 1.19, so keep // pre-1.19 code to cover min TinyGo. If TinyGo min moves to 1.19 or higher, @@ -469,6 +469,10 @@ type response struct { // Content-Length. closeAfterReply bool + // When fullDuplex is false (the default), we consume any remaining + // request body before starting to write a response. + fullDuplex bool + // requestBodyLimitHit is set by requestTooLarge when // maxBytesReader hits its max size. It is checked in // WriteHeader, to make sure we don't consume the @@ -506,6 +510,11 @@ func (c *response) SetWriteDeadline(deadline time.Time) error { return c.conn.rwc.SetWriteDeadline(deadline) } +func (c *response) EnableFullDuplex() error { + c.fullDuplex = true + return nil +} + // TrailerPrefix is a magic prefix for ResponseWriter.Header map keys // that, if present, signals that the map entry is actually for // the response trailers, and not the response headers. The prefix @@ -1162,8 +1171,11 @@ func (w *response) WriteHeader(code int) { } checkWriteHeaderCode(code) - // Handle informational headers - if code >= 100 && code <= 199 { + // Handle informational headers. + // + // We shouldn't send any further headers after 101 Switching Protocols, + // so it takes the non-informational path. + if code >= 100 && code <= 199 && code != StatusSwitchingProtocols { // Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read() if code == 100 && w.canWriteContinue.isSet() { w.writeContinueMu.Lock() @@ -1325,7 +1337,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // send a Content-Length header. // Further, we don't send an automatic Content-Length if they // set a Transfer-Encoding, because they're generally incompatible. - if w.handlerDone.isSet() && !trailers && !hasTE && bodyAllowedForStatus(w.status) && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) { + if w.handlerDone.isSet() && !trailers && !hasTE && bodyAllowedForStatus(w.status) && !header.has("Content-Length") && (!isHEAD || len(p) > 0) { w.contentLength = int64(len(p)) setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10) } @@ -1371,14 +1383,14 @@ func (cw *chunkWriter) writeHeader(p []byte) { w.closeAfterReply = true } - // Per RFC 2616, we should consume the request body before - // replying, if the handler hasn't already done so. But we - // don't want to do an unbounded amount of reading here for - // DoS reasons, so we only try up to a threshold. - // TODO(bradfitz): where does RFC 2616 say that? See Issue 15527 - // about HTTP/1.x Handlers concurrently reading and writing, like - // HTTP/2 handlers can do. Maybe this code should be relaxed? - if w.req.ContentLength != 0 && !w.closeAfterReply { + // We do this by default because there are a number of clients that + // send a full request before starting to read the response, and they + // can deadlock if we start writing the response with unconsumed body + // remaining. See Issue 15527 for some history. + // + // If full duplex mode has been enabled with ResponseController.EnableFullDuplex, + // then leave the request body alone. + if w.req.ContentLength != 0 && !w.closeAfterReply && !w.fullDuplex { var discard, tooBig bool switch bdy := w.req.Body.(type) { @@ -1766,7 +1778,7 @@ type closeWriter interface { var _ closeWriter = (*net.TCPConn)(nil) -// closeWrite flushes any outstanding data and sends a FIN packet (if +// closeWriteAndWait flushes any outstanding data and sends a FIN packet (if // client is connected via TCP), signaling that we're done. We then // pause for a bit, hoping the client processes it before any // subsequent RST. @@ -1861,7 +1873,9 @@ func isCommonNetReadError(err error) bool { // Serve a new connection. func (c *conn) serve(ctx context.Context) { - c.remoteAddr = c.rwc.RemoteAddr().String() + if ra := c.rwc.RemoteAddr(); ra != nil { + c.remoteAddr = ra.String() + } ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) var inFlightResponse *response defer func() { @@ -2258,7 +2272,7 @@ func RedirectHandler(url string, code int) Handler { // Longer patterns take precedence over shorter ones, so that // if there are handlers registered for both "/images/" // and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the +// called for paths beginning with "/images/thumbnails/" and the // former will receive requests for any other paths in the // "/images/" subtree. // @@ -2873,23 +2887,9 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler = globalOptionsHandler{} } - if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { - var allowQuerySemicolonsInUse int32 - req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { - atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) - })) - defer func() { - if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { - sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") - } - }() - } - handler.ServeHTTP(rw, req) } -var silenceSemWarnContextKey = &contextKey{"silence-semicolons"} - // AllowQuerySemicolons returns a handler that serves requests by converting any // unescaped semicolons in the URL query to ampersands, and invoking the handler h. // @@ -2901,9 +2901,6 @@ var silenceSemWarnContextKey = &contextKey{"silence-semicolons"} // AllowQuerySemicolons should be invoked before Request.ParseForm is called. func AllowQuerySemicolons(h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { - if silenceSemicolonsWarning, ok := r.Context().Value(silenceSemWarnContextKey).(func()); ok { - silenceSemicolonsWarning() - } if strings.Contains(r.URL.RawQuery, ";") { r2 := new(Request) *r2 = *r diff --git a/http/sniff.go b/http/sniff.go index 66c55b3..127791f 100644 --- a/http/sniff.go +++ b/http/sniff.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/status.go b/http/status.go index ec4f1ee..b568bfb 100644 --- a/http/status.go +++ b/http/status.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/http/transfer.go b/http/transfer.go index e528968..e282835 100644 --- a/http/transfer.go +++ b/http/transfer.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // TINYGO: Removed trace stuff // TINYGO: Hook readTransfer is onEOF callback to get notified when request of @@ -409,7 +409,7 @@ func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err return } -// unwrapBodyReader unwraps the body's inner reader if it's a +// unwrapBody unwraps the body's inner reader if it's a // nopCloser. This is to ensure that body writes sourced from local // files (*os.File types) are properly optimized. // diff --git a/http/transport.go b/http/transport.go index ae29865..f117558 100644 --- a/http/transport.go +++ b/http/transport.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/ip.go b/ip.go index 16477a6..0b6542c 100644 --- a/ip.go +++ b/ip.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -17,6 +17,7 @@ package net import ( "internal/bytealg" "internal/itoa" + "net/netip" ) // IP address lengths (bytes). @@ -287,25 +288,6 @@ func (ip IP) Mask(mask IPMask) IP { return out } -// ubtoa encodes the string form of the integer v to dst[start:] and -// returns the number of bytes written to dst. The caller must ensure -// that dst has sufficient length. -func ubtoa(dst []byte, start int, v byte) int { - if v < 10 { - dst[start] = v + '0' - return 1 - } else if v < 100 { - dst[start+1] = v%10 + '0' - dst[start] = v/10 + '0' - return 2 - } - - dst[start+2] = v%10 + '0' - dst[start+1] = (v/10)%10 + '0' - dst[start] = v/100 + '0' - return 3 -} - // String returns the string form of the IP address ip. // It returns one of 4 forms: // - "", if ip has length 0 @@ -313,73 +295,18 @@ func ubtoa(dst []byte, start int, v byte) int { // - IPv6 conforming to RFC 5952 ("2001:db8::1"), if ip is a valid IPv6 address // - the hexadecimal form of ip, without punctuation, if no other cases apply func (ip IP) String() string { - p := ip - if len(ip) == 0 { return "" } - // If IPv4, use dotted notation. - if p4 := p.To4(); len(p4) == IPv4len { - const maxIPv4StringLen = len("255.255.255.255") - b := make([]byte, maxIPv4StringLen) - - n := ubtoa(b, 0, p4[0]) - b[n] = '.' - n++ - - n += ubtoa(b, n, p4[1]) - b[n] = '.' - n++ - - n += ubtoa(b, n, p4[2]) - b[n] = '.' - n++ - - n += ubtoa(b, n, p4[3]) - return string(b[:n]) - } - if len(p) != IPv6len { + if len(ip) != IPv4len && len(ip) != IPv6len { return "?" + hexString(ip) } - - // Find longest run of zeros. - e0 := -1 - e1 := -1 - for i := 0; i < IPv6len; i += 2 { - j := i - for j < IPv6len && p[j] == 0 && p[j+1] == 0 { - j += 2 - } - if j > i && j-i > e1-e0 { - e0 = i - e1 = j - i = j - } - } - // The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field. - if e1-e0 <= 2 { - e0 = -1 - e1 = -1 - } - - const maxLen = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") - b := make([]byte, 0, maxLen) - - // Print with possible :: in place of run of zeros - for i := 0; i < IPv6len; i += 2 { - if i == e0 { - b = append(b, ':', ':') - i = e1 - if i >= IPv6len { - break - } - } else if i > 0 { - b = append(b, ':') - } - b = appendHex(b, (uint32(p[i])<<8)|uint32(p[i+1])) + // If IPv4, use dotted notation. + if p4 := ip.To4(); len(p4) == IPv4len { + return netip.AddrFrom4([4]byte(p4)).String() } - return string(b) + return netip.AddrFrom16([16]byte(ip)).String() } func hexString(b []byte) string { @@ -561,175 +488,24 @@ func (n *IPNet) String() string { return nn.String() + "/" + itoa.Uitoa(uint(l)) } -// Parse IPv4 address (d.d.d.d). -func parseIPv4(s string) IP { - var p [IPv4len]byte - for i := 0; i < IPv4len; i++ { - if len(s) == 0 { - // Missing octets. - return nil - } - if i > 0 { - if s[0] != '.' { - return nil - } - s = s[1:] - } - n, c, ok := dtoi(s) - if !ok || n > 0xFF { - return nil - } - if c > 1 && s[0] == '0' { - // Reject non-zero components with leading zeroes. - return nil - } - s = s[c:] - p[i] = byte(n) - } - if len(s) != 0 { - return nil - } - return IPv4(p[0], p[1], p[2], p[3]) -} - -// parseIPv6Zone parses s as a literal IPv6 address and its associated zone -// identifier which is described in RFC 4007. -func parseIPv6Zone(s string) (IP, string) { - s, zone := splitHostZone(s) - return parseIPv6(s), zone -} - -// parseIPv6 parses s as a literal IPv6 address described in RFC 4291 -// and RFC 5952. -func parseIPv6(s string) (ip IP) { - ip = make(IP, IPv6len) - ellipsis := -1 // position of ellipsis in ip - - // Might have leading ellipsis - if len(s) >= 2 && s[0] == ':' && s[1] == ':' { - ellipsis = 0 - s = s[2:] - // Might be only ellipsis - if len(s) == 0 { - return ip - } - } - - // Loop, parsing hex numbers followed by colon. - i := 0 - for i < IPv6len { - // Hex number. - n, c, ok := xtoi(s) - if !ok || n > 0xFFFF { - return nil - } - - // If followed by dot, might be in trailing IPv4. - if c < len(s) && s[c] == '.' { - if ellipsis < 0 && i != IPv6len-IPv4len { - // Not the right place. - return nil - } - if i+IPv4len > IPv6len { - // Not enough room. - return nil - } - ip4 := parseIPv4(s) - if ip4 == nil { - return nil - } - ip[i] = ip4[12] - ip[i+1] = ip4[13] - ip[i+2] = ip4[14] - ip[i+3] = ip4[15] - s = "" - i += IPv4len - break - } - - // Save this 16-bit chunk. - ip[i] = byte(n >> 8) - ip[i+1] = byte(n) - i += 2 - - // Stop at end of string. - s = s[c:] - if len(s) == 0 { - break - } - - // Otherwise must be followed by colon and more. - if s[0] != ':' || len(s) == 1 { - return nil - } - s = s[1:] - - // Look for ellipsis. - if s[0] == ':' { - if ellipsis >= 0 { // already have one - return nil - } - ellipsis = i - s = s[1:] - if len(s) == 0 { // can be at end - break - } - } - } - - // Must have used entire string. - if len(s) != 0 { - return nil - } - - // If didn't parse enough, expand ellipsis. - if i < IPv6len { - if ellipsis < 0 { - return nil - } - n := IPv6len - i - for j := i - 1; j >= ellipsis; j-- { - ip[j+n] = ip[j] - } - for j := ellipsis + n - 1; j >= ellipsis; j-- { - ip[j] = 0 - } - } else if ellipsis >= 0 { - // Ellipsis must represent at least one 0 group. - return nil - } - return ip -} - // ParseIP parses s as an IP address, returning the result. // The string s can be in IPv4 dotted decimal ("192.0.2.1"), IPv6 // ("2001:db8::68"), or IPv4-mapped IPv6 ("::ffff:192.0.2.1") form. // If s is not a valid textual representation of an IP address, // ParseIP returns nil. func ParseIP(s string) IP { - for i := 0; i < len(s); i++ { - switch s[i] { - case '.': - return parseIPv4(s) - case ':': - return parseIPv6(s) - } + if addr, valid := parseIP(s); valid { + return IP(addr[:]) } return nil } -// parseIPZone parses s as an IP address, return it and its associated zone -// identifier (IPv6 only). -func parseIPZone(s string) (IP, string) { - for i := 0; i < len(s); i++ { - switch s[i] { - case '.': - return parseIPv4(s), "" - case ':': - return parseIPv6Zone(s) - } +func parseIP(s string) ([16]byte, bool) { + ip, err := netip.ParseAddr(s) + if err != nil || ip.Zone() != "" { + return [16]byte{}, false } - return nil, "" + return ip.As16(), true } // ParseCIDR parses s as a CIDR notation IP address and prefix length, @@ -746,16 +522,23 @@ func ParseCIDR(s string) (IP, *IPNet, error) { return nil, nil, &ParseError{Type: "CIDR address", Text: s} } addr, mask := s[:i], s[i+1:] - iplen := IPv4len - ip := parseIPv4(addr) - if ip == nil { - iplen = IPv6len - ip = parseIPv6(addr) + + ipAddr, err := netip.ParseAddr(addr) + if err != nil || ipAddr.Zone() != "" { + return nil, nil, &ParseError{Type: "CIDR address", Text: s} } + n, i, ok := dtoi(mask) - if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { + if !ok || i != len(mask) || n < 0 || n > ipAddr.BitLen() { return nil, nil, &ParseError{Type: "CIDR address", Text: s} } - m := CIDRMask(n, 8*iplen) - return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil + m := CIDRMask(n, ipAddr.BitLen()) + addr16 := ipAddr.As16() + return IP(addr16[:]), &IPNet{IP: IP(addr16[:]).Mask(m), Mask: m}, nil +} + +func copyIP(x IP) IP { + y := make(IP, len(x)) + copy(y, x) + return y } diff --git a/iprawsock.go b/iprawsock.go index f39850e..dcac6b6 100644 --- a/iprawsock.go +++ b/iprawsock.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/ipsock.go b/ipsock.go index 3ab9ebf..880878b 100644 --- a/ipsock.go +++ b/ipsock.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/mac.go b/mac.go index 2c855cb..1721982 100644 --- a/mac.go +++ b/mac.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/mac_test.go b/mac_test.go index 8bd8ba8..d08dac1 100644 --- a/mac_test.go +++ b/mac_test.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/net.go b/net.go index e66e19b..da27db6 100644 --- a/net.go +++ b/net.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/parse.go b/parse.go index 1a5e6c1..80d9d12 100644 --- a/parse.go +++ b/parse.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied from Go 1.20.5 official implementation. +// TINYGO: The following is copied from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/pipe.go b/pipe.go index b61a4dc..720511b 100644 --- a/pipe.go +++ b/pipe.go @@ -1,4 +1,4 @@ -// The following is copied from Go 1.20.5 official implementation. +// The following is copied from Go 1.21.4 official implementation. // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/tcpsock.go b/tcpsock.go index 90605b7..4a01133 100644 --- a/tcpsock.go +++ b/tcpsock.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/tlssock.go b/tlssock.go index 639e998..2dae8f0 100644 --- a/tlssock.go +++ b/tlssock.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/udpsock.go b/udpsock.go index 086668b..1bcda1b 100644 --- a/udpsock.go +++ b/udpsock.go @@ -1,4 +1,4 @@ -// TINYGO: The following is copied and modified from Go 1.20.5 official implementation. +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style