From 3c333c0c5288a7cf127e427ddda5b1b54020a2b4 Mon Sep 17 00:00:00 2001 From: Carlos Hernandez Date: Fri, 6 Sep 2024 00:19:28 +0000 Subject: [PATCH 01/31] route: fix address parsing of messages on Darwin sizeofSockaddrInet is 16, but first byte of sockaddr specifies actual size of sockaddr. Although, 16 works for most cases, it fails for Netmasks addresses. On Darwin only the significant bits of the netmask are in the msg. Take this route message as an example ``` // rt_msg_hdr 88 00 05 01 00 00 00 00 41 08 00 00 07 00 00 00 92 7b 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // metrics 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // SOCKADDRS - DST (100.113.0.0) 10 02 00 00 64 71 00 00 00 00 00 00 00 00 00 00 // GW utun4319 14 12 21 00 01 08 00 00 75 74 75 6e 34 33 31 39 00 00 00 00 // NETMASK 255.255.0.0 06 02 00 00 ff ff // NULL 00 00 ``` i.e. ipv4 ``` 06 02 00 00 ff ff ``` The above byte sequence is for a sockaddr that is 6 bytes long representing an ipv4 for address that is 255.255.0.0. i.e. ipv6 netmask ``` 0e 1e 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 ``` The above is `/48` netmask that should also be parsed using `b[0]` of the sockaddr that contains the length. Confirmed by using `route monitor`. sources: https://github.com/apple/darwin-xnu/blob/main/bsd/net/route.h https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h#L603 Fixes golang/go#44740 Change-Id: I8153130d02d0a5e547fbf60a85762d3889e1d08c GitHub-Last-Rev: f7b9253061ab7daea8c04833f30bb60699c205f1 GitHub-Pull-Request: golang/net#220 Reviewed-on: https://go-review.googlesource.com/c/net/+/609577 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Auto-Submit: Ian Lance Taylor --- route/address.go | 27 ++++++++++++++---- route/address_darwin_test.go | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/route/address.go b/route/address.go index 5443d67223..bae63003f0 100644 --- a/route/address.go +++ b/route/address.go @@ -170,20 +170,37 @@ func (a *Inet6Addr) marshal(b []byte) (int, error) { // parseInetAddr parses b as an internet address for IPv4 or IPv6. func parseInetAddr(af int, b []byte) (Addr, error) { + const ( + off4 = 4 // offset of in_addr + off6 = 8 // offset of in6_addr + ) switch af { case syscall.AF_INET: - if len(b) < sizeofSockaddrInet { + if len(b) < (off4+1) || len(b) < int(b[0]) { return nil, errInvalidAddr } + sockAddrLen := int(b[0]) a := &Inet4Addr{} - copy(a.IP[:], b[4:8]) + n := off4 + 4 + if sockAddrLen < n { + n = sockAddrLen + } + copy(a.IP[:], b[off4:n]) return a, nil case syscall.AF_INET6: - if len(b) < sizeofSockaddrInet6 { + if len(b) < (off6+1) || len(b) < int(b[0]) { return nil, errInvalidAddr } - a := &Inet6Addr{ZoneID: int(nativeEndian.Uint32(b[24:28]))} - copy(a.IP[:], b[8:24]) + sockAddrLen := int(b[0]) + n := off6 + 16 + if sockAddrLen < n { + n = sockAddrLen + } + a := &Inet6Addr{} + if sockAddrLen == sizeofSockaddrInet6 { + a.ZoneID = int(nativeEndian.Uint32(b[24:28])) + } + copy(a.IP[:], b[off6:n]) if a.IP[0] == 0xfe && a.IP[1]&0xc0 == 0x80 || a.IP[0] == 0xff && (a.IP[1]&0x0f == 0x01 || a.IP[1]&0x0f == 0x02) { // KAME based IPv6 protocol stack usually // embeds the interface index in the diff --git a/route/address_darwin_test.go b/route/address_darwin_test.go index b819183a78..0b5c72d1dc 100644 --- a/route/address_darwin_test.go +++ b/route/address_darwin_test.go @@ -42,6 +42,60 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ nil, }, }, + { + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + parseKernelInetAddr, + []byte{ + 0x10, 0x02, 0x00, 0x00, 0x64, 0x71, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x14, 0x12, 0x21, 0x00, 0x01, 0x08, 0x00, 0x00, + 0x75, 0x74, 0x75, 0x6e, 0x34, 0x33, 0x31, 0x39, + 0x00, 0x00, 0x00, 0x00, + + 0x06, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + }, + []Addr{ + &Inet4Addr{IP: [4]byte{100, 113, 0, 0}}, + &LinkAddr{Index: 33, Name: "utun4319"}, + &Inet4Addr{IP: [4]byte{255, 255, 0, 0}}, + nil, + nil, + nil, + nil, + nil, + }, + }, + // route -n add -inet6 fd84:1b4e:6281:: -prefixlen 48 fe80::f22f:4bff:fe09:3bff%utun4319 + // gw fe80:0000:0000:0000:f22f:4bff:fe09:3bff + { + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + parseKernelInetAddr, + []byte{ + 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfd, 0x84, 0x1b, 0x4e, 0x62, 0x81, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x80, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff, + 0x00, 0x00, 0x00, 0x00, + + 0x0e, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + }, + []Addr{ + &Inet6Addr{IP: [16]byte{ 0xfd, 0x84, 0x1b, 0x4e, 0x62, 0x81 }}, + &Inet6Addr{IP: [16]byte{ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff }, ZoneID: 33}, + &Inet6Addr{IP: [16]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,}}, + nil, + nil, + nil, + nil, + nil, + }, + }, } func TestParseAddrsOnDarwin(t *testing.T) { From 541dbe58b6bc869fc1c7de361846682a34365325 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Thu, 9 May 2024 10:24:28 -0700 Subject: [PATCH 02/31] http2: add Server.WriteByteTimeout Transports support a WriteByteTimeout option which sets the maximum amount of time we can go without being able to write any bytes to a connection. Add an equivalent option to Server for consistency. Fixes golang/go#61777 Change-Id: Iaa8a69dfc403906eb224829320f901e5a6a5c429 Reviewed-on: https://go-review.googlesource.com/c/net/+/601496 LUCI-TryBot-Result: Go LUCI Reviewed-by: Carlos Amedee Reviewed-by: Brad Fitzpatrick --- http2/connframes_test.go | 5 ++-- http2/http2.go | 53 ++++++++++++++++++++++++++++++++++------ http2/server.go | 12 ++++++++- http2/server_test.go | 32 ++++++++++++++++++++++++ http2/transport.go | 24 ++++++------------ 5 files changed, 98 insertions(+), 28 deletions(-) diff --git a/http2/connframes_test.go b/http2/connframes_test.go index 7db8b74e2e..1f7834c01f 100644 --- a/http2/connframes_test.go +++ b/http2/connframes_test.go @@ -6,7 +6,6 @@ package http2 import ( "bytes" - "context" "io" "net/http" "os" @@ -295,7 +294,7 @@ func (tf *testConnFramer) wantClosed() { if err == nil { tf.t.Fatalf("got unexpected frame (want closed connection): %v", fr) } - if err == context.DeadlineExceeded { + if err == os.ErrDeadlineExceeded { tf.t.Fatalf("connection is not closed; want it to be") } } @@ -306,7 +305,7 @@ func (tf *testConnFramer) wantIdle() { if err == nil { tf.t.Fatalf("got unexpected frame (want idle connection): %v", fr) } - if err != context.DeadlineExceeded { + if err != os.ErrDeadlineExceeded { tf.t.Fatalf("got unexpected frame error (want idle connection): %v", err) } } diff --git a/http2/http2.go b/http2/http2.go index 003e649f30..7688c356b7 100644 --- a/http2/http2.go +++ b/http2/http2.go @@ -19,8 +19,9 @@ import ( "bufio" "context" "crypto/tls" + "errors" "fmt" - "io" + "net" "net/http" "os" "sort" @@ -237,13 +238,19 @@ func (cw closeWaiter) Wait() { // Its buffered writer is lazily allocated as needed, to minimize // idle memory usage with many connections. type bufferedWriter struct { - _ incomparable - w io.Writer // immutable - bw *bufio.Writer // non-nil when data is buffered + _ incomparable + group synctestGroupInterface // immutable + conn net.Conn // immutable + bw *bufio.Writer // non-nil when data is buffered + byteTimeout time.Duration // immutable, WriteByteTimeout } -func newBufferedWriter(w io.Writer) *bufferedWriter { - return &bufferedWriter{w: w} +func newBufferedWriter(group synctestGroupInterface, conn net.Conn, timeout time.Duration) *bufferedWriter { + return &bufferedWriter{ + group: group, + conn: conn, + byteTimeout: timeout, + } } // bufWriterPoolBufferSize is the size of bufio.Writer's @@ -270,7 +277,7 @@ func (w *bufferedWriter) Available() int { func (w *bufferedWriter) Write(p []byte) (n int, err error) { if w.bw == nil { bw := bufWriterPool.Get().(*bufio.Writer) - bw.Reset(w.w) + bw.Reset((*bufferedWriterTimeoutWriter)(w)) w.bw = bw } return w.bw.Write(p) @@ -288,6 +295,38 @@ func (w *bufferedWriter) Flush() error { return err } +type bufferedWriterTimeoutWriter bufferedWriter + +func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { + return writeWithByteTimeout(w.group, w.conn, w.byteTimeout, p) +} + +// writeWithByteTimeout writes to conn. +// If more than timeout passes without any bytes being written to the connection, +// the write fails. +func writeWithByteTimeout(group synctestGroupInterface, conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { + if timeout <= 0 { + return conn.Write(p) + } + for { + var now time.Time + if group == nil { + now = time.Now() + } else { + now = group.Now() + } + conn.SetWriteDeadline(now.Add(timeout)) + nn, err := conn.Write(p[n:]) + n += nn + if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { + // Either we finished the write, made no progress, or hit the deadline. + // Whichever it is, we're done now. + conn.SetWriteDeadline(time.Time{}) + return n, err + } + } +} + func mustUint31(v int32) uint32 { if v < 0 || v > 2147483647 { panic("out of range") diff --git a/http2/server.go b/http2/server.go index 6c349f3ec6..b16173c578 100644 --- a/http2/server.go +++ b/http2/server.go @@ -127,6 +127,12 @@ type Server struct { // If zero or negative, there is no timeout. IdleTimeout time.Duration + // WriteByteTimeout is the timeout after which a connection will be + // closed if no data can be written to it. The timeout begins when data is + // available to write, and is extended whenever any bytes are written. + // If zero or negative, there is no timeout. + WriteByteTimeout time.Duration + // MaxUploadBufferPerConnection is the size of the initial flow // control window for each connections. The HTTP/2 spec does not // allow this to be smaller than 65535 or larger than 2^32-1. @@ -446,7 +452,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon conn: c, baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), - bw: newBufferedWriter(c), + bw: newBufferedWriter(s.group, c, s.WriteByteTimeout), handler: opts.handler(), streams: make(map[uint32]*stream), readFrameCh: make(chan readFrameResult), @@ -1320,6 +1326,10 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) { sc.writingFrame = false sc.writingFrameAsync = false + if res.err != nil { + sc.conn.Close() + } + wr := res.wr if writeEndsStream(wr.write) { diff --git a/http2/server_test.go b/http2/server_test.go index 47c3c619c0..ab53c26946 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -4674,3 +4674,35 @@ func TestServerSetReadWriteDeadlineRace(t *testing.T) { } resp.Body.Close() } + +func TestServerWriteByteTimeout(t *testing.T) { + const timeout = 1 * time.Second + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + w.Write(make([]byte, 100)) + }, func(s *Server) { + s.WriteByteTimeout = timeout + }) + st.greet() + + st.cc.(*synctestNetConn).SetReadBufferSize(1) // write one byte at a time + st.writeHeaders(HeadersFrameParam{ + StreamID: 1, + BlockFragment: st.encodeHeader(), + EndStream: true, + EndHeaders: true, + }) + + // Read a few bytes, staying just under WriteByteTimeout. + for i := 0; i < 10; i++ { + st.advance(timeout - 1) + if n, err := st.cc.Read(make([]byte, 1)); n != 1 || err != nil { + t.Fatalf("read %v: %v, %v; want 1, nil", i, n, err) + } + } + + // Wait for WriteByteTimeout. + // The connection should close. + st.advance(1 * time.Second) // timeout after writing one byte + st.advance(1 * time.Second) // timeout after failing to write any more bytes + st.wantClosed() +} diff --git a/http2/transport.go b/http2/transport.go index 61f511f97a..49fc792adc 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -25,7 +25,6 @@ import ( "net/http" "net/http/httptrace" "net/textproto" - "os" "sort" "strconv" "strings" @@ -499,6 +498,7 @@ func (cs *clientStream) closeReqBodyLocked() { } type stickyErrWriter struct { + group synctestGroupInterface conn net.Conn timeout time.Duration err *error @@ -508,22 +508,9 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } - for { - if sew.timeout != 0 { - sew.conn.SetWriteDeadline(time.Now().Add(sew.timeout)) - } - nn, err := sew.conn.Write(p[n:]) - n += nn - if n < len(p) && nn > 0 && errors.Is(err, os.ErrDeadlineExceeded) { - // Keep extending the deadline so long as we're making progress. - continue - } - if sew.timeout != 0 { - sew.conn.SetWriteDeadline(time.Time{}) - } - *sew.err = err - return n, err - } + n, err = writeWithByteTimeout(sew.group, sew.conn, sew.timeout, p) + *sew.err = err + return n, err } // noCachedConnError is the concrete type of ErrNoCachedConn, which @@ -792,10 +779,12 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), } + var group synctestGroupInterface if t.transportTestHooks != nil { t.markNewGoroutine() t.transportTestHooks.newclientconn(cc) c = cc.tconn + group = t.group } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) @@ -807,6 +796,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. cc.bw = bufio.NewWriter(stickyErrWriter{ + group: group, conn: c, timeout: t.WriteByteTimeout, err: &cc.werr, From 4790dc7047441aed4889873cdd30e1e6adf49735 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 28 May 2024 14:11:26 -0700 Subject: [PATCH 03/31] http2: add support for server-originated pings Add configurable support for health-checking idle connections accepted by the HTTP/2 server, following the same configuration as the Transport. Fixes golang/go#67812 Change-Id: Ia4014e691546b2c29db8dad3af5f39966d0ceb93 Reviewed-on: https://go-review.googlesource.com/c/net/+/601497 Reviewed-by: Carlos Amedee Reviewed-by: Brad Fitzpatrick LUCI-TryBot-Result: Go LUCI --- http2/server.go | 60 ++++++++++++++++++++++++++++++++++++++++++++ http2/server_test.go | 43 +++++++++++++++++++++++++++++++ http2/write.go | 10 ++++++++ 3 files changed, 113 insertions(+) diff --git a/http2/server.go b/http2/server.go index b16173c578..645c8da696 100644 --- a/http2/server.go +++ b/http2/server.go @@ -29,6 +29,7 @@ import ( "bufio" "bytes" "context" + "crypto/rand" "crypto/tls" "errors" "fmt" @@ -127,6 +128,16 @@ type Server struct { // If zero or negative, there is no timeout. IdleTimeout time.Duration + // ReadIdleTimeout is the timeout after which a health check using a ping + // frame will be carried out if no frame is received on the connection. + // If zero, no health check is performed. + ReadIdleTimeout time.Duration + + // PingTimeout is the timeout after which the connection will be closed + // if a response to a ping is not received. + // If zero, a default of 15 seconds is used. + PingTimeout time.Duration + // WriteByteTimeout is the timeout after which a connection will be // closed if no data can be written to it. The timeout begins when data is // available to write, and is extended whenever any bytes are written. @@ -644,9 +655,12 @@ type serverConn struct { inGoAway bool // we've started to or sent GOAWAY inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop needToSendGoAway bool // we need to schedule a GOAWAY frame write + pingSent bool + sentPingData [8]byte goAwayCode ErrCode shutdownTimer timer // nil until used idleTimer timer // nil if unused + readIdleTimer timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -974,11 +988,17 @@ func (sc *serverConn) serve() { defer sc.idleTimer.Stop() } + if sc.srv.ReadIdleTimeout > 0 { + sc.readIdleTimer = sc.srv.afterFunc(sc.srv.ReadIdleTimeout, sc.onReadIdleTimer) + defer sc.readIdleTimer.Stop() + } + go sc.readFrames() // closed by defer sc.conn.Close above settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() + lastFrameTime := sc.srv.now() loopNum := 0 for { loopNum++ @@ -992,6 +1012,7 @@ func (sc *serverConn) serve() { case res := <-sc.wroteFrameCh: sc.wroteFrame(res) case res := <-sc.readFrameCh: + lastFrameTime = sc.srv.now() // Process any written frames before reading new frames from the client since a // written frame could have triggered a new stream to be started. if sc.writingFrameAsync { @@ -1023,6 +1044,8 @@ func (sc *serverConn) serve() { case idleTimerMsg: sc.vlogf("connection is idle") sc.goAway(ErrCodeNo) + case readIdleTimerMsg: + sc.handlePingTimer(lastFrameTime) case shutdownTimerMsg: sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr()) return @@ -1061,12 +1084,43 @@ func (sc *serverConn) serve() { } } +func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) { + if sc.pingSent { + sc.vlogf("timeout waiting for PING response") + sc.conn.Close() + return + } + + pingAt := lastFrameReadTime.Add(sc.srv.ReadIdleTimeout) + now := sc.srv.now() + if pingAt.After(now) { + // We received frames since arming the ping timer. + // Reset it for the next possible timeout. + sc.readIdleTimer.Reset(pingAt.Sub(now)) + return + } + + sc.pingSent = true + // Ignore crypto/rand.Read errors: It generally can't fail, and worse case if it does + // is we send a PING frame containing 0s. + _, _ = rand.Read(sc.sentPingData[:]) + sc.writeFrame(FrameWriteRequest{ + write: &writePing{data: sc.sentPingData}, + }) + pingTimeout := sc.srv.PingTimeout + if pingTimeout <= 0 { + pingTimeout = 15 * time.Second + } + sc.readIdleTimer.Reset(pingTimeout) +} + type serverMessage int // Message values sent to serveMsgCh. var ( settingsTimerMsg = new(serverMessage) idleTimerMsg = new(serverMessage) + readIdleTimerMsg = new(serverMessage) shutdownTimerMsg = new(serverMessage) gracefulShutdownMsg = new(serverMessage) handlerDoneMsg = new(serverMessage) @@ -1074,6 +1128,7 @@ var ( func (sc *serverConn) onSettingsTimer() { sc.sendServeMsg(settingsTimerMsg) } func (sc *serverConn) onIdleTimer() { sc.sendServeMsg(idleTimerMsg) } +func (sc *serverConn) onReadIdleTimer() { sc.sendServeMsg(readIdleTimerMsg) } func (sc *serverConn) onShutdownTimer() { sc.sendServeMsg(shutdownTimerMsg) } func (sc *serverConn) sendServeMsg(msg interface{}) { @@ -1604,6 +1659,11 @@ func (sc *serverConn) processFrame(f Frame) error { func (sc *serverConn) processPing(f *PingFrame) error { sc.serveG.check() if f.IsAck() { + if sc.pingSent && sc.sentPingData == f.Data { + // This is a response to a PING we sent. + sc.pingSent = false + sc.readIdleTimer.Reset(sc.srv.ReadIdleTimeout) + } // 6.7 PING: " An endpoint MUST NOT respond to PING frames // containing this flag." return nil diff --git a/http2/server_test.go b/http2/server_test.go index ab53c26946..ec7ae043cd 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -4706,3 +4706,46 @@ func TestServerWriteByteTimeout(t *testing.T) { st.advance(1 * time.Second) // timeout after failing to write any more bytes st.wantClosed() } + +func TestServerPingSent(t *testing.T) { + const readIdleTimeout = 15 * time.Second + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + }, func(s *Server) { + s.ReadIdleTimeout = readIdleTimeout + }) + st.greet() + + st.wantIdle() + + st.advance(readIdleTimeout) + _ = readFrame[*PingFrame](t, st) + st.wantIdle() + + st.advance(14 * time.Second) + st.wantIdle() + st.advance(1 * time.Second) + st.wantClosed() +} + +func TestServerPingResponded(t *testing.T) { + const readIdleTimeout = 15 * time.Second + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + }, func(s *Server) { + s.ReadIdleTimeout = readIdleTimeout + }) + st.greet() + + st.wantIdle() + + st.advance(readIdleTimeout) + pf := readFrame[*PingFrame](t, st) + st.wantIdle() + + st.advance(14 * time.Second) + st.wantIdle() + + st.writePing(true, pf.Data) + + st.advance(2 * time.Second) + st.wantIdle() +} diff --git a/http2/write.go b/http2/write.go index 33f61398a1..6ff6bee7e9 100644 --- a/http2/write.go +++ b/http2/write.go @@ -131,6 +131,16 @@ func (se StreamError) writeFrame(ctx writeContext) error { func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } +type writePing struct { + data [8]byte +} + +func (w writePing) writeFrame(ctx writeContext) error { + return ctx.Framer().WritePing(false, w.data) +} + +func (w writePing) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.data) <= max } + type writePingAck struct{ pf *PingFrame } func (w writePingAck) writeFrame(ctx writeContext) error { From 7191757bc637cf79a7ece0546e33f903bf5e9709 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 12 Aug 2024 12:52:08 -0700 Subject: [PATCH 04/31] http2: add support for net/http HTTP2 config field For golang/go#67813 Change-Id: I6b7f857d6ed250ba8b09649730980a91b3e8d7e9 Reviewed-on: https://go-review.googlesource.com/c/net/+/607255 Reviewed-by: Brad Fitzpatrick LUCI-TryBot-Result: Go LUCI Reviewed-by: Jonathan Amsterdam --- http2/clientconn_test.go | 14 ++++- http2/config.go | 122 +++++++++++++++++++++++++++++++++++ http2/config_go124.go | 61 ++++++++++++++++++ http2/config_pre_go124.go | 16 +++++ http2/config_test.go | 95 ++++++++++++++++++++++++++++ http2/connframes_test.go | 18 ++++++ http2/server.go | 129 +++++++++++++------------------------- http2/server_test.go | 8 ++- http2/transport.go | 117 ++++++++++++++-------------------- 9 files changed, 416 insertions(+), 164 deletions(-) create mode 100644 http2/config.go create mode 100644 http2/config_go124.go create mode 100644 http2/config_pre_go124.go create mode 100644 http2/config_test.go diff --git a/http2/clientconn_test.go b/http2/clientconn_test.go index 5533790992..92d6305143 100644 --- a/http2/clientconn_test.go +++ b/http2/clientconn_test.go @@ -148,7 +148,7 @@ func (tc *testClientConn) readClientPreface() { } } -func newTestClientConn(t *testing.T, opts ...func(*Transport)) *testClientConn { +func newTestClientConn(t *testing.T, opts ...any) *testClientConn { t.Helper() tt := newTestTransport(t, opts...) @@ -486,7 +486,7 @@ type testTransport struct { ccs []*testClientConn } -func newTestTransport(t *testing.T, opts ...func(*Transport)) *testTransport { +func newTestTransport(t *testing.T, opts ...any) *testTransport { tt := &testTransport{ t: t, group: newSynctest(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)), @@ -495,7 +495,15 @@ func newTestTransport(t *testing.T, opts ...func(*Transport)) *testTransport { tr := &Transport{} for _, o := range opts { - o(tr) + switch o := o.(type) { + case func(*http.Transport): + if tr.t1 == nil { + tr.t1 = &http.Transport{} + } + o(tr.t1) + case func(*Transport): + o(tr) + } } tt.tr = tr diff --git a/http2/config.go b/http2/config.go new file mode 100644 index 0000000000..de58dfb8dc --- /dev/null +++ b/http2/config.go @@ -0,0 +1,122 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http2 + +import ( + "math" + "net/http" + "time" +) + +// http2Config is a package-internal version of net/http.HTTP2Config. +// +// http.HTTP2Config was added in Go 1.24. +// When running with a version of net/http that includes HTTP2Config, +// we merge the configuration with the fields in Transport or Server +// to produce an http2Config. +// +// Zero valued fields in http2Config are interpreted as in the +// net/http.HTTPConfig documentation. +// +// Precedence order for reconciling configurations is: +// +// - Use the net/http.{Server,Transport}.HTTP2Config value, when non-zero. +// - Otherwise use the http2.{Server.Transport} value. +// - If the resulting value is zero or out of range, use a default. +type http2Config struct { + MaxConcurrentStreams uint32 + MaxDecoderHeaderTableSize uint32 + MaxEncoderHeaderTableSize uint32 + MaxReadFrameSize uint32 + MaxUploadBufferPerConnection int32 + MaxUploadBufferPerStream int32 + SendPingTimeout time.Duration + PingTimeout time.Duration + WriteByteTimeout time.Duration + PermitProhibitedCipherSuites bool + CountError func(errType string) +} + +// configFromServer merges configuration settings from +// net/http.Server.HTTP2Config and http2.Server. +func configFromServer(h1 *http.Server, h2 *Server) http2Config { + conf := http2Config{ + MaxConcurrentStreams: h2.MaxConcurrentStreams, + MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, + MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, + MaxReadFrameSize: h2.MaxReadFrameSize, + MaxUploadBufferPerConnection: h2.MaxUploadBufferPerConnection, + MaxUploadBufferPerStream: h2.MaxUploadBufferPerStream, + SendPingTimeout: h2.ReadIdleTimeout, + PingTimeout: h2.PingTimeout, + WriteByteTimeout: h2.WriteByteTimeout, + PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, + CountError: h2.CountError, + } + fillNetHTTPServerConfig(&conf, h1) + setConfigDefaults(&conf, true) + return conf +} + +// configFromServer merges configuration settings from h2 and h2.t1.HTTP2 +// (the net/http Transport). +func configFromTransport(h2 *Transport) http2Config { + conf := http2Config{ + MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, + MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, + MaxReadFrameSize: h2.MaxReadFrameSize, + SendPingTimeout: h2.ReadIdleTimeout, + PingTimeout: h2.PingTimeout, + WriteByteTimeout: h2.WriteByteTimeout, + } + + // Unlike most config fields, where out-of-range values revert to the default, + // Transport.MaxReadFrameSize clips. + if conf.MaxReadFrameSize < minMaxFrameSize { + conf.MaxReadFrameSize = minMaxFrameSize + } else if conf.MaxReadFrameSize > maxFrameSize { + conf.MaxReadFrameSize = maxFrameSize + } + + if h2.t1 != nil { + fillNetHTTPTransportConfig(&conf, h2.t1) + } + setConfigDefaults(&conf, false) + return conf +} + +func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) { + if *v < minval || *v > maxval { + *v = defval + } +} + +func setConfigDefaults(conf *http2Config, server bool) { + setDefault(&conf.MaxConcurrentStreams, 1, math.MaxUint32, defaultMaxStreams) + setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) + setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) + if server { + setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20) + } else { + setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow) + } + if server { + setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, 1<<20) + } else { + setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, transportDefaultStreamFlow) + } + setDefault(&conf.MaxReadFrameSize, minMaxFrameSize, maxFrameSize, defaultMaxReadFrameSize) + setDefault(&conf.PingTimeout, 1, math.MaxInt64, 15*time.Second) +} + +// adjustHTTP1MaxHeaderSize converts a limit in bytes on the size of an HTTP/1 header +// to an HTTP/2 MAX_HEADER_LIST_SIZE value. +func adjustHTTP1MaxHeaderSize(n int64) int64 { + // http2's count is in a slightly different unit and includes 32 bytes per pair. + // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. + const perFieldOverhead = 32 // per http2 spec + const typicalHeaders = 10 // conservative + return n + typicalHeaders*perFieldOverhead +} diff --git a/http2/config_go124.go b/http2/config_go124.go new file mode 100644 index 0000000000..e3784123c8 --- /dev/null +++ b/http2/config_go124.go @@ -0,0 +1,61 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package http2 + +import "net/http" + +// fillNetHTTPServerConfig sets fields in conf from srv.HTTP2. +func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) { + fillNetHTTPConfig(conf, srv.HTTP2) +} + +// fillNetHTTPServerConfig sets fields in conf from tr.HTTP2. +func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) { + fillNetHTTPConfig(conf, tr.HTTP2) +} + +func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) { + if h2 == nil { + return + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if h2.MaxEncoderHeaderTableSize != 0 { + conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize) + } + if h2.MaxDecoderHeaderTableSize != 0 { + conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize) + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if h2.MaxReadFrameSize != 0 { + conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize) + } + if h2.MaxReceiveBufferPerConnection != 0 { + conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection) + } + if h2.MaxReceiveBufferPerStream != 0 { + conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream) + } + if h2.SendPingTimeout != 0 { + conf.SendPingTimeout = h2.SendPingTimeout + } + if h2.PingTimeout != 0 { + conf.PingTimeout = h2.PingTimeout + } + if h2.WriteByteTimeout != 0 { + conf.WriteByteTimeout = h2.WriteByteTimeout + } + if h2.PermitProhibitedCipherSuites { + conf.PermitProhibitedCipherSuites = true + } + if h2.CountError != nil { + conf.CountError = h2.CountError + } +} diff --git a/http2/config_pre_go124.go b/http2/config_pre_go124.go new file mode 100644 index 0000000000..060fd6c64c --- /dev/null +++ b/http2/config_pre_go124.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.24 + +package http2 + +import "net/http" + +// Pre-Go 1.24 fallback. +// The Server.HTTP2 and Transport.HTTP2 config fields were added in Go 1.24. + +func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) {} + +func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) {} diff --git a/http2/config_test.go b/http2/config_test.go new file mode 100644 index 0000000000..b8e7a7b043 --- /dev/null +++ b/http2/config_test.go @@ -0,0 +1,95 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package http2 + +import ( + "net/http" + "testing" + "time" +) + +func TestConfigServerSettings(t *testing.T) { + config := &http.HTTP2Config{ + MaxConcurrentStreams: 1, + MaxDecoderHeaderTableSize: 1<<20 + 2, + MaxEncoderHeaderTableSize: 1<<20 + 3, + MaxReadFrameSize: 1<<20 + 4, + MaxReceiveBufferPerConnection: 64<<10 + 5, + MaxReceiveBufferPerStream: 64<<10 + 6, + } + const maxHeaderBytes = 4096 + 7 + st := newServerTester(t, nil, func(s *http.Server) { + s.MaxHeaderBytes = maxHeaderBytes + s.HTTP2 = config + }) + st.writePreface() + st.writeSettings() + st.wantSettings(map[SettingID]uint32{ + SettingMaxConcurrentStreams: uint32(config.MaxConcurrentStreams), + SettingHeaderTableSize: uint32(config.MaxDecoderHeaderTableSize), + SettingInitialWindowSize: uint32(config.MaxReceiveBufferPerStream), + SettingMaxFrameSize: uint32(config.MaxReadFrameSize), + SettingMaxHeaderListSize: maxHeaderBytes + (32 * 10), + }) +} + +func TestConfigTransportSettings(t *testing.T) { + config := &http.HTTP2Config{ + MaxConcurrentStreams: 1, // ignored by Transport + MaxDecoderHeaderTableSize: 1<<20 + 2, + MaxEncoderHeaderTableSize: 1<<20 + 3, + MaxReadFrameSize: 1<<20 + 4, + MaxReceiveBufferPerConnection: 64<<10 + 5, + MaxReceiveBufferPerStream: 64<<10 + 6, + } + const maxHeaderBytes = 4096 + 7 + tc := newTestClientConn(t, func(tr *http.Transport) { + tr.HTTP2 = config + tr.MaxResponseHeaderBytes = maxHeaderBytes + }) + tc.wantSettings(map[SettingID]uint32{ + SettingHeaderTableSize: uint32(config.MaxDecoderHeaderTableSize), + SettingInitialWindowSize: uint32(config.MaxReceiveBufferPerStream), + SettingMaxFrameSize: uint32(config.MaxReadFrameSize), + SettingMaxHeaderListSize: maxHeaderBytes + (32 * 10), + }) + tc.wantWindowUpdate(0, uint32(config.MaxReceiveBufferPerConnection)) +} + +func TestConfigPingTimeoutServer(t *testing.T) { + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + }, func(s *Server) { + s.ReadIdleTimeout = 2 * time.Second + s.PingTimeout = 3 * time.Second + }) + st.greet() + + st.advance(2 * time.Second) + _ = readFrame[*PingFrame](t, st) + st.advance(3 * time.Second) + st.wantClosed() +} + +func TestConfigPingTimeoutTransport(t *testing.T) { + tc := newTestClientConn(t, func(tr *Transport) { + tr.ReadIdleTimeout = 2 * time.Second + tr.PingTimeout = 3 * time.Second + }) + tc.greet() + + req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) + rt := tc.roundTrip(req) + tc.wantFrameType(FrameHeaders) + + tc.advance(2 * time.Second) + tc.wantFrameType(FramePing) + tc.advance(3 * time.Second) + err := rt.err() + if err == nil { + t.Fatalf("expected connection to close") + } +} diff --git a/http2/connframes_test.go b/http2/connframes_test.go index 1f7834c01f..2c4532571a 100644 --- a/http2/connframes_test.go +++ b/http2/connframes_test.go @@ -261,6 +261,24 @@ func (tf *testConnFramer) wantRSTStream(streamID uint32, code ErrCode) { } } +func (tf *testConnFramer) wantSettings(want map[SettingID]uint32) { + fr := readFrame[*SettingsFrame](tf.t, tf) + if fr.Header().Flags.Has(FlagSettingsAck) { + tf.t.Errorf("got SETTINGS frame with ACK set, want no ACK") + } + for wantID, wantVal := range want { + gotVal, ok := fr.Value(wantID) + if !ok { + tf.t.Errorf("SETTINGS: %v is not set, want %v", wantID, wantVal) + } else if gotVal != wantVal { + tf.t.Errorf("SETTINGS: %v is %v, want %v", wantID, gotVal, wantVal) + } + } + if tf.t.Failed() { + tf.t.Fatalf("%v", fr) + } +} + func (tf *testConnFramer) wantSettingsAck() { tf.t.Helper() fr := readFrame[*SettingsFrame](tf.t, tf) diff --git a/http2/server.go b/http2/server.go index 645c8da696..617b4a4762 100644 --- a/http2/server.go +++ b/http2/server.go @@ -53,10 +53,14 @@ import ( ) const ( - prefaceTimeout = 10 * time.Second - firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway - handlerChunkWriteSize = 4 << 10 - defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to? + prefaceTimeout = 10 * time.Second + firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway + handlerChunkWriteSize = 4 << 10 + defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to? + + // maxQueuedControlFrames is the maximum number of control frames like + // SETTINGS, PING and RST_STREAM that will be queued for writing before + // the connection is closed to prevent memory exhaustion attacks. maxQueuedControlFrames = 10000 ) @@ -206,57 +210,6 @@ func (s *Server) afterFunc(d time.Duration, f func()) timer { return timeTimer{time.AfterFunc(d, f)} } -func (s *Server) initialConnRecvWindowSize() int32 { - if s.MaxUploadBufferPerConnection >= initialWindowSize { - return s.MaxUploadBufferPerConnection - } - return 1 << 20 -} - -func (s *Server) initialStreamRecvWindowSize() int32 { - if s.MaxUploadBufferPerStream > 0 { - return s.MaxUploadBufferPerStream - } - return 1 << 20 -} - -func (s *Server) maxReadFrameSize() uint32 { - if v := s.MaxReadFrameSize; v >= minMaxFrameSize && v <= maxFrameSize { - return v - } - return defaultMaxReadFrameSize -} - -func (s *Server) maxConcurrentStreams() uint32 { - if v := s.MaxConcurrentStreams; v > 0 { - return v - } - return defaultMaxStreams -} - -func (s *Server) maxDecoderHeaderTableSize() uint32 { - if v := s.MaxDecoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -func (s *Server) maxEncoderHeaderTableSize() uint32 { - if v := s.MaxEncoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -// maxQueuedControlFrames is the maximum number of control frames like -// SETTINGS, PING and RST_STREAM that will be queued for writing before -// the connection is closed to prevent memory exhaustion attacks. -func (s *Server) maxQueuedControlFrames() int { - // TODO: if anybody asks, add a Server field, and remember to define the - // behavior of negative values. - return maxQueuedControlFrames -} - type serverInternalState struct { mu sync.Mutex activeConns map[*serverConn]struct{} @@ -457,13 +410,15 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon baseCtx, cancel := serverConnBaseContext(c, opts) defer cancel() + http1srv := opts.baseConfig() + conf := configFromServer(http1srv, s) sc := &serverConn{ srv: s, - hs: opts.baseConfig(), + hs: http1srv, conn: c, baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), - bw: newBufferedWriter(s.group, c, s.WriteByteTimeout), + bw: newBufferedWriter(s.group, c, conf.WriteByteTimeout), handler: opts.handler(), streams: make(map[uint32]*stream), readFrameCh: make(chan readFrameResult), @@ -473,9 +428,12 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way doneServing: make(chan struct{}), clientMaxStreams: math.MaxUint32, // Section 6.5.2: "Initially, there is no limit to this value" - advMaxStreams: s.maxConcurrentStreams(), + advMaxStreams: conf.MaxConcurrentStreams, initialStreamSendWindowSize: initialWindowSize, + initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, maxFrameSize: initialMaxFrameSize, + pingTimeout: conf.PingTimeout, + countErrorFunc: conf.CountError, serveG: newGoroutineLock(), pushEnabled: true, sawClientPreface: opts.SawClientPreface, @@ -508,15 +466,15 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon sc.flow.add(initialWindowSize) sc.inflow.init(initialWindowSize) sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) - sc.hpackEncoder.SetMaxDynamicTableSizeLimit(s.maxEncoderHeaderTableSize()) + sc.hpackEncoder.SetMaxDynamicTableSizeLimit(conf.MaxEncoderHeaderTableSize) fr := NewFramer(sc.bw, c) - if s.CountError != nil { - fr.countError = s.CountError + if conf.CountError != nil { + fr.countError = conf.CountError } - fr.ReadMetaHeaders = hpack.NewDecoder(s.maxDecoderHeaderTableSize(), nil) + fr.ReadMetaHeaders = hpack.NewDecoder(conf.MaxDecoderHeaderTableSize, nil) fr.MaxHeaderListSize = sc.maxHeaderListSize() - fr.SetMaxReadFrameSize(s.maxReadFrameSize()) + fr.SetMaxReadFrameSize(conf.MaxReadFrameSize) sc.framer = fr if tc, ok := c.(connectionStater); ok { @@ -549,7 +507,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon // So for now, do nothing here again. } - if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) { + if !conf.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) { // "Endpoints MAY choose to generate a connection error // (Section 5.4.1) of type INADEQUATE_SECURITY if one of // the prohibited cipher suites are negotiated." @@ -586,7 +544,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon opts.UpgradeRequest = nil } - sc.serve() + sc.serve(conf) } func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) { @@ -626,6 +584,7 @@ type serverConn struct { tlsState *tls.ConnectionState // shared by all handlers, like net/http remoteAddrStr string writeSched WriteScheduler + countErrorFunc func(errType string) // Everything following is owned by the serve loop; use serveG.check(): serveG goroutineLock // used to verify funcs are on serve() @@ -645,6 +604,7 @@ type serverConn struct { streams map[uint32]*stream unstartedHandlers []unstartedHandler initialStreamSendWindowSize int32 + initialStreamRecvWindowSize int32 maxFrameSize int32 peerMaxHeaderListSize uint32 // zero means unknown (default) canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case @@ -660,6 +620,8 @@ type serverConn struct { goAwayCode ErrCode shutdownTimer timer // nil until used idleTimer timer // nil if unused + readIdleTimeout time.Duration + pingTimeout time.Duration readIdleTimer timer // nil if unused // Owned by the writeFrameAsync goroutine: @@ -675,11 +637,7 @@ func (sc *serverConn) maxHeaderListSize() uint32 { if n <= 0 { n = http.DefaultMaxHeaderBytes } - // http2's count is in a slightly different unit and includes 32 bytes per pair. - // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. - const perFieldOverhead = 32 // per http2 spec - const typicalHeaders = 10 // conservative - return uint32(n + typicalHeaders*perFieldOverhead) + return uint32(adjustHTTP1MaxHeaderSize(int64(n))) } func (sc *serverConn) curOpenStreams() uint32 { @@ -943,7 +901,7 @@ func (sc *serverConn) notePanic() { } } -func (sc *serverConn) serve() { +func (sc *serverConn) serve(conf http2Config) { sc.serveG.check() defer sc.notePanic() defer sc.conn.Close() @@ -957,18 +915,18 @@ func (sc *serverConn) serve() { sc.writeFrame(FrameWriteRequest{ write: writeSettings{ - {SettingMaxFrameSize, sc.srv.maxReadFrameSize()}, + {SettingMaxFrameSize, conf.MaxReadFrameSize}, {SettingMaxConcurrentStreams, sc.advMaxStreams}, {SettingMaxHeaderListSize, sc.maxHeaderListSize()}, - {SettingHeaderTableSize, sc.srv.maxDecoderHeaderTableSize()}, - {SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())}, + {SettingHeaderTableSize, conf.MaxDecoderHeaderTableSize}, + {SettingInitialWindowSize, uint32(sc.initialStreamRecvWindowSize)}, }, }) sc.unackedSettings++ // Each connection starts with initialWindowSize inflow tokens. // If a higher value is configured, we add more tokens. - if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 { + if diff := conf.MaxUploadBufferPerConnection - initialWindowSize; diff > 0 { sc.sendWindowUpdate(nil, int(diff)) } @@ -988,8 +946,9 @@ func (sc *serverConn) serve() { defer sc.idleTimer.Stop() } - if sc.srv.ReadIdleTimeout > 0 { - sc.readIdleTimer = sc.srv.afterFunc(sc.srv.ReadIdleTimeout, sc.onReadIdleTimer) + if conf.SendPingTimeout > 0 { + sc.readIdleTimeout = conf.SendPingTimeout + sc.readIdleTimer = sc.srv.afterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) defer sc.readIdleTimer.Stop() } @@ -1068,7 +1027,7 @@ func (sc *serverConn) serve() { // If the peer is causing us to generate a lot of control frames, // but not reading them from us, assume they are trying to make us // run out of memory. - if sc.queuedControlFrames > sc.srv.maxQueuedControlFrames() { + if sc.queuedControlFrames > maxQueuedControlFrames { sc.vlogf("http2: too many control frames in send queue, closing connection") return } @@ -1091,7 +1050,7 @@ func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) { return } - pingAt := lastFrameReadTime.Add(sc.srv.ReadIdleTimeout) + pingAt := lastFrameReadTime.Add(sc.readIdleTimeout) now := sc.srv.now() if pingAt.After(now) { // We received frames since arming the ping timer. @@ -1107,11 +1066,7 @@ func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) { sc.writeFrame(FrameWriteRequest{ write: &writePing{data: sc.sentPingData}, }) - pingTimeout := sc.srv.PingTimeout - if pingTimeout <= 0 { - pingTimeout = 15 * time.Second - } - sc.readIdleTimer.Reset(pingTimeout) + sc.readIdleTimer.Reset(sc.pingTimeout) } type serverMessage int @@ -1662,7 +1617,7 @@ func (sc *serverConn) processPing(f *PingFrame) error { if sc.pingSent && sc.sentPingData == f.Data { // This is a response to a PING we sent. sc.pingSent = false - sc.readIdleTimer.Reset(sc.srv.ReadIdleTimeout) + sc.readIdleTimer.Reset(sc.readIdleTimeout) } // 6.7 PING: " An endpoint MUST NOT respond to PING frames // containing this flag." @@ -2230,7 +2185,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.cw.Init() st.flow.conn = &sc.flow // link to conn-level counter st.flow.add(sc.initialStreamSendWindowSize) - st.inflow.init(sc.srv.initialStreamRecvWindowSize()) + st.inflow.init(sc.initialStreamRecvWindowSize) if sc.hs.WriteTimeout > 0 { st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } @@ -3371,7 +3326,7 @@ func (sc *serverConn) countError(name string, err error) error { if sc == nil || sc.srv == nil { return err } - f := sc.srv.CountError + f := sc.countErrorFunc if f == nil { return err } diff --git a/http2/server_test.go b/http2/server_test.go index ec7ae043cd..77de1be209 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -461,7 +461,8 @@ func (st *serverTester) greetAndCheckSettings(checkSetting func(s Setting) error if f.FrameHeader.StreamID != 0 { st.t.Fatalf("WindowUpdate StreamID = %d; want 0", f.FrameHeader.StreamID) } - incr := uint32(st.sc.srv.initialConnRecvWindowSize() - initialWindowSize) + conf := configFromServer(st.sc.hs, st.sc.srv) + incr := uint32(conf.MaxUploadBufferPerConnection - initialWindowSize) if f.Increment != incr { st.t.Fatalf("WindowUpdate increment = %d; want %d", f.Increment, incr) } @@ -589,11 +590,12 @@ func (st *serverTester) bodylessReq1(headers ...string) { } func (st *serverTester) wantFlowControlConsumed(streamID, consumed int32) { + conf := configFromServer(st.sc.hs, st.sc.srv) var initial int32 if streamID == 0 { - initial = st.sc.srv.initialConnRecvWindowSize() + initial = conf.MaxUploadBufferPerConnection } else { - initial = st.sc.srv.initialStreamRecvWindowSize() + initial = conf.MaxUploadBufferPerStream } donec := make(chan struct{}) st.sc.sendServeMsg(func(sc *serverConn) { diff --git a/http2/transport.go b/http2/transport.go index 49fc792adc..0c5f64aa8b 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -226,40 +226,26 @@ func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (co } func (t *Transport) maxHeaderListSize() uint32 { - if t.MaxHeaderListSize == 0 { + n := int64(t.MaxHeaderListSize) + if t.t1 != nil && t.t1.MaxResponseHeaderBytes != 0 { + n = t.t1.MaxResponseHeaderBytes + if n > 0 { + n = adjustHTTP1MaxHeaderSize(n) + } + } + if n <= 0 { return 10 << 20 } - if t.MaxHeaderListSize == 0xffffffff { + if n >= 0xffffffff { return 0 } - return t.MaxHeaderListSize -} - -func (t *Transport) maxFrameReadSize() uint32 { - if t.MaxReadFrameSize == 0 { - return 0 // use the default provided by the peer - } - if t.MaxReadFrameSize < minMaxFrameSize { - return minMaxFrameSize - } - if t.MaxReadFrameSize > maxFrameSize { - return maxFrameSize - } - return t.MaxReadFrameSize + return uint32(n) } func (t *Transport) disableCompression() bool { return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) } -func (t *Transport) pingTimeout() time.Duration { - if t.PingTimeout == 0 { - return 15 * time.Second - } - return t.PingTimeout - -} - // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. // It returns an error if t1 has already been HTTP/2-enabled. // @@ -369,11 +355,14 @@ type ClientConn struct { lastActive time.Time lastIdle time.Time // time last idle // Settings from peer: (also guarded by wmu) - maxFrameSize uint32 - maxConcurrentStreams uint32 - peerMaxHeaderListSize uint64 - peerMaxHeaderTableSize uint32 - initialWindowSize uint32 + maxFrameSize uint32 + maxConcurrentStreams uint32 + peerMaxHeaderListSize uint64 + peerMaxHeaderTableSize uint32 + initialWindowSize uint32 + initialStreamRecvWindowSize int32 + readIdleTimeout time.Duration + pingTimeout time.Duration // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // Write to reqHeaderMu to lock it, read from it to unlock. @@ -745,39 +734,29 @@ func (t *Transport) expectContinueTimeout() time.Duration { return t.t1.ExpectContinueTimeout } -func (t *Transport) maxDecoderHeaderTableSize() uint32 { - if v := t.MaxDecoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -func (t *Transport) maxEncoderHeaderTableSize() uint32 { - if v := t.MaxEncoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { return t.newClientConn(c, t.disableKeepAlives()) } func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { + conf := configFromTransport(t) cc := &ClientConn{ - t: t, - tconn: c, - readerDone: make(chan struct{}), - nextStreamID: 1, - maxFrameSize: 16 << 10, // spec default - initialWindowSize: 65535, // spec default - maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. - peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. - streams: make(map[uint32]*clientStream), - singleUse: singleUse, - wantSettingsAck: true, - pings: make(map[[8]byte]chan struct{}), - reqHeaderMu: make(chan struct{}, 1), + t: t, + tconn: c, + readerDone: make(chan struct{}), + nextStreamID: 1, + maxFrameSize: 16 << 10, // spec default + initialWindowSize: 65535, // spec default + initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, + maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. + peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. + streams: make(map[uint32]*clientStream), + singleUse: singleUse, + wantSettingsAck: true, + readIdleTimeout: conf.SendPingTimeout, + pingTimeout: conf.PingTimeout, + pings: make(map[[8]byte]chan struct{}), + reqHeaderMu: make(chan struct{}, 1), } var group synctestGroupInterface if t.transportTestHooks != nil { @@ -798,23 +777,21 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro cc.bw = bufio.NewWriter(stickyErrWriter{ group: group, conn: c, - timeout: t.WriteByteTimeout, + timeout: conf.WriteByteTimeout, err: &cc.werr, }) cc.br = bufio.NewReader(c) cc.fr = NewFramer(cc.bw, cc.br) - if t.maxFrameReadSize() != 0 { - cc.fr.SetMaxReadFrameSize(t.maxFrameReadSize()) - } + cc.fr.SetMaxReadFrameSize(conf.MaxReadFrameSize) if t.CountError != nil { cc.fr.countError = t.CountError } - maxHeaderTableSize := t.maxDecoderHeaderTableSize() + maxHeaderTableSize := conf.MaxDecoderHeaderTableSize cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil) cc.fr.MaxHeaderListSize = t.maxHeaderListSize() cc.henc = hpack.NewEncoder(&cc.hbuf) - cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize()) + cc.henc.SetMaxDynamicTableSizeLimit(conf.MaxEncoderHeaderTableSize) cc.peerMaxHeaderTableSize = initialHeaderTableSize if cs, ok := c.(connectionStater); ok { @@ -824,11 +801,9 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro initialSettings := []Setting{ {ID: SettingEnablePush, Val: 0}, - {ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow}, - } - if max := t.maxFrameReadSize(); max != 0 { - initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: max}) + {ID: SettingInitialWindowSize, Val: uint32(cc.initialStreamRecvWindowSize)}, } + initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: conf.MaxReadFrameSize}) if max := t.maxHeaderListSize(); max != 0 { initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max}) } @@ -838,8 +813,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro cc.bw.Write(clientPreface) cc.fr.WriteSettings(initialSettings...) - cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow) - cc.inflow.init(transportDefaultConnFlow + initialWindowSize) + cc.fr.WriteWindowUpdate(0, uint32(conf.MaxUploadBufferPerConnection)) + cc.inflow.init(conf.MaxUploadBufferPerConnection + initialWindowSize) cc.bw.Flush() if cc.werr != nil { cc.Close() @@ -857,7 +832,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro } func (cc *ClientConn) healthCheck() { - pingTimeout := cc.t.pingTimeout() + pingTimeout := cc.pingTimeout // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) @@ -2189,7 +2164,7 @@ type resAndError struct { func (cc *ClientConn) addStreamLocked(cs *clientStream) { cs.flow.add(int32(cc.initialWindowSize)) cs.flow.setConnFlow(&cc.flow) - cs.inflow.init(transportDefaultStreamFlow) + cs.inflow.init(cc.initialStreamRecvWindowSize) cs.ID = cc.nextStreamID cc.nextStreamID += 2 cc.streams[cs.ID] = cs @@ -2335,7 +2310,7 @@ func (cc *ClientConn) countReadFrameError(err error) { func (rl *clientConnReadLoop) run() error { cc := rl.cc gotSettings := false - readIdleTimeout := cc.t.ReadIdleTimeout + readIdleTimeout := cc.readIdleTimeout var t timer if readIdleTimeout != 0 { t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) From f88258d67e0f0f144c79964ca05bb81d51ee8411 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 25 Sep 2024 19:42:35 +0000 Subject: [PATCH 05/31] websocket: update nhooyr.io/websocket to github.com/coder/websocket Maintenance of nhooyr.io/websocket has moved to github.com/coder/websocket. Read more about the transition at https://coder.com/blog/websocket Updates golang/go#18152 Change-Id: Ia2b11c9a57ad7ded775b50a5bbb7ea91562d39b5 GitHub-Last-Rev: 59cea5e101430586ca445f6e119e25cae5242254 GitHub-Pull-Request: golang/net#222 Reviewed-on: https://go-review.googlesource.com/c/net/+/614075 Reviewed-by: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Damien Neil Auto-Submit: Damien Neil --- websocket/websocket.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websocket/websocket.go b/websocket/websocket.go index 923a5780ec..ac76165ceb 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -8,7 +8,7 @@ // This package currently lacks some features found in an alternative // and more actively maintained WebSocket package: // -// https://pkg.go.dev/nhooyr.io/websocket +// https://pkg.go.dev/github.com/coder/websocket package websocket // import "golang.org/x/net/websocket" import ( From 6cc5ac4e9a03d73b331eb1d6db98a02e558243b7 Mon Sep 17 00:00:00 2001 From: Gopher Robot Date: Fri, 4 Oct 2024 16:00:50 +0000 Subject: [PATCH 06/31] go.mod: update golang.org/x dependencies Update golang.org/x dependencies to their latest tagged versions. Change-Id: I57613519f6e5795e60d258865e81f6954d672606 Reviewed-on: https://go-review.googlesource.com/c/net/+/617959 Reviewed-by: David Chase Auto-Submit: Gopher Robot Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index ffda816a88..77935d3f2b 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module golang.org/x/net go 1.18 require ( - golang.org/x/crypto v0.27.0 - golang.org/x/sys v0.25.0 - golang.org/x/term v0.24.0 - golang.org/x/text v0.18.0 + golang.org/x/crypto v0.28.0 + golang.org/x/sys v0.26.0 + golang.org/x/term v0.25.0 + golang.org/x/text v0.19.0 ) diff --git a/go.sum b/go.sum index a1a56bf3d0..1b8c61d600 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= From 42b11863606139133313f265e6cf2a4d1d8ca972 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 1 Mar 2023 15:35:42 -0800 Subject: [PATCH 07/31] http2: support ResponseController.EnableFullDuplex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ResponseController.EnableFullDuplex method indicates that an HTTP handler intends to interleave reads from a request body with writes to the response body. Add an EnableFullDuplex method to the ResponseWriter so we don't return a not-supported error. The HTTP/2 server always supports full duplex, so this is a no-op. For golang/go#57786 Change-Id: I6529e6ce01d59b8b48fb67ba7c244255df57c174 Reviewed-on: https://go-review.googlesource.com/c/net/+/472717 Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Reviewed-by: Brad Fitzpatrick Reviewed-by: Дарья Бочкар --- http2/server.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/http2/server.go b/http2/server.go index 617b4a4762..d6d8ac88b8 100644 --- a/http2/server.go +++ b/http2/server.go @@ -2880,6 +2880,11 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { return nil } +func (w *responseWriter) EnableFullDuplex() error { + // We always support full duplex responses, so this is a no-op. + return nil +} + func (w *responseWriter) Flush() { w.FlushError() } From 5716b9813d2c78aa3bb6e08160517facfb2e84e6 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Fri, 11 Oct 2024 14:23:01 +0800 Subject: [PATCH 08/31] internal/socket: execute gofmt Change-Id: Ifc793d535c31da3ba183ee44e1808e0072d7f099 Reviewed-on: https://go-review.googlesource.com/c/net/+/619595 Reviewed-by: Damien Neil Auto-Submit: Damien Neil LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor --- internal/socket/zsys_openbsd_ppc64.go | 28 ++++++++++++------------- internal/socket/zsys_openbsd_riscv64.go | 28 ++++++++++++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/internal/socket/zsys_openbsd_ppc64.go b/internal/socket/zsys_openbsd_ppc64.go index cebde7634f..3c9576e2d8 100644 --- a/internal/socket/zsys_openbsd_ppc64.go +++ b/internal/socket/zsys_openbsd_ppc64.go @@ -4,27 +4,27 @@ package socket type iovec struct { - Base *byte - Len uint64 + Base *byte + Len uint64 } type msghdr struct { - Name *byte - Namelen uint32 - Iov *iovec - Iovlen uint32 - Control *byte - Controllen uint32 - Flags int32 + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 } type cmsghdr struct { - Len uint32 - Level int32 - Type int32 + Len uint32 + Level int32 + Type int32 } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x30 + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 ) diff --git a/internal/socket/zsys_openbsd_riscv64.go b/internal/socket/zsys_openbsd_riscv64.go index cebde7634f..3c9576e2d8 100644 --- a/internal/socket/zsys_openbsd_riscv64.go +++ b/internal/socket/zsys_openbsd_riscv64.go @@ -4,27 +4,27 @@ package socket type iovec struct { - Base *byte - Len uint64 + Base *byte + Len uint64 } type msghdr struct { - Name *byte - Namelen uint32 - Iov *iovec - Iovlen uint32 - Control *byte - Controllen uint32 - Flags int32 + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 } type cmsghdr struct { - Len uint32 - Level int32 - Type int32 + Len uint32 + Level int32 + Type int32 } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x30 + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 ) From 4783315416d92ff3d4664762748bd21776b42b98 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 23 Sep 2024 15:06:22 -0700 Subject: [PATCH 09/31] http2: limit 1xx based on size, do not limit when delivered Replace Transport's limit of 5 1xx responses with a limit based on the maximum header size: The total size of all 1xx response headers must not exceed the limit we use on the size of the final response headers. (This differs slightly from the corresponding HTTP/1 change, which imposes a limit on all 1xx response headers *plus* the final response headers. The difference isn't substantial, and this implementation fits better with the HTTP/2 framer.) When the user is reading 1xx responses using a Got1xxResponse client trace hook, disable the limit: Each 1xx response is individually limited by the header size limit, but there is no limit on the total number of responses. The user is responsible for imposing a limit if they want one. For golang/go#65035 Change-Id: I9c19dbf068e0f580789d952f63113b3d21ad86fc Reviewed-on: https://go-review.googlesource.com/c/net/+/615295 Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Auto-Submit: Damien Neil Reviewed-by: Brad Fitzpatrick --- http2/transport.go | 41 ++++++++++++++----- http2/transport_test.go | 91 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 11 deletions(-) diff --git a/http2/transport.go b/http2/transport.go index 0c5f64aa8b..e989bd19e8 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -420,12 +420,12 @@ type clientStream struct { sentHeaders bool // owned by clientConnReadLoop: - firstByte bool // got the first response byte - pastHeaders bool // got first MetaHeadersFrame (actual headers) - pastTrailers bool // got optional second MetaHeadersFrame (trailers) - num1xx uint8 // number of 1xx responses seen - readClosed bool // peer sent an END_STREAM flag - readAborted bool // read loop reset the stream + firstByte bool // got the first response byte + pastHeaders bool // got first MetaHeadersFrame (actual headers) + pastTrailers bool // got optional second MetaHeadersFrame (trailers) + readClosed bool // peer sent an END_STREAM flag + readAborted bool // read loop reset the stream + totalHeaderSize int64 // total size of 1xx headers seen trailer http.Header // accumulated trailers resTrailer *http.Header // client's Response.Trailer @@ -2494,15 +2494,34 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra if f.StreamEnded() { return nil, errors.New("1xx informational response with END_STREAM flag") } - cs.num1xx++ - const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http - if cs.num1xx > max1xxResponses { - return nil, errors.New("http2: too many 1xx informational responses") - } if fn := cs.get1xxTraceFunc(); fn != nil { + // If the 1xx response is being delivered to the user, + // then they're responsible for limiting the number + // of responses. if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil { return nil, err } + } else { + // If the user didn't examine the 1xx response, then we + // limit the size of all 1xx headers. + // + // This differs a bit from the HTTP/1 implementation, which + // limits the size of all 1xx headers plus the final response. + // Use the larger limit of MaxHeaderListSize and + // net/http.Transport.MaxResponseHeaderBytes. + limit := int64(cs.cc.t.maxHeaderListSize()) + if t1 := cs.cc.t.t1; t1 != nil && t1.MaxResponseHeaderBytes > limit { + limit = t1.MaxResponseHeaderBytes + } + for _, h := range f.Fields { + cs.totalHeaderSize += int64(h.Size()) + } + if cs.totalHeaderSize > limit { + if VerboseLogs { + log.Printf("http2: 1xx informational responses too large") + } + return nil, errors.New("header list too large") + } } if statusCode == 100 { traceGot100Continue(cs.trace) diff --git a/http2/transport_test.go b/http2/transport_test.go index 498e27932c..757a45a7ad 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -5421,3 +5421,94 @@ func TestIssue67671(t *testing.T) { res.Body.Close() } } + +func TestTransport1xxLimits(t *testing.T) { + for _, test := range []struct { + name string + opt any + ctxfn func(context.Context) context.Context + hcount int + limited bool + }{{ + name: "default", + hcount: 10, + limited: false, + }, { + name: "MaxHeaderListSize", + opt: func(tr *Transport) { + tr.MaxHeaderListSize = 10000 + }, + hcount: 10, + limited: true, + }, { + name: "MaxResponseHeaderBytes", + opt: func(tr *http.Transport) { + tr.MaxResponseHeaderBytes = 10000 + }, + hcount: 10, + limited: true, + }, { + name: "limit by client trace", + ctxfn: func(ctx context.Context) context.Context { + count := 0 + return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{ + Got1xxResponse: func(code int, header textproto.MIMEHeader) error { + count++ + if count >= 10 { + return errors.New("too many 1xx") + } + return nil + }, + }) + }, + hcount: 10, + limited: true, + }, { + name: "limit disabled by client trace", + opt: func(tr *Transport) { + tr.MaxHeaderListSize = 10000 + }, + ctxfn: func(ctx context.Context) context.Context { + return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{ + Got1xxResponse: func(code int, header textproto.MIMEHeader) error { + return nil + }, + }) + }, + hcount: 20, + limited: false, + }} { + t.Run(test.name, func(t *testing.T) { + tc := newTestClientConn(t, test.opt) + tc.greet() + + ctx := context.Background() + if test.ctxfn != nil { + ctx = test.ctxfn(ctx) + } + req, _ := http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil) + rt := tc.roundTrip(req) + tc.wantFrameType(FrameHeaders) + + for i := 0; i < test.hcount; i++ { + if fr, err := tc.fr.ReadFrame(); err != os.ErrDeadlineExceeded { + t.Fatalf("after writing %v 1xx headers: read %v, %v; want idle", i, fr, err) + } + tc.writeHeaders(HeadersFrameParam{ + StreamID: rt.streamID(), + EndHeaders: true, + EndStream: false, + BlockFragment: tc.makeHeaderBlockFragment( + ":status", "103", + "x-field", strings.Repeat("a", 1000), + ), + }) + } + if test.limited { + tc.wantFrameType(FrameRSTStream) + } else { + tc.wantIdle() + } + }) + } +} From 511cc3a40645a2f6ed3d21a1d0803b5057b9aaa1 Mon Sep 17 00:00:00 2001 From: Carlana Johnson Date: Tue, 29 Oct 2024 01:29:34 +0000 Subject: [PATCH 10/31] html: add Node.{Ancestors,ChildNodes,Descendants}() Adds iterators for the parents, immediate children, and all children of a Node respectively. Fixes golang/go#62113 Change-Id: Iab015872cc3a20fe5e7cae3bc90b89cba68cc3f8 GitHub-Last-Rev: d99de580ab8c30bb04c9fee401d6bfc5dd55d1ec GitHub-Pull-Request: golang/net#215 Reviewed-on: https://go-review.googlesource.com/c/net/+/594195 Reviewed-by: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Auto-Submit: Ian Lance Taylor Reviewed-by: Damien Neil --- html/doc.go | 7 +--- html/example_test.go | 13 +++--- html/iter.go | 56 ++++++++++++++++++++++++++ html/iter_test.go | 96 ++++++++++++++++++++++++++++++++++++++++++++ html/node.go | 4 ++ 5 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 html/iter.go create mode 100644 html/iter_test.go diff --git a/html/doc.go b/html/doc.go index 3a7e5ab176..885c4c5936 100644 --- a/html/doc.go +++ b/html/doc.go @@ -78,16 +78,11 @@ example, to process each anchor node in depth-first order: if err != nil { // ... } - var f func(*html.Node) - f = func(n *html.Node) { + for n := range doc.Descendants() { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } - for c := n.FirstChild; c != nil; c = c.NextSibling { - f(c) - } } - f(doc) The relevant specifications include: https://html.spec.whatwg.org/multipage/syntax.html and diff --git a/html/example_test.go b/html/example_test.go index 0b06ed7730..830f0b27af 100644 --- a/html/example_test.go +++ b/html/example_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.23 + // This example demonstrates parsing HTML data and walking the resulting tree. package html_test @@ -11,6 +13,7 @@ import ( "strings" "golang.org/x/net/html" + "golang.org/x/net/html/atom" ) func ExampleParse() { @@ -19,9 +22,8 @@ func ExampleParse() { if err != nil { log.Fatal(err) } - var f func(*html.Node) - f = func(n *html.Node) { - if n.Type == html.ElementNode && n.Data == "a" { + for n := range doc.Descendants() { + if n.Type == html.ElementNode && n.DataAtom == atom.A { for _, a := range n.Attr { if a.Key == "href" { fmt.Println(a.Val) @@ -29,11 +31,8 @@ func ExampleParse() { } } } - for c := n.FirstChild; c != nil; c = c.NextSibling { - f(c) - } } - f(doc) + // Output: // foo // /bar/baz diff --git a/html/iter.go b/html/iter.go new file mode 100644 index 0000000000..54be8fd30f --- /dev/null +++ b/html/iter.go @@ -0,0 +1,56 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.23 + +package html + +import "iter" + +// Ancestors returns an iterator over the ancestors of n, starting with n.Parent. +// +// Mutating a Node or its parents while iterating may have unexpected results. +func (n *Node) Ancestors() iter.Seq[*Node] { + _ = n.Parent // eager nil check + + return func(yield func(*Node) bool) { + for p := n.Parent; p != nil && yield(p); p = p.Parent { + } + } +} + +// ChildNodes returns an iterator over the immediate children of n, +// starting with n.FirstChild. +// +// Mutating a Node or its children while iterating may have unexpected results. +func (n *Node) ChildNodes() iter.Seq[*Node] { + _ = n.FirstChild // eager nil check + + return func(yield func(*Node) bool) { + for c := n.FirstChild; c != nil && yield(c); c = c.NextSibling { + } + } + +} + +// Descendants returns an iterator over all nodes recursively beneath +// n, excluding n itself. Nodes are visited in depth-first preorder. +// +// Mutating a Node or its descendants while iterating may have unexpected results. +func (n *Node) Descendants() iter.Seq[*Node] { + _ = n.FirstChild // eager nil check + + return func(yield func(*Node) bool) { + n.descendants(yield) + } +} + +func (n *Node) descendants(yield func(*Node) bool) bool { + for c := range n.ChildNodes() { + if !yield(c) || !c.descendants(yield) { + return false + } + } + return true +} diff --git a/html/iter_test.go b/html/iter_test.go new file mode 100644 index 0000000000..cca7f82f54 --- /dev/null +++ b/html/iter_test.go @@ -0,0 +1,96 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.23 + +package html + +import ( + "strings" + "testing" +) + +func TestNode_ChildNodes(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"", ""}, + {"", "a"}, + {"a", "a"}, + {"", "a b"}, + {"ac", "a b c"}, + {"ad", "a b d"}, + {"cefi", "a f g h"}, + } + for _, test := range tests { + doc, err := Parse(strings.NewReader(test.in)) + if err != nil { + t.Fatal(err) + } + // Drill to + n := doc.FirstChild.FirstChild.NextSibling + var results []string + for c := range n.ChildNodes() { + results = append(results, c.Data) + } + if got := strings.Join(results, " "); got != test.want { + t.Errorf("ChildNodes = %q, want %q", got, test.want) + } + } +} + +func TestNode_Descendants(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"", ""}, + {"", "a"}, + {"", "a b"}, + {"b", "a b"}, + {"", "a b"}, + {"bd", "a b c d"}, + {"be", "a b c d e"}, + {"dfgj", "a b c d e f g h i j"}, + } + for _, test := range tests { + doc, err := Parse(strings.NewReader(test.in)) + if err != nil { + t.Fatal(err) + } + // Drill to + n := doc.FirstChild.FirstChild.NextSibling + var results []string + for c := range n.Descendants() { + results = append(results, c.Data) + } + if got := strings.Join(results, " "); got != test.want { + t.Errorf("Descendants = %q; want: %q", got, test.want) + } + } +} + +func TestNode_Ancestors(t *testing.T) { + for _, size := range []int{0, 1, 2, 10, 100, 10_000} { + n := buildChain(size) + nParents := 0 + for _ = range n.Ancestors() { + nParents++ + } + if nParents != size { + t.Errorf("number of Ancestors = %d; want: %d", nParents, size) + } + } +} + +func buildChain(size int) *Node { + child := new(Node) + for range size { + parent := child + child = new(Node) + parent.AppendChild(child) + } + return child +} diff --git a/html/node.go b/html/node.go index 1350eef22c..77741a1950 100644 --- a/html/node.go +++ b/html/node.go @@ -38,6 +38,10 @@ var scopeMarker = Node{Type: scopeMarkerNode} // that it looks like "a Date: Thu, 31 Oct 2024 15:33:43 -0700 Subject: [PATCH 11/31] README: don't recommend go get These days people will just import the packages and the go tool will do the right thing. We don't need to explain it. Add a pointer to the git repo, though. For golang/go#62645 Change-Id: Ia5a16d8d66395e3feee2029ea1c3140b4d3939e7 Reviewed-on: https://go-review.googlesource.com/c/net/+/624175 Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Damien Neil Reviewed-by: Ian Lance Taylor --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a15f253dff..235c8d8b30 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,15 @@ [![Go Reference](https://pkg.go.dev/badge/golang.org/x/net.svg)](https://pkg.go.dev/golang.org/x/net) -This repository holds supplementary Go networking libraries. +This repository holds supplementary Go networking packages. -## Download/Install +## Report Issues / Send Patches -The easiest way to install is to run `go get -u golang.org/x/net`. You can -also manually git clone the repository to `$GOPATH/src/golang.org/x/net`. +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://go.dev/doc/contribute. -## Report Issues / Send Patches +The git repository is https://go.googlesource.com/net. -This repository uses Gerrit for code changes. To learn how to submit -changes to this repository, see https://golang.org/doc/contribute.html. The main issue tracker for the net repository is located at -https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the +https://go.dev/issues. Prefix your issue with "x/net:" in the subject line, so it is easy to find. From f35fec92ec9213ee211cf45f451a5970386f7978 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 2 Oct 2024 16:45:27 -0700 Subject: [PATCH 12/31] http2: detect hung client connections by confirming stream resets Consider the case of an unresponsive client connection, where the server has stopped responding. We send an infinite sequence of requests to the connection in sequence, each with a timeout. Each request counts against the concurrency limit for the connection while active, but when a request times out we send a RST_STREAM and free up the concurrency slot it was using. We continue to try to send requests to the connection forever (or until the kernel closes the underlying TCP connection, or until ReadIdleTimeout/WriteByteTimeout results in us closing the connection). Defend against this scenario by counting a canceled request against the connection concurrency limit until we confirm the server is responding. Specifically: Track the number of in-flight request cancellations in cc.pendingResets. This total counts against the connection concurrency limit. When sending a RST_STREAM for a canceled request, increment cc.pendingResets. Send a PING frame to the server, unless a PING is already in flight. When receiving a PING response, set cc.pendingResets to 0. A hung connection will be used for at most SETTINGS_MAX_CONCURRENT_STREAMS requests. When StrictMaxConcurrentStreams is false, we will create a new connection after reaching the concurrency limit for a hung one. When StrictMaxConcurrentStreams is true, we will continue to wait for the existing connection until some timeout closes it or it becomes responsive again. For golang/go#59690 Change-Id: I0151f9a594af14b32bcb6005a239fa19eb103704 Reviewed-on: https://go-review.googlesource.com/c/net/+/617655 LUCI-TryBot-Result: Go LUCI Reviewed-by: Brad Fitzpatrick Reviewed-by: Jonathan Amsterdam Reviewed-by: Carlos Amedee --- http2/http2_test.go | 8 +++ http2/transport.go | 71 +++++++++++++++++++--- http2/transport_test.go | 126 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 7 deletions(-) diff --git a/http2/http2_test.go b/http2/http2_test.go index b7c946b982..b1e71f1532 100644 --- a/http2/http2_test.go +++ b/http2/http2_test.go @@ -283,3 +283,11 @@ func TestNoUnicodeStrings(t *testing.T) { t.Fatal(err) } } + +// must returns v if err is nil, or panics otherwise. +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} diff --git a/http2/transport.go b/http2/transport.go index e989bd19e8..5d198baa50 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -364,6 +364,14 @@ type ClientConn struct { readIdleTimeout time.Duration pingTimeout time.Duration + // pendingResets is the number of RST_STREAM frames we have sent to the peer, + // without confirming that the peer has received them. When we send a RST_STREAM, + // we bundle it with a PING frame, unless a PING is already in flight. We count + // the reset stream against the connection's concurrency limit until we get + // a PING response. This limits the number of requests we'll try to send to a + // completely unresponsive connection. + pendingResets int + // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // Write to reqHeaderMu to lock it, read from it to unlock. // Lock reqmu BEFORE mu or wmu. @@ -960,7 +968,7 @@ func (cc *ClientConn) State() ClientConnState { return ClientConnState{ Closed: cc.closed, Closing: cc.closing || cc.singleUse || cc.doNotReuse || cc.goAway != nil, - StreamsActive: len(cc.streams), + StreamsActive: len(cc.streams) + cc.pendingResets, StreamsReserved: cc.streamsReserved, StreamsPending: cc.pendingRequests, LastIdle: cc.lastIdle, @@ -992,7 +1000,13 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { // writing it. maxConcurrentOkay = true } else { - maxConcurrentOkay = int64(len(cc.streams)+cc.streamsReserved+1) <= int64(cc.maxConcurrentStreams) + // We can take a new request if the total of + // - active streams; + // - reservation slots for new streams; and + // - streams for which we have sent a RST_STREAM and a PING, + // but received no subsequent frame + // is less than the concurrency limit. + maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) } st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && @@ -1002,6 +1016,12 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { return } +// currentRequestCountLocked reports the number of concurrency slots currently in use, +// including active streams, reserved slots, and reset streams waiting for acknowledgement. +func (cc *ClientConn) currentRequestCountLocked() int { + return len(cc.streams) + cc.streamsReserved + cc.pendingResets +} + func (cc *ClientConn) canTakeNewRequestLocked() bool { st := cc.idleStateLocked() return st.canTakeNewRequest @@ -1578,6 +1598,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) { cs.reqBodyClosed = make(chan struct{}) } bodyClosed := cs.reqBodyClosed + closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil cc.mu.Unlock() if mustCloseBody { cs.reqBody.Close() @@ -1602,16 +1623,40 @@ func (cs *clientStream) cleanupWriteRequest(err error) { if cs.sentHeaders { if se, ok := err.(StreamError); ok { if se.Cause != errFromPeer { - cc.writeStreamReset(cs.ID, se.Code, err) + cc.writeStreamReset(cs.ID, se.Code, false, err) } } else { - cc.writeStreamReset(cs.ID, ErrCodeCancel, err) + // We're cancelling an in-flight request. + // + // This could be due to the server becoming unresponsive. + // To avoid sending too many requests on a dead connection, + // we let the request continue to consume a concurrency slot + // until we can confirm the server is still responding. + // We do this by sending a PING frame along with the RST_STREAM + // (unless a ping is already in flight). + // + // For simplicity, we don't bother tracking the PING payload: + // We reset cc.pendingResets any time we receive a PING ACK. + // + // We skip this if the conn is going to be closed on idle, + // because it's short lived and will probably be closed before + // we get the ping response. + ping := false + if !closeOnIdle { + cc.mu.Lock() + if cc.pendingResets == 0 { + ping = true + } + cc.pendingResets++ + cc.mu.Unlock() + } + cc.writeStreamReset(cs.ID, ErrCodeCancel, ping, err) } } cs.bufPipe.CloseWithError(err) // no-op if already closed } else { if cs.sentHeaders && !cs.sentEndStream { - cc.writeStreamReset(cs.ID, ErrCodeNo, nil) + cc.writeStreamReset(cs.ID, ErrCodeNo, false, nil) } cs.bufPipe.CloseWithError(errRequestCanceled) } @@ -1638,7 +1683,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { return errClientConnUnusable } cc.lastIdle = time.Time{} - if int64(len(cc.streams)) < int64(cc.maxConcurrentStreams) { + if cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) { return nil } cc.pendingRequests++ @@ -3065,6 +3110,11 @@ func (rl *clientConnReadLoop) processPing(f *PingFrame) error { close(c) delete(cc.pings, f.Data) } + if cc.pendingResets > 0 { + // See clientStream.cleanupWriteRequest. + cc.pendingResets = 0 + cc.cond.Broadcast() + } return nil } cc := rl.cc @@ -3087,13 +3137,20 @@ func (rl *clientConnReadLoop) processPushPromise(f *PushPromiseFrame) error { return ConnectionError(ErrCodeProtocol) } -func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error) { +// writeStreamReset sends a RST_STREAM frame. +// When ping is true, it also sends a PING frame with a random payload. +func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, ping bool, err error) { // TODO: map err to more interesting error codes, once the // HTTP community comes up with some. But currently for // RST_STREAM there's no equivalent to GOAWAY frame's debug // data, and the error codes are all pretty vague ("cancel"). cc.wmu.Lock() cc.fr.WriteRSTStream(streamID, code) + if ping { + var payload [8]byte + rand.Read(payload[:]) + cc.fr.WritePing(false, payload) + } cc.bw.Flush() cc.wmu.Unlock() } diff --git a/http2/transport_test.go b/http2/transport_test.go index 757a45a7ad..f6ef295a42 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -2559,6 +2559,9 @@ func testTransportReturnsUnusedFlowControl(t *testing.T, oneDataFrame bool) { } return true }, + func(f *PingFrame) bool { + return true + }, func(f *WindowUpdateFrame) bool { if !oneDataFrame && !sentAdditionalData { t.Fatalf("Got WindowUpdateFrame, don't expect one yet") @@ -5512,3 +5515,126 @@ func TestTransport1xxLimits(t *testing.T) { }) } } + +func TestTransportSendPingWithReset(t *testing.T) { + tc := newTestClientConn(t, func(tr *Transport) { + tr.StrictMaxConcurrentStreams = true + }) + + const maxConcurrent = 3 + tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent}) + + // Start several requests. + var rts []*testRoundTrip + for i := 0; i < maxConcurrent+1; i++ { + req := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt := tc.roundTrip(req) + if i >= maxConcurrent { + tc.wantIdle() + continue + } + tc.wantFrameType(FrameHeaders) + tc.writeHeaders(HeadersFrameParam{ + StreamID: rt.streamID(), + EndHeaders: true, + BlockFragment: tc.makeHeaderBlockFragment( + ":status", "200", + ), + }) + rt.wantStatus(200) + rts = append(rts, rt) + } + + // Cancel one request. We send a PING frame along with the RST_STREAM. + rts[0].response().Body.Close() + tc.wantRSTStream(rts[0].streamID(), ErrCodeCancel) + pf := readFrame[*PingFrame](t, tc) + tc.wantIdle() + + // Cancel another request. No PING frame, since one is in flight. + rts[1].response().Body.Close() + tc.wantRSTStream(rts[1].streamID(), ErrCodeCancel) + tc.wantIdle() + + // Respond to the PING. + // This finalizes the previous resets, and allows the pending request to be sent. + tc.writePing(true, pf.Data) + tc.wantFrameType(FrameHeaders) + tc.wantIdle() + + // Cancel the last request. We send another PING, since none are in flight. + rts[2].response().Body.Close() + tc.wantRSTStream(rts[2].streamID(), ErrCodeCancel) + tc.wantFrameType(FramePing) + tc.wantIdle() +} + +func TestTransportConnBecomesUnresponsive(t *testing.T) { + // We send a number of requests in series to an unresponsive connection. + // Each request is canceled or times out without a response. + // Eventually, we open a new connection rather than trying to use the old one. + tt := newTestTransport(t) + + const maxConcurrent = 3 + + t.Logf("first request opens a new connection and succeeds") + req1 := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt1 := tt.roundTrip(req1) + tc1 := tt.getConn() + tc1.wantFrameType(FrameSettings) + tc1.wantFrameType(FrameWindowUpdate) + hf1 := readFrame[*HeadersFrame](t, tc1) + tc1.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent}) + tc1.wantFrameType(FrameSettings) // ack + tc1.writeHeaders(HeadersFrameParam{ + StreamID: hf1.StreamID, + EndHeaders: true, + EndStream: true, + BlockFragment: tc1.makeHeaderBlockFragment( + ":status", "200", + ), + }) + rt1.wantStatus(200) + rt1.response().Body.Close() + + // Send more requests. + // None receive a response. + // Each is canceled. + for i := 0; i < maxConcurrent; i++ { + t.Logf("request %v receives no response and is canceled", i) + ctx, cancel := context.WithCancel(context.Background()) + req := must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)) + tt.roundTrip(req) + if tt.hasConn() { + t.Fatalf("new connection created; expect existing conn to be reused") + } + tc1.wantFrameType(FrameHeaders) + cancel() + tc1.wantFrameType(FrameRSTStream) + if i == 0 { + tc1.wantFrameType(FramePing) + } + tc1.wantIdle() + } + + // The conn has hit its concurrency limit. + // The next request is sent on a new conn. + req2 := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt2 := tt.roundTrip(req2) + tc2 := tt.getConn() + tc2.wantFrameType(FrameSettings) + tc2.wantFrameType(FrameWindowUpdate) + hf := readFrame[*HeadersFrame](t, tc2) + tc2.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent}) + tc2.wantFrameType(FrameSettings) // ack + tc2.writeHeaders(HeadersFrameParam{ + StreamID: hf.StreamID, + EndHeaders: true, + EndStream: true, + BlockFragment: tc2.makeHeaderBlockFragment( + ":status", "200", + ), + }) + rt2.wantStatus(200) + rt2.response().Body.Close() +} From 0aa844c2c8b6054d98e91f074355d5a50528934c Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 4 Nov 2024 13:54:41 -0800 Subject: [PATCH 13/31] http2: support unencrypted HTTP/2 handoff from net/http Allow net/http to pass unencrypted net.Conns to Server/Transport. We don't have an existing way to pass a conn other than a *tls.Conn into this package, so (ab)use TLSNextProto to pass unencrypted connections: The http2 package adds an "unencrypted_http2" entry to the TLSNextProto maps. The net/http package calls this function with a *tls.Conn wrapping a net.Conn with an UnencryptedNetConn method returning the underlying, unencrypted net.Conn. For golang/go#67816 Change-Id: I31f9c1ba31a17c82c8ed651382bd94193acf09b9 Reviewed-on: https://go-review.googlesource.com/c/net/+/625175 LUCI-TryBot-Result: Go LUCI Reviewed-by: David Chase Reviewed-by: Brad Fitzpatrick --- http2/client_conn_pool.go | 8 +++---- http2/server.go | 29 +++++++++++++++++++++----- http2/transport.go | 44 ++++++++++++++++++++++++++++++++------- http2/unencrypted.go | 32 ++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 17 deletions(-) create mode 100644 http2/unencrypted.go diff --git a/http2/client_conn_pool.go b/http2/client_conn_pool.go index 780968d6c1..e81b73e6a7 100644 --- a/http2/client_conn_pool.go +++ b/http2/client_conn_pool.go @@ -8,8 +8,8 @@ package http2 import ( "context" - "crypto/tls" "errors" + "net" "net/http" "sync" ) @@ -158,7 +158,7 @@ func (c *dialCall) dial(ctx context.Context, addr string) { // This code decides which ones live or die. // The return value used is whether c was used. // c is never closed. -func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) { +func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c net.Conn) (used bool, err error) { p.mu.Lock() for _, cc := range p.conns[key] { if cc.CanTakeNewRequest() { @@ -194,8 +194,8 @@ type addConnCall struct { err error } -func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) { - cc, err := t.NewClientConn(tc) +func (c *addConnCall) run(t *Transport, key string, nc net.Conn) { + cc, err := t.NewClientConn(nc) p := c.p p.mu.Lock() diff --git a/http2/server.go b/http2/server.go index d6d8ac88b8..832414b450 100644 --- a/http2/server.go +++ b/http2/server.go @@ -306,7 +306,7 @@ func ConfigureServer(s *http.Server, conf *Server) error { if s.TLSNextProto == nil { s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} } - protoHandler := func(hs *http.Server, c *tls.Conn, h http.Handler) { + protoHandler := func(hs *http.Server, c net.Conn, h http.Handler, sawClientPreface bool) { if testHookOnConn != nil { testHookOnConn() } @@ -323,12 +323,31 @@ func ConfigureServer(s *http.Server, conf *Server) error { ctx = bc.BaseContext() } conf.ServeConn(c, &ServeConnOpts{ - Context: ctx, - Handler: h, - BaseConfig: hs, + Context: ctx, + Handler: h, + BaseConfig: hs, + SawClientPreface: sawClientPreface, }) } - s.TLSNextProto[NextProtoTLS] = protoHandler + s.TLSNextProto[NextProtoTLS] = func(hs *http.Server, c *tls.Conn, h http.Handler) { + protoHandler(hs, c, h, false) + } + // The "unencrypted_http2" TLSNextProto key is used to pass off non-TLS HTTP/2 conns. + // + // A connection passed in this method has already had the HTTP/2 preface read from it. + s.TLSNextProto[nextProtoUnencryptedHTTP2] = func(hs *http.Server, c *tls.Conn, h http.Handler) { + nc, err := unencryptedNetConnFromTLSConn(c) + if err != nil { + if lg := hs.ErrorLog; lg != nil { + lg.Print(err) + } else { + log.Print(err) + } + go c.Close() + return + } + protoHandler(hs, nc, h, true) + } return nil } diff --git a/http2/transport.go b/http2/transport.go index 5d198baa50..af0162f657 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -281,8 +281,8 @@ func configureTransports(t1 *http.Transport) (*Transport, error) { if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") { t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") } - upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper { - addr := authorityAddr("https", authority) + upgradeFn := func(scheme, authority string, c net.Conn) http.RoundTripper { + addr := authorityAddr(scheme, authority) if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil { go c.Close() return erringRoundTripper{err} @@ -293,18 +293,37 @@ func configureTransports(t1 *http.Transport) (*Transport, error) { // was unknown) go c.Close() } + if scheme == "http" { + return (*unencryptedTransport)(t2) + } return t2 } - if m := t1.TLSNextProto; len(m) == 0 { - t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{ - "h2": upgradeFn, + if t1.TLSNextProto == nil { + t1.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper) + } + t1.TLSNextProto[NextProtoTLS] = func(authority string, c *tls.Conn) http.RoundTripper { + return upgradeFn("https", authority, c) + } + // The "unencrypted_http2" TLSNextProto key is used to pass off non-TLS HTTP/2 conns. + t1.TLSNextProto[nextProtoUnencryptedHTTP2] = func(authority string, c *tls.Conn) http.RoundTripper { + nc, err := unencryptedNetConnFromTLSConn(c) + if err != nil { + go c.Close() + return erringRoundTripper{err} } - } else { - m["h2"] = upgradeFn + return upgradeFn("http", authority, nc) } return t2, nil } +// unencryptedTransport is a Transport with a RoundTrip method that +// always permits http:// URLs. +type unencryptedTransport Transport + +func (t *unencryptedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + return (*Transport)(t).RoundTripOpt(req, RoundTripOpt{allowHTTP: true}) +} + func (t *Transport) connPool() ClientConnPool { t.connPoolOnce.Do(t.initConnPool) return t.connPoolOrDef @@ -538,6 +557,8 @@ type RoundTripOpt struct { // no cached connection is available, RoundTripOpt // will return ErrNoCachedConn. OnlyCachedConn bool + + allowHTTP bool // allow http:// URLs } func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { @@ -570,7 +591,14 @@ func authorityAddr(scheme string, authority string) (addr string) { // RoundTripOpt is like RoundTrip, but takes options. func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { - if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { + switch req.URL.Scheme { + case "https": + // Always okay. + case "http": + if !t.AllowHTTP && !opt.allowHTTP { + return nil, errors.New("http2: unencrypted HTTP/2 not enabled") + } + default: return nil, errors.New("http2: unsupported scheme") } diff --git a/http2/unencrypted.go b/http2/unencrypted.go new file mode 100644 index 0000000000..b2de211613 --- /dev/null +++ b/http2/unencrypted.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http2 + +import ( + "crypto/tls" + "errors" + "net" +) + +const nextProtoUnencryptedHTTP2 = "unencrypted_http2" + +// unencryptedNetConnFromTLSConn retrieves a net.Conn wrapped in a *tls.Conn. +// +// TLSNextProto functions accept a *tls.Conn. +// +// When passing an unencrypted HTTP/2 connection to a TLSNextProto function, +// we pass a *tls.Conn with an underlying net.Conn containing the unencrypted connection. +// To be extra careful about mistakes (accidentally dropping TLS encryption in a place +// where we want it), the tls.Conn contains a net.Conn with an UnencryptedNetConn method +// that returns the actual connection we want to use. +func unencryptedNetConnFromTLSConn(tc *tls.Conn) (net.Conn, error) { + conner, ok := tc.NetConn().(interface { + UnencryptedNetConn() net.Conn + }) + if !ok { + return nil, errors.New("http2: TLS conn unexpectedly found in unencrypted handoff") + } + return conner.UnencryptedNetConn(), nil +} From 858db1a8c8f71cbefb8ff2c896ff9aa86d761a47 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 5 Nov 2024 11:26:18 -0800 Subject: [PATCH 14/31] http2: surface errors occurring very early in a client conn's lifetime When we create a new connection for a request, the request should fail if the connection attempt fails. There is a race condition which can cause this to not happen: - net/http sends a request to a http2.Transport - the http2.Transport returns ErrNoCachedConn - net/http creates a new tls.Conn and passes it to the http2.Transport - the http2.Transport adds the conn to its connection pool - the connection immediately encounters an error - the http2.Transport removes the conn from its connection pool - net/http resends the request to the http2.Transport - the http2.Transport returns ErrNoCachedConn, and the process repeates If the request is sent to the http2.Transport before the connection encounters an error, then the request fails. But otherwise, we get stuck in an infinite loop of the http2.Transport asking for a new connection, receiving one, and throwing it away. To fix this, leave a dead connection in the pool for a short while if it has never had a request sent to it. If a dead connection is used for a new request, return an error and remove the connection from the pool. Change-Id: I64eb15a8f1512a6bda52db423072b945fab6f4b5 Reviewed-on: https://go-review.googlesource.com/c/net/+/625398 Reviewed-by: Brad Fitzpatrick LUCI-TryBot-Result: Go LUCI Reviewed-by: Jonathan Amsterdam --- http2/clientconn_test.go | 30 ++++++++--- http2/netconn_test.go | 10 +++- http2/transport.go | 86 +++++++++++++++++++++++++---- http2/transport_test.go | 113 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 20 deletions(-) diff --git a/http2/clientconn_test.go b/http2/clientconn_test.go index 92d6305143..42d9fd2dcc 100644 --- a/http2/clientconn_test.go +++ b/http2/clientconn_test.go @@ -10,6 +10,7 @@ package http2 import ( "bytes" "context" + "crypto/tls" "fmt" "io" "net/http" @@ -112,27 +113,40 @@ func newTestClientConnFromClientConn(t *testing.T, cc *ClientConn) *testClientCo cc: cc, group: cc.t.transportTestHooks.group.(*synctestGroup), } - cli, srv := synctestNetPipe(tc.group) + + // srv is the side controlled by the test. + var srv *synctestNetConn + if cc.tconn == nil { + // If cc.tconn is nil, we're being called with a new conn created by the + // Transport's client pool. This path skips dialing the server, and we + // create a test connection pair here. + cc.tconn, srv = synctestNetPipe(tc.group) + } else { + // If cc.tconn is non-nil, we're in a test which provides a conn to the + // Transport via a TLSNextProto hook. Extract the test connection pair. + if tc, ok := cc.tconn.(*tls.Conn); ok { + // Unwrap any *tls.Conn to the underlying net.Conn, + // to avoid dealing with encryption in tests. + cc.tconn = tc.NetConn() + } + srv = cc.tconn.(*synctestNetConn).peer + } + srv.SetReadDeadline(tc.group.Now()) srv.autoWait = true tc.netconn = srv tc.enc = hpack.NewEncoder(&tc.encbuf) - - // all writes and reads are finished. - // - // cli is the ClientConn's side, srv is the side controlled by the test. - cc.tconn = cli tc.fr = NewFramer(srv, srv) tc.testConnFramer = testConnFramer{ t: t, fr: tc.fr, dec: hpack.NewDecoder(initialHeaderTableSize, nil), } - tc.fr.SetMaxReadFrameSize(10 << 20) t.Cleanup(func() { tc.closeWrite() }) + return tc } @@ -503,6 +517,8 @@ func newTestTransport(t *testing.T, opts ...any) *testTransport { o(tr.t1) case func(*Transport): o(tr) + case *Transport: + tr = o } } tt.tr = tr diff --git a/http2/netconn_test.go b/http2/netconn_test.go index 8a61fbef10..0f1b5fb1f3 100644 --- a/http2/netconn_test.go +++ b/http2/netconn_test.go @@ -28,8 +28,11 @@ func synctestNetPipe(group *synctestGroup) (r, w *synctestNetConn) { s2addr := net.TCPAddrFromAddrPort(netip.MustParseAddrPort("127.0.0.1:8001")) s1 := newSynctestNetConnHalf(s1addr) s2 := newSynctestNetConnHalf(s2addr) - return &synctestNetConn{group: group, loc: s1, rem: s2}, - &synctestNetConn{group: group, loc: s2, rem: s1} + r = &synctestNetConn{group: group, loc: s1, rem: s2} + w = &synctestNetConn{group: group, loc: s2, rem: s1} + r.peer = w + w.peer = r + return r, w } // A synctestNetConn is one endpoint of the connection created by synctestNetPipe. @@ -43,6 +46,9 @@ type synctestNetConn struct { // When set, group.Wait is automatically called before reads and after writes. autoWait bool + + // peer is the other endpoint. + peer *synctestNetConn } // Read reads data from the connection. diff --git a/http2/transport.go b/http2/transport.go index af0162f657..f5968f4407 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -202,6 +202,20 @@ func (t *Transport) markNewGoroutine() { } } +func (t *Transport) now() time.Time { + if t != nil && t.transportTestHooks != nil { + return t.transportTestHooks.group.Now() + } + return time.Now() +} + +func (t *Transport) timeSince(when time.Time) time.Duration { + if t != nil && t.transportTestHooks != nil { + return t.now().Sub(when) + } + return time.Since(when) +} + // newTimer creates a new time.Timer, or a synthetic timer in tests. func (t *Transport) newTimer(d time.Duration) timer { if t.transportTestHooks != nil { @@ -343,7 +357,7 @@ type ClientConn struct { t *Transport tconn net.Conn // usually *tls.Conn, except specialized impls tlsState *tls.ConnectionState // nil only for specialized impls - reused uint32 // whether conn is being reused; atomic + atomicReused uint32 // whether conn is being reused; atomic singleUse bool // whether being used for a single http.Request getConnCalled bool // used by clientConnPool @@ -609,7 +623,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) return nil, err } - reused := !atomic.CompareAndSwapUint32(&cc.reused, 0, 1) + reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1) traceGotConn(req, cc, reused) res, err := cc.RoundTrip(req) if err != nil && retry <= 6 { @@ -634,6 +648,22 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res } } } + if err == errClientConnNotEstablished { + // This ClientConn was created recently, + // this is the first request to use it, + // and the connection is closed and not usable. + // + // In this state, cc.idleTimer will remove the conn from the pool + // when it fires. Stop the timer and remove it here so future requests + // won't try to use this connection. + // + // If the timer has already fired and we're racing it, the redundant + // call to MarkDead is harmless. + if cc.idleTimer != nil { + cc.idleTimer.Stop() + } + t.connPool().MarkDead(cc) + } if err != nil { t.vlogf("RoundTrip failure: %v", err) return nil, err @@ -652,9 +682,10 @@ func (t *Transport) CloseIdleConnections() { } var ( - errClientConnClosed = errors.New("http2: client conn is closed") - errClientConnUnusable = errors.New("http2: client conn not usable") - errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") + errClientConnClosed = errors.New("http2: client conn is closed") + errClientConnUnusable = errors.New("http2: client conn not usable") + errClientConnNotEstablished = errors.New("http2: client conn could not be established") + errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") ) // shouldRetryRequest is called by RoundTrip when a request fails to get @@ -793,6 +824,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro pingTimeout: conf.PingTimeout, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), + lastActive: t.now(), } var group synctestGroupInterface if t.transportTestHooks != nil { @@ -1041,6 +1073,16 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { !cc.doNotReuse && int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && !cc.tooIdleLocked() + + // If this connection has never been used for a request and is closed, + // then let it take a request (which will fail). + // + // This avoids a situation where an error early in a connection's lifetime + // goes unreported. + if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed { + st.canTakeNewRequest = true + } + return } @@ -1062,7 +1104,7 @@ func (cc *ClientConn) tooIdleLocked() bool { // times are compared based on their wall time. We don't want // to reuse a connection that's been sitting idle during // VM/laptop suspend if monotonic time was also frozen. - return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout + return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && cc.t.timeSince(cc.lastIdle.Round(0)) > cc.idleTimeout } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -1706,7 +1748,12 @@ func (cs *clientStream) cleanupWriteRequest(err error) { // Must hold cc.mu. func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { for { - cc.lastActive = time.Now() + if cc.closed && cc.nextStreamID == 1 && cc.streamsReserved == 0 { + // This is the very first request sent to this connection. + // Return a fatal error which aborts the retry loop. + return errClientConnNotEstablished + } + cc.lastActive = cc.t.now() if cc.closed || !cc.canTakeNewRequestLocked() { return errClientConnUnusable } @@ -2253,10 +2300,10 @@ func (cc *ClientConn) forgetStreamID(id uint32) { if len(cc.streams) != slen-1 { panic("forgetting unknown stream id") } - cc.lastActive = time.Now() + cc.lastActive = cc.t.now() if len(cc.streams) == 0 && cc.idleTimer != nil { cc.idleTimer.Reset(cc.idleTimeout) - cc.lastIdle = time.Now() + cc.lastIdle = cc.t.now() } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. @@ -2316,7 +2363,6 @@ func isEOFOrNetReadError(err error) bool { func (rl *clientConnReadLoop) cleanup() { cc := rl.cc - cc.t.connPool().MarkDead(cc) defer cc.closeConn() defer close(cc.readerDone) @@ -2340,6 +2386,24 @@ func (rl *clientConnReadLoop) cleanup() { } cc.closed = true + // If the connection has never been used, and has been open for only a short time, + // leave it in the connection pool for a little while. + // + // This avoids a situation where new connections are constantly created, + // added to the pool, fail, and are removed from the pool, without any error + // being surfaced to the user. + const unusedWaitTime = 5 * time.Second + idleTime := cc.t.now().Sub(cc.lastActive) + if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime { + cc.idleTimer = cc.t.afterFunc(unusedWaitTime-idleTime, func() { + cc.t.connPool().MarkDead(cc) + }) + } else { + cc.mu.Unlock() // avoid any deadlocks in MarkDead + cc.t.connPool().MarkDead(cc) + cc.mu.Lock() + } + for _, cs := range cc.streams { select { case <-cs.peerClosed: @@ -3332,7 +3396,7 @@ func traceGotConn(req *http.Request, cc *ClientConn, reused bool) { cc.mu.Lock() ci.WasIdle = len(cc.streams) == 0 && reused if ci.WasIdle && !cc.lastActive.IsZero() { - ci.IdleTime = time.Since(cc.lastActive) + ci.IdleTime = cc.t.timeSince(cc.lastActive) } cc.mu.Unlock() diff --git a/http2/transport_test.go b/http2/transport_test.go index f6ef295a42..100da8bd1c 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -5638,3 +5638,116 @@ func TestTransportConnBecomesUnresponsive(t *testing.T) { rt2.wantStatus(200) rt2.response().Body.Close() } + +// Test that the Transport can use a conn provided to it by a TLSNextProto hook. +func TestTransportTLSNextProtoConnOK(t *testing.T) { + t1 := &http.Transport{} + t2, _ := ConfigureTransports(t1) + tt := newTestTransport(t, t2) + + // Create a new, fake connection and pass it to the Transport via the TLSNextProto hook. + cli, _ := synctestNetPipe(tt.group) + cliTLS := tls.Client(cli, tlsConfigInsecure) + go func() { + tt.group.Join() + t1.TLSNextProto["h2"]("dummy.tld", cliTLS) + }() + tt.sync() + tc := tt.getConn() + tc.greet() + + // Send a request on the Transport. + // It uses the conn we provided. + req := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt := tt.roundTrip(req) + tc.wantHeaders(wantHeader{ + streamID: 1, + endStream: true, + header: http.Header{ + ":authority": []string{"dummy.tld"}, + ":method": []string{"GET"}, + ":path": []string{"/"}, + }, + }) + tc.writeHeaders(HeadersFrameParam{ + StreamID: 1, + EndHeaders: true, + EndStream: true, + BlockFragment: tc.makeHeaderBlockFragment( + ":status", "200", + ), + }) + rt.wantStatus(200) + rt.wantBody(nil) +} + +// Test the case where a conn provided via a TLSNextProto hook immediately encounters an error. +func TestTransportTLSNextProtoConnImmediateFailureUsed(t *testing.T) { + t1 := &http.Transport{} + t2, _ := ConfigureTransports(t1) + tt := newTestTransport(t, t2) + + // Create a new, fake connection and pass it to the Transport via the TLSNextProto hook. + cli, _ := synctestNetPipe(tt.group) + cliTLS := tls.Client(cli, tlsConfigInsecure) + go func() { + tt.group.Join() + t1.TLSNextProto["h2"]("dummy.tld", cliTLS) + }() + tt.sync() + tc := tt.getConn() + + // The connection encounters an error before we send a request that uses it. + tc.closeWrite() + + // Send a request on the Transport. + // + // It should fail, because we have no usable connections, but not with ErrNoCachedConn. + req := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt := tt.roundTrip(req) + if err := rt.err(); err == nil || errors.Is(err, ErrNoCachedConn) { + t.Fatalf("RoundTrip with broken conn: got %v, want an error other than ErrNoCachedConn", err) + } + + // Send the request again. + // This time it should fail with ErrNoCachedConn, + // because the dead conn has been removed from the pool. + rt = tt.roundTrip(req) + if err := rt.err(); !errors.Is(err, ErrNoCachedConn) { + t.Fatalf("RoundTrip after broken conn is used: got %v, want ErrNoCachedConn", err) + } +} + +// Test the case where a conn provided via a TLSNextProto hook immediately encounters an error, +// but no requests are sent which would use the bad connection. +func TestTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) { + t1 := &http.Transport{} + t2, _ := ConfigureTransports(t1) + tt := newTestTransport(t, t2) + + // Create a new, fake connection and pass it to the Transport via the TLSNextProto hook. + cli, _ := synctestNetPipe(tt.group) + cliTLS := tls.Client(cli, tlsConfigInsecure) + go func() { + tt.group.Join() + t1.TLSNextProto["h2"]("dummy.tld", cliTLS) + }() + tt.sync() + tc := tt.getConn() + + // The connection encounters an error before we send a request that uses it. + tc.closeWrite() + + // Some time passes. + // The dead connection is removed from the pool. + tc.advance(10 * time.Second) + + // Send a request on the Transport. + // + // It should fail with ErrNoCachedConn, because the pool contains no conns. + req := must(http.NewRequest("GET", "https://dummy.tld/", nil)) + rt := tt.roundTrip(req) + if err := rt.err(); !errors.Is(err, ErrNoCachedConn) { + t.Fatalf("RoundTrip after broken conn expires: got %v, want ErrNoCachedConn", err) + } +} From d7f220d3b8964f859b642e7d7c1a0d0973000939 Mon Sep 17 00:00:00 2001 From: jfgiorgi Date: Fri, 1 Nov 2024 23:54:42 +0000 Subject: [PATCH 15/31] quic: add LocalAddr and RemoteAddr to quic.Conn These are missing for quic.Conn. Fixes golang/go#70138 Change-Id: Ia443ffe0e73e143be5c29233a1ceb7cb16951acd GitHub-Last-Rev: a326378fddecb4136100d345993230a970ba8b22 GitHub-Pull-Request: golang/net#225 Reviewed-on: https://go-review.googlesource.com/c/net/+/623157 Reviewed-by: Damien Neil Reviewed-by: David Chase Auto-Submit: Damien Neil LUCI-TryBot-Result: Go LUCI --- quic/conn.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/quic/conn.go b/quic/conn.go index 38e8fe8f4e..fbd8b84348 100644 --- a/quic/conn.go +++ b/quic/conn.go @@ -176,6 +176,16 @@ func (c *Conn) String() string { return fmt.Sprintf("quic.Conn(%v,->%v)", c.side, c.peerAddr) } +// LocalAddr returns the local network address, if known. +func (c *Conn) LocalAddr() netip.AddrPort { + return c.localAddr +} + +// RemoteAddr returns the remote network address, if known. +func (c *Conn) RemoteAddr() netip.AddrPort { + return c.peerAddr +} + // confirmHandshake is called when the handshake is confirmed. // https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2 func (c *Conn) confirmHandshake(now time.Time) { From 334afa0d53434157eb708b09ff35a42db2c4531a Mon Sep 17 00:00:00 2001 From: Gopher Robot Date: Thu, 7 Nov 2024 23:20:51 +0000 Subject: [PATCH 16/31] go.mod: update golang.org/x dependencies Update golang.org/x dependencies to their latest tagged versions. Change-Id: Ic3d15c610f766d40730157ea878be90dd9e9c084 Reviewed-on: https://go-review.googlesource.com/c/net/+/626378 Reviewed-by: Dmitri Shuralyov Reviewed-by: David Chase Auto-Submit: Gopher Robot LUCI-TryBot-Result: Go LUCI --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 77935d3f2b..2721bac68d 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module golang.org/x/net go 1.18 require ( - golang.org/x/crypto v0.28.0 - golang.org/x/sys v0.26.0 - golang.org/x/term v0.25.0 - golang.org/x/text v0.19.0 + golang.org/x/crypto v0.29.0 + golang.org/x/sys v0.27.0 + golang.org/x/term v0.26.0 + golang.org/x/text v0.20.0 ) diff --git a/go.sum b/go.sum index 1b8c61d600..6868ecce4c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= From 9a518991035b3a8f23c03004d4db041a7fa6c06f Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Fri, 22 Nov 2024 00:25:03 +0000 Subject: [PATCH 17/31] http2: add SETTINGS_ENABLE_CONNECT_PROTOCOL support For golang/go#49918 Change-Id: Ibcd8fb189200c0976cf1bd03a796abae4afa4cfd GitHub-Last-Rev: cba5ecd7b7ecf842317301c425ef5107c54bc2ae GitHub-Pull-Request: golang/net#221 Reviewed-on: https://go-review.googlesource.com/c/net/+/610977 Reviewed-by: Damien Neil Auto-Submit: Damien Neil LUCI-TryBot-Result: Go LUCI Reviewed-by: Roland Shoemaker --- http2/frame.go | 4 +- http2/http2.go | 42 +++++++++++-------- http2/server.go | 35 ++++++++++++---- http2/transport.go | 91 +++++++++++++++++++++++++++++++---------- http2/transport_test.go | 62 ++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+), 49 deletions(-) diff --git a/http2/frame.go b/http2/frame.go index 105c3b279c..81faec7e75 100644 --- a/http2/frame.go +++ b/http2/frame.go @@ -1490,7 +1490,7 @@ func (mh *MetaHeadersFrame) checkPseudos() error { pf := mh.PseudoFields() for i, hf := range pf { switch hf.Name { - case ":method", ":path", ":scheme", ":authority": + case ":method", ":path", ":scheme", ":authority", ":protocol": isRequest = true case ":status": isResponse = true @@ -1498,7 +1498,7 @@ func (mh *MetaHeadersFrame) checkPseudos() error { return pseudoHeaderError(hf.Name) } // Check for duplicates. - // This would be a bad algorithm, but N is 4. + // This would be a bad algorithm, but N is 5. // And this doesn't allocate. for _, hf2 := range pf[:i] { if hf.Name == hf2.Name { diff --git a/http2/http2.go b/http2/http2.go index 7688c356b7..c7601c909f 100644 --- a/http2/http2.go +++ b/http2/http2.go @@ -34,10 +34,11 @@ import ( ) var ( - VerboseLogs bool - logFrameWrites bool - logFrameReads bool - inTests bool + VerboseLogs bool + logFrameWrites bool + logFrameReads bool + inTests bool + disableExtendedConnectProtocol bool ) func init() { @@ -50,6 +51,9 @@ func init() { logFrameWrites = true logFrameReads = true } + if strings.Contains(e, "http2xconnect=0") { + disableExtendedConnectProtocol = true + } } const ( @@ -141,6 +145,10 @@ func (s Setting) Valid() error { if s.Val < 16384 || s.Val > 1<<24-1 { return ConnectionError(ErrCodeProtocol) } + case SettingEnableConnectProtocol: + if s.Val != 1 && s.Val != 0 { + return ConnectionError(ErrCodeProtocol) + } } return nil } @@ -150,21 +158,23 @@ func (s Setting) Valid() error { type SettingID uint16 const ( - SettingHeaderTableSize SettingID = 0x1 - SettingEnablePush SettingID = 0x2 - SettingMaxConcurrentStreams SettingID = 0x3 - SettingInitialWindowSize SettingID = 0x4 - SettingMaxFrameSize SettingID = 0x5 - SettingMaxHeaderListSize SettingID = 0x6 + SettingHeaderTableSize SettingID = 0x1 + SettingEnablePush SettingID = 0x2 + SettingMaxConcurrentStreams SettingID = 0x3 + SettingInitialWindowSize SettingID = 0x4 + SettingMaxFrameSize SettingID = 0x5 + SettingMaxHeaderListSize SettingID = 0x6 + SettingEnableConnectProtocol SettingID = 0x8 ) var settingName = map[SettingID]string{ - SettingHeaderTableSize: "HEADER_TABLE_SIZE", - SettingEnablePush: "ENABLE_PUSH", - SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", - SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", - SettingMaxFrameSize: "MAX_FRAME_SIZE", - SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", + SettingHeaderTableSize: "HEADER_TABLE_SIZE", + SettingEnablePush: "ENABLE_PUSH", + SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", + SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", + SettingMaxFrameSize: "MAX_FRAME_SIZE", + SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", + SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL", } func (s SettingID) String() string { diff --git a/http2/server.go b/http2/server.go index 832414b450..b55547aec6 100644 --- a/http2/server.go +++ b/http2/server.go @@ -932,14 +932,18 @@ func (sc *serverConn) serve(conf http2Config) { sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs) } + settings := writeSettings{ + {SettingMaxFrameSize, conf.MaxReadFrameSize}, + {SettingMaxConcurrentStreams, sc.advMaxStreams}, + {SettingMaxHeaderListSize, sc.maxHeaderListSize()}, + {SettingHeaderTableSize, conf.MaxDecoderHeaderTableSize}, + {SettingInitialWindowSize, uint32(sc.initialStreamRecvWindowSize)}, + } + if !disableExtendedConnectProtocol { + settings = append(settings, Setting{SettingEnableConnectProtocol, 1}) + } sc.writeFrame(FrameWriteRequest{ - write: writeSettings{ - {SettingMaxFrameSize, conf.MaxReadFrameSize}, - {SettingMaxConcurrentStreams, sc.advMaxStreams}, - {SettingMaxHeaderListSize, sc.maxHeaderListSize()}, - {SettingHeaderTableSize, conf.MaxDecoderHeaderTableSize}, - {SettingInitialWindowSize, uint32(sc.initialStreamRecvWindowSize)}, - }, + write: settings, }) sc.unackedSettings++ @@ -1801,6 +1805,9 @@ func (sc *serverConn) processSetting(s Setting) error { sc.maxFrameSize = int32(s.Val) // the maximum valid s.Val is < 2^31 case SettingMaxHeaderListSize: sc.peerMaxHeaderListSize = s.Val + case SettingEnableConnectProtocol: + // Receipt of this parameter by a server does not + // have any impact default: // Unknown setting: "An endpoint that receives a SETTINGS // frame with any unknown or unsupported identifier MUST @@ -2231,11 +2238,17 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res scheme: f.PseudoValue("scheme"), authority: f.PseudoValue("authority"), path: f.PseudoValue("path"), + protocol: f.PseudoValue("protocol"), + } + + // extended connect is disabled, so we should not see :protocol + if disableExtendedConnectProtocol && rp.protocol != "" { + return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } isConnect := rp.method == "CONNECT" if isConnect { - if rp.path != "" || rp.scheme != "" || rp.authority == "" { + if rp.protocol == "" && (rp.path != "" || rp.scheme != "" || rp.authority == "") { return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } } else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") { @@ -2259,6 +2272,9 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res if rp.authority == "" { rp.authority = rp.header.Get("Host") } + if rp.protocol != "" { + rp.header.Set(":protocol", rp.protocol) + } rw, req, err := sc.newWriterAndRequestNoBody(st, rp) if err != nil { @@ -2285,6 +2301,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res type requestParam struct { method string scheme, authority, path string + protocol string header http.Header } @@ -2326,7 +2343,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r var url_ *url.URL var requestURI string - if rp.method == "CONNECT" { + if rp.method == "CONNECT" && rp.protocol == "" { url_ = &url.URL{Host: rp.authority} requestURI = rp.authority // mimic HTTP/1 server behavior } else { diff --git a/http2/transport.go b/http2/transport.go index f5968f4407..7fcfa89e05 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -368,25 +368,26 @@ type ClientConn struct { idleTimeout time.Duration // or 0 for never idleTimer timer - mu sync.Mutex // guards following - cond *sync.Cond // hold mu; broadcast on flow/closed changes - flow outflow // our conn-level flow control quota (cs.outflow is per stream) - inflow inflow // peer's conn-level flow control - doNotReuse bool // whether conn is marked to not be reused for any future requests - closing bool - closed bool - seenSettings bool // true if we've seen a settings frame, false otherwise - wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back - goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received - goAwayDebug string // goAway frame's debug data, retained as a string - streams map[uint32]*clientStream // client-initiated - streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip - nextStreamID uint32 - pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams - pings map[[8]byte]chan struct{} // in flight ping data to notification channel - br *bufio.Reader - lastActive time.Time - lastIdle time.Time // time last idle + mu sync.Mutex // guards following + cond *sync.Cond // hold mu; broadcast on flow/closed changes + flow outflow // our conn-level flow control quota (cs.outflow is per stream) + inflow inflow // peer's conn-level flow control + doNotReuse bool // whether conn is marked to not be reused for any future requests + closing bool + closed bool + seenSettings bool // true if we've seen a settings frame, false otherwise + seenSettingsChan chan struct{} // closed when seenSettings is true or frame reading fails + wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back + goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received + goAwayDebug string // goAway frame's debug data, retained as a string + streams map[uint32]*clientStream // client-initiated + streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip + nextStreamID uint32 + pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams + pings map[[8]byte]chan struct{} // in flight ping data to notification channel + br *bufio.Reader + lastActive time.Time + lastIdle time.Time // time last idle // Settings from peer: (also guarded by wmu) maxFrameSize uint32 maxConcurrentStreams uint32 @@ -396,6 +397,7 @@ type ClientConn struct { initialStreamRecvWindowSize int32 readIdleTimeout time.Duration pingTimeout time.Duration + extendedConnectAllowed bool // pendingResets is the number of RST_STREAM frames we have sent to the peer, // without confirming that the peer has received them. When we send a RST_STREAM, @@ -819,6 +821,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. streams: make(map[uint32]*clientStream), singleUse: singleUse, + seenSettingsChan: make(chan struct{}), wantSettingsAck: true, readIdleTimeout: conf.SendPingTimeout, pingTimeout: conf.PingTimeout, @@ -1466,6 +1469,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream) cs.cleanupWriteRequest(err) } +var errExtendedConnectNotSupported = errors.New("net/http: extended connect not supported by peer") + // writeRequest sends a request. // // It returns nil after the request is written, the response read, @@ -1481,12 +1486,31 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre return err } + // wait for setting frames to be received, a server can change this value later, + // but we just wait for the first settings frame + var isExtendedConnect bool + if req.Method == "CONNECT" && req.Header.Get(":protocol") != "" { + isExtendedConnect = true + } + // Acquire the new-request lock by writing to reqHeaderMu. // This lock guards the critical section covering allocating a new stream ID // (requires mu) and creating the stream (requires wmu). if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } + if isExtendedConnect { + select { + case <-cs.reqCancel: + return errRequestCanceled + case <-ctx.Done(): + return ctx.Err() + case <-cc.seenSettingsChan: + if !cc.extendedConnectAllowed { + return errExtendedConnectNotSupported + } + } + } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: @@ -2030,7 +2054,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) func validateHeaders(hdrs http.Header) string { for k, vv := range hdrs { - if !httpguts.ValidHeaderFieldName(k) { + if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" { return fmt.Sprintf("name %q", k) } for _, v := range vv { @@ -2046,6 +2070,10 @@ func validateHeaders(hdrs http.Header) string { var errNilRequestURL = errors.New("http2: Request.URI is nil") +func isNormalConnect(req *http.Request) bool { + return req.Method == "CONNECT" && req.Header.Get(":protocol") == "" +} + // requires cc.wmu be held. func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { cc.hbuf.Reset() @@ -2066,7 +2094,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail } var path string - if req.Method != "CONNECT" { + if !isNormalConnect(req) { path = req.URL.RequestURI() if !validPseudoPath(path) { orig := path @@ -2103,7 +2131,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail m = http.MethodGet } f(":method", m) - if req.Method != "CONNECT" { + if !isNormalConnect(req) { f(":path", path) f(":scheme", req.URL.Scheme) } @@ -2507,6 +2535,9 @@ func (rl *clientConnReadLoop) run() error { if VerboseLogs { cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, summarizeFrame(f), err) } + if !cc.seenSettings { + close(cc.seenSettingsChan) + } return err } } @@ -3073,6 +3104,21 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { case SettingHeaderTableSize: cc.henc.SetMaxDynamicTableSize(s.Val) cc.peerMaxHeaderTableSize = s.Val + case SettingEnableConnectProtocol: + if err := s.Valid(); err != nil { + return err + } + // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL, + // we require that it do so in the first SETTINGS frame. + // + // When we attempt to use extended CONNECT, we wait for the first + // SETTINGS frame to see if the server supports it. If we let the + // server enable the feature with a later SETTINGS frame, then + // users will see inconsistent results depending on whether we've + // seen that frame or not. + if !cc.seenSettings { + cc.extendedConnectAllowed = s.Val == 1 + } default: cc.vlogf("Unhandled Setting: %v", s) } @@ -3090,6 +3136,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { // connection can establish to our default. cc.maxConcurrentStreams = defaultMaxConcurrentStreams } + close(cc.seenSettingsChan) cc.seenSettings = true } diff --git a/http2/transport_test.go b/http2/transport_test.go index 100da8bd1c..746f6e3eee 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -5751,3 +5751,65 @@ func TestTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) { t.Fatalf("RoundTrip after broken conn expires: got %v, want ErrNoCachedConn", err) } } + +func TestExtendedConnectClientWithServerSupport(t *testing.T) { + disableExtendedConnectProtocol = false + ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get(":protocol") != "extended-connect" { + t.Fatalf("unexpected :protocol header received") + } + t.Log(io.Copy(w, r.Body)) + }) + tr := &Transport{ + TLSClientConfig: tlsConfigInsecure, + AllowHTTP: true, + } + defer tr.CloseIdleConnections() + pr, pw := io.Pipe() + pwDone := make(chan struct{}) + req, _ := http.NewRequest("CONNECT", ts.URL, pr) + req.Header.Set(":protocol", "extended-connect") + go func() { + pw.Write([]byte("hello, extended connect")) + pw.Close() + close(pwDone) + }() + + res, err := tr.RoundTrip(req) + if err != nil { + t.Fatal(err) + } + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(body, []byte("hello, extended connect")) { + t.Fatal("unexpected body received") + } +} + +func TestExtendedConnectClientWithoutServerSupport(t *testing.T) { + disableExtendedConnectProtocol = true + ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { + io.Copy(w, r.Body) + }) + tr := &Transport{ + TLSClientConfig: tlsConfigInsecure, + AllowHTTP: true, + } + defer tr.CloseIdleConnections() + pr, pw := io.Pipe() + pwDone := make(chan struct{}) + req, _ := http.NewRequest("CONNECT", ts.URL, pr) + req.Header.Set(":protocol", "extended-connect") + go func() { + pw.Write([]byte("hello, extended connect")) + pw.Close() + close(pwDone) + }() + + _, err := tr.RoundTrip(req) + if !errors.Is(err, errExtendedConnectNotSupported) { + t.Fatalf("expected error errExtendedConnectNotSupported, got: %v", err) + } +} From e9cd716925e5c6cfb200c82f9d9bf6bc4118d47a Mon Sep 17 00:00:00 2001 From: James Tucker Date: Mon, 25 Nov 2024 22:52:41 +0000 Subject: [PATCH 18/31] route: fix parse of zero-length sockaddrs in RIBs Zero-length sockaddrs were observed in RIBs within golang/go#70528. These records are to be skipped, and an invariant for later slice manipulation is to be enforced by a defensive check in parseAddr. Fixes golang/go#70528 Change-Id: I4b8b5bd2339bbadc1d1be1ce14deeb6dd3b8e536 GitHub-Last-Rev: 066ba8a559e8ff543a445246279ad808cc67660f GitHub-Pull-Request: golang/net#228 Reviewed-on: https://go-review.googlesource.com/c/net/+/631475 Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Reviewed-by: Damien Neil --- route/address.go | 18 ++++++----- route/address_darwin_test.go | 58 ++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/route/address.go b/route/address.go index bae63003f0..b649f43141 100644 --- a/route/address.go +++ b/route/address.go @@ -176,7 +176,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { ) switch af { case syscall.AF_INET: - if len(b) < (off4+1) || len(b) < int(b[0]) { + if len(b) < (off4+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } sockAddrLen := int(b[0]) @@ -188,7 +188,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { copy(a.IP[:], b[off4:n]) return a, nil case syscall.AF_INET6: - if len(b) < (off6+1) || len(b) < int(b[0]) { + if len(b) < (off6+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } sockAddrLen := int(b[0]) @@ -404,12 +404,16 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ( } b = b[l:] case syscall.AF_INET, syscall.AF_INET6: - af = int(b[1]) - a, err := parseInetAddr(af, b) - if err != nil { - return nil, err + // #70528: if the sockaddrlen is 0, no address to parse inside, + // skip over the record. + if b[0] > 0 { + af = int(b[1]) + a, err := parseInetAddr(af, b) + if err != nil { + return nil, err + } + as[i] = a } - as[i] = a l := roundup(int(b[0])) if len(b) < l { return nil, errMessageTooShort diff --git a/route/address_darwin_test.go b/route/address_darwin_test.go index 0b5c72d1dc..add72e37ec 100644 --- a/route/address_darwin_test.go +++ b/route/address_darwin_test.go @@ -86,9 +86,61 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, }, []Addr{ - &Inet6Addr{IP: [16]byte{ 0xfd, 0x84, 0x1b, 0x4e, 0x62, 0x81 }}, - &Inet6Addr{IP: [16]byte{ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff }, ZoneID: 33}, - &Inet6Addr{IP: [16]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,}}, + &Inet6Addr{IP: [16]byte{0xfd, 0x84, 0x1b, 0x4e, 0x62, 0x81}}, + &Inet6Addr{IP: [16]byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff}, ZoneID: 33}, + &Inet6Addr{IP: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + nil, + nil, + nil, + nil, + nil, + }, + }, + // golang/go#70528, the kernel can produce addresses of length 0 + { + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + parseKernelInetAddr, + []byte{ + 0x00, 0x1e, 0x00, 0x00, + + 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x80, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff, + 0x00, 0x00, 0x00, 0x00, + + 0x0e, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + }, + []Addr{ + nil, + &Inet6Addr{IP: [16]byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x2f, 0x4b, 0xff, 0xfe, 0x09, 0x3b, 0xff}, ZoneID: 33}, + &Inet6Addr{IP: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + nil, + nil, + nil, + nil, + nil, + }, + }, + // Additional case: golang/go/issues/70528#issuecomment-2498692877 + { + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + parseKernelInetAddr, + []byte{ + 0x84, 0x00, 0x05, 0x04, 0x01, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x01, 0x15, 0x00, 0x00, 0x00, + 0x1B, 0x01, 0x00, 0x00, 0xF5, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }, + []Addr{ + &Inet4Addr{IP: [4]byte{0x0, 0x0, 0x0, 0x0}}, + nil, + nil, nil, nil, nil, From bc37675919d77638c33240a80610699db46fb739 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 2 Dec 2024 10:30:06 -0800 Subject: [PATCH 19/31] http2: limit number of PINGs bundled with RST_STREAMs gRPC has an unfortunate behavior of stictly rate limiting the number of PING frames that it will receive. The default is two PING frames every two hours when no requests are in flight; two PING frames every five minutes when a request is in flight; and the limit resets every time the gRPC endpoint sends a HEADERS or DATA frame. When sending a RST_STREAM frame, the Transport can bundle a PING frame with it to confirm the server is responding. When canceling several requests in succession, this can result in hitting the gRPC ping limit. Work around this gRPC behavior by sending at most one bundled PING per HEADERS or DATA frame received. We already limit ourselves to one PING in flight at a time; now, when we receive a PING response, disable sending additional bundled PINGs until we read a HEADERS/DATA frame. This does not affect keep-alive pings. Fixes golang/go#70575. Change-Id: I7c4003039bd2dc52106b2806ca31eeeee37b7e09 Reviewed-on: https://go-review.googlesource.com/c/net/+/632995 Reviewed-by: Jonathan Amsterdam Auto-Submit: Damien Neil LUCI-TryBot-Result: Go LUCI --- http2/transport.go | 46 ++++++++++++++++++++------ http2/transport_test.go | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/http2/transport.go b/http2/transport.go index 7fcfa89e05..090d0e1bdb 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -399,6 +399,16 @@ type ClientConn struct { pingTimeout time.Duration extendedConnectAllowed bool + // rstStreamPingsBlocked works around an unfortunate gRPC behavior. + // gRPC strictly limits the number of PING frames that it will receive. + // The default is two pings per two hours, but the limit resets every time + // the gRPC endpoint sends a HEADERS or DATA frame. See golang/go#70575. + // + // rstStreamPingsBlocked is set after receiving a response to a PING frame + // bundled with an RST_STREAM (see pendingResets below), and cleared after + // receiving a HEADERS or DATA frame. + rstStreamPingsBlocked bool + // pendingResets is the number of RST_STREAM frames we have sent to the peer, // without confirming that the peer has received them. When we send a RST_STREAM, // we bundle it with a PING frame, unless a PING is already in flight. We count @@ -1738,10 +1748,14 @@ func (cs *clientStream) cleanupWriteRequest(err error) { ping := false if !closeOnIdle { cc.mu.Lock() - if cc.pendingResets == 0 { - ping = true + // rstStreamPingsBlocked works around a gRPC behavior: + // see comment on the field for details. + if !cc.rstStreamPingsBlocked { + if cc.pendingResets == 0 { + ping = true + } + cc.pendingResets++ } - cc.pendingResets++ cc.mu.Unlock() } cc.writeStreamReset(cs.ID, ErrCodeCancel, ping, err) @@ -2489,7 +2503,7 @@ func (rl *clientConnReadLoop) run() error { cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(StreamError); ok { - if cs := rl.streamByID(se.StreamID); cs != nil { + if cs := rl.streamByID(se.StreamID, notHeaderOrDataFrame); cs != nil { if se.Cause == nil { se.Cause = cc.fr.errDetail } @@ -2544,7 +2558,7 @@ func (rl *clientConnReadLoop) run() error { } func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, headerOrDataFrame) if cs == nil { // We'd get here if we canceled a request while the // server had its response still in flight. So if this @@ -2873,7 +2887,7 @@ func (b transportResponseBody) Close() error { func (rl *clientConnReadLoop) processData(f *DataFrame) error { cc := rl.cc - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, headerOrDataFrame) data := f.Data() if cs == nil { cc.mu.Lock() @@ -3008,9 +3022,22 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) { cs.abortStream(err) } -func (rl *clientConnReadLoop) streamByID(id uint32) *clientStream { +// Constants passed to streamByID for documentation purposes. +const ( + headerOrDataFrame = true + notHeaderOrDataFrame = false +) + +// streamByID returns the stream with the given id, or nil if no stream has that id. +// If headerOrData is true, it clears rst.StreamPingsBlocked. +func (rl *clientConnReadLoop) streamByID(id uint32, headerOrData bool) *clientStream { rl.cc.mu.Lock() defer rl.cc.mu.Unlock() + if headerOrData { + // Work around an unfortunate gRPC behavior. + // See comment on ClientConn.rstStreamPingsBlocked for details. + rl.cc.rstStreamPingsBlocked = false + } cs := rl.cc.streams[id] if cs != nil && !cs.readAborted { return cs @@ -3145,7 +3172,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { cc := rl.cc - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if f.StreamID != 0 && cs == nil { return nil } @@ -3174,7 +3201,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { } func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error { - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if cs == nil { // TODO: return error if server tries to RST_STREAM an idle stream return nil @@ -3252,6 +3279,7 @@ func (rl *clientConnReadLoop) processPing(f *PingFrame) error { if cc.pendingResets > 0 { // See clientStream.cleanupWriteRequest. cc.pendingResets = 0 + cc.rstStreamPingsBlocked = true cc.cond.Broadcast() } return nil diff --git a/http2/transport_test.go b/http2/transport_test.go index 746f6e3eee..0e12e0f1c7 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -5562,6 +5562,10 @@ func TestTransportSendPingWithReset(t *testing.T) { tc.wantFrameType(FrameHeaders) tc.wantIdle() + // Receive a byte of data for the remaining stream, which resets our ability + // to send pings (see comment on ClientConn.rstStreamPingsBlocked). + tc.writeData(rts[2].streamID(), false, []byte{0}) + // Cancel the last request. We send another PING, since none are in flight. rts[2].response().Body.Close() tc.wantRSTStream(rts[2].streamID(), ErrCodeCancel) @@ -5569,6 +5573,73 @@ func TestTransportSendPingWithReset(t *testing.T) { tc.wantIdle() } +// Issue #70505: gRPC gets upset if we send more than 2 pings per HEADERS/DATA frame +// sent by the server. +func TestTransportSendNoMoreThanOnePingWithReset(t *testing.T) { + tc := newTestClientConn(t) + tc.greet() + + makeAndResetRequest := func() { + t.Helper() + ctx, cancel := context.WithCancel(context.Background()) + req := must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)) + rt := tc.roundTrip(req) + tc.wantFrameType(FrameHeaders) + cancel() + tc.wantRSTStream(rt.streamID(), ErrCodeCancel) // client sends RST_STREAM + } + + // Create a request and cancel it. + // The client sends a PING frame along with the reset. + makeAndResetRequest() + pf1 := readFrame[*PingFrame](t, tc) // client sends PING + + // Create another request and cancel it. + // We do not send a PING frame along with the reset, + // because we haven't received a HEADERS or DATA frame from the server + // since the last PING we sent. + makeAndResetRequest() + + // Server belatedly responds to request 1. + // The server has not responded to our first PING yet. + tc.writeHeaders(HeadersFrameParam{ + StreamID: 1, + EndHeaders: true, + EndStream: true, + BlockFragment: tc.makeHeaderBlockFragment( + ":status", "200", + ), + }) + + // Create yet another request and cancel it. + // We still do not send a PING frame along with the reset. + // We've received a HEADERS frame, but it came before the response to the PING. + makeAndResetRequest() + + // The server responds to our PING. + tc.writePing(true, pf1.Data) + + // Create yet another request and cancel it. + // Still no PING frame; we got a response to the previous one, + // but no HEADERS or DATA. + makeAndResetRequest() + + // Server belatedly responds to the second request. + tc.writeHeaders(HeadersFrameParam{ + StreamID: 3, + EndHeaders: true, + EndStream: true, + BlockFragment: tc.makeHeaderBlockFragment( + ":status", "200", + ), + }) + + // One more request. + // This time we send a PING frame. + makeAndResetRequest() + tc.wantFrameType(FramePing) +} + func TestTransportConnBecomesUnresponsive(t *testing.T) { // We send a number of requests in series to an unresponsive connection. // Each request is canceled or times out without a response. From 4be12533d8e2957bafe945c501689eba78cbf0f6 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 2 Dec 2024 10:25:46 -0800 Subject: [PATCH 20/31] route: change from syscall to x/sys/unix This lets us drop some of the defs files and cgo usage. Change-Id: I5a00e77610da36c752d28ea07e40b8a9c7c59ae4 Reviewed-on: https://go-review.googlesource.com/c/net/+/632816 Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Damien Neil LUCI-TryBot-Result: Go LUCI --- route/address.go | 45 +++++++++++----------- route/address_darwin_test.go | 13 ++++--- route/address_test.go | 7 ++-- route/defs_darwin.go | 15 +------- route/defs_dragonfly.go | 56 --------------------------- route/defs_freebsd.go | 12 ------ route/defs_netbsd.go | 32 ---------------- route/defs_openbsd.go | 27 ------------- route/interface_classic.go | 9 +++-- route/interface_freebsd.go | 12 +++--- route/interface_openbsd.go | 8 ++-- route/message_darwin_test.go | 7 ++-- route/message_freebsd_test.go | 15 ++++---- route/message_test.go | 71 ++++++++++++++++++----------------- route/route.go | 11 +++--- route/route_classic.go | 5 ++- route/route_openbsd.go | 14 +++---- route/route_test.go | 25 ++++++------ route/sys.go | 5 ++- route/sys_darwin.go | 48 +++++++++++------------ route/sys_dragonfly.go | 53 ++++++++++++++------------ route/sys_freebsd.go | 51 ++++++++++++------------- route/sys_netbsd.go | 36 +++++++++--------- route/sys_openbsd.go | 31 +++++++-------- route/zsys_darwin.go | 15 +------- route/zsys_dragonfly.go | 20 ---------- route/zsys_freebsd_386.go | 12 ------ route/zsys_freebsd_amd64.go | 12 ------ route/zsys_freebsd_arm.go | 12 ------ route/zsys_freebsd_arm64.go | 12 ------ route/zsys_freebsd_riscv64.go | 12 ------ route/zsys_netbsd.go | 17 --------- route/zsys_openbsd.go | 12 ------ 33 files changed, 243 insertions(+), 489 deletions(-) delete mode 100644 route/defs_dragonfly.go delete mode 100644 route/defs_netbsd.go delete mode 100644 route/defs_openbsd.go delete mode 100644 route/zsys_dragonfly.go delete mode 100644 route/zsys_netbsd.go delete mode 100644 route/zsys_openbsd.go diff --git a/route/address.go b/route/address.go index b649f43141..6f72c6cc35 100644 --- a/route/address.go +++ b/route/address.go @@ -8,7 +8,8 @@ package route import ( "runtime" - "syscall" + + "golang.org/x/sys/unix" ) // An Addr represents an address associated with packet routing. @@ -25,7 +26,7 @@ type LinkAddr struct { } // Family implements the Family method of Addr interface. -func (a *LinkAddr) Family() int { return syscall.AF_LINK } +func (a *LinkAddr) Family() int { return unix.AF_LINK } func (a *LinkAddr) lenAndSpace() (int, int) { l := 8 + len(a.Name) + len(a.Addr) @@ -42,7 +43,7 @@ func (a *LinkAddr) marshal(b []byte) (int, error) { return 0, errInvalidAddr } b[0] = byte(l) - b[1] = syscall.AF_LINK + b[1] = unix.AF_LINK if a.Index > 0 { nativeEndian.PutUint16(b[2:4], uint16(a.Index)) } @@ -64,7 +65,7 @@ func parseLinkAddr(b []byte) (Addr, error) { if len(b) < 8 { return nil, errInvalidAddr } - _, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:]) + _, a, err := parseKernelLinkAddr(unix.AF_LINK, b[4:]) if err != nil { return nil, err } @@ -124,10 +125,10 @@ type Inet4Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet4Addr) Family() int { return syscall.AF_INET } +func (a *Inet4Addr) Family() int { return unix.AF_INET } func (a *Inet4Addr) lenAndSpace() (int, int) { - return sizeofSockaddrInet, roundup(sizeofSockaddrInet) + return unix.SizeofSockaddrInet4, roundup(unix.SizeofSockaddrInet4) } func (a *Inet4Addr) marshal(b []byte) (int, error) { @@ -136,7 +137,7 @@ func (a *Inet4Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = syscall.AF_INET + b[1] = unix.AF_INET copy(b[4:8], a.IP[:]) return ll, nil } @@ -148,10 +149,10 @@ type Inet6Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet6Addr) Family() int { return syscall.AF_INET6 } +func (a *Inet6Addr) Family() int { return unix.AF_INET6 } func (a *Inet6Addr) lenAndSpace() (int, int) { - return sizeofSockaddrInet6, roundup(sizeofSockaddrInet6) + return unix.SizeofSockaddrInet6, roundup(unix.SizeofSockaddrInet6) } func (a *Inet6Addr) marshal(b []byte) (int, error) { @@ -160,7 +161,7 @@ func (a *Inet6Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = syscall.AF_INET6 + b[1] = unix.AF_INET6 copy(b[8:24], a.IP[:]) if a.ZoneID > 0 { nativeEndian.PutUint32(b[24:28], uint32(a.ZoneID)) @@ -175,7 +176,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { off6 = 8 // offset of in6_addr ) switch af { - case syscall.AF_INET: + case unix.AF_INET: if len(b) < (off4+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } @@ -187,7 +188,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { } copy(a.IP[:], b[off4:n]) return a, nil - case syscall.AF_INET6: + case unix.AF_INET6: if len(b) < (off6+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } @@ -197,7 +198,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { n = sockAddrLen } a := &Inet6Addr{} - if sockAddrLen == sizeofSockaddrInet6 { + if sockAddrLen == unix.SizeofSockaddrInet6 { a.ZoneID = int(nativeEndian.Uint32(b[24:28])) } copy(a.IP[:], b[off6:n]) @@ -260,11 +261,11 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { off6 = 8 // offset of in6_addr ) switch { - case b[0] == sizeofSockaddrInet6: + case b[0] == unix.SizeofSockaddrInet6: a := &Inet6Addr{} copy(a.IP[:], b[off6:off6+16]) return int(b[0]), a, nil - case af == syscall.AF_INET6: + case af == unix.AF_INET6: a := &Inet6Addr{} if l-1 < off6 { copy(a.IP[:], b[1:l]) @@ -272,7 +273,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { copy(a.IP[:], b[l-off6:l]) } return int(b[0]), a, nil - case b[0] == sizeofSockaddrInet: + case b[0] == unix.SizeofSockaddrInet4: a := &Inet4Addr{} copy(a.IP[:], b[off4:off4+4]) return int(b[0]), a, nil @@ -384,15 +385,15 @@ func marshalAddrs(b []byte, as []Addr) (uint, error) { } func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) { - var as [syscall.RTAX_MAX]Addr - af := int(syscall.AF_UNSPEC) - for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ { + var as [unix.RTAX_MAX]Addr + af := int(unix.AF_UNSPEC) + for i := uint(0); i < unix.RTAX_MAX && len(b) >= roundup(0); i++ { if attrs&(1< 0 { diff --git a/route/address_darwin_test.go b/route/address_darwin_test.go index add72e37ec..08eb8b7d1d 100644 --- a/route/address_darwin_test.go +++ b/route/address_darwin_test.go @@ -6,8 +6,9 @@ package route import ( "reflect" - "syscall" "testing" + + "golang.org/x/sys/unix" ) type parseAddrsOnDarwinTest struct { @@ -19,7 +20,7 @@ type parseAddrsOnDarwinTest struct { var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x10, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0x56, 0x0, @@ -43,7 +44,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, }, { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x10, 0x02, 0x00, 0x00, 0x64, 0x71, 0x00, 0x00, @@ -69,7 +70,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ // route -n add -inet6 fd84:1b4e:6281:: -prefixlen 48 fe80::f22f:4bff:fe09:3bff%utun4319 // gw fe80:0000:0000:0000:f22f:4bff:fe09:3bff { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -98,7 +99,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, // golang/go#70528, the kernel can produce addresses of length 0 { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x00, 0x1e, 0x00, 0x00, @@ -124,7 +125,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, // Additional case: golang/go/issues/70528#issuecomment-2498692877 { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x84, 0x00, 0x05, 0x04, 0x01, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x01, 0x15, 0x00, 0x00, 0x00, diff --git a/route/address_test.go b/route/address_test.go index 31087576ed..cd0f3ab499 100644 --- a/route/address_test.go +++ b/route/address_test.go @@ -8,8 +8,9 @@ package route import ( "reflect" - "syscall" "testing" + + "golang.org/x/sys/unix" ) type parseAddrsTest struct { @@ -21,7 +22,7 @@ type parseAddrsTest struct { var parseAddrsLittleEndianTests = []parseAddrsTest{ { - syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK | syscall.RTA_BRD, + unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK | unix.RTA_BRD, parseKernelInetAddr, []byte{ 0x38, 0x12, 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, @@ -58,7 +59,7 @@ var parseAddrsLittleEndianTests = []parseAddrsTest{ }, }, { - syscall.RTA_NETMASK | syscall.RTA_IFP | syscall.RTA_IFA, + unix.RTA_NETMASK | unix.RTA_IFP | unix.RTA_IFA, parseKernelInetAddr, []byte{ 0x7, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, diff --git a/route/defs_darwin.go b/route/defs_darwin.go index ec56ca02e1..0b95479c2e 100644 --- a/route/defs_darwin.go +++ b/route/defs_darwin.go @@ -19,19 +19,8 @@ package route import "C" const ( - sizeofIfMsghdrDarwin15 = C.sizeof_struct_if_msghdr - sizeofIfaMsghdrDarwin15 = C.sizeof_struct_ifa_msghdr - sizeofIfmaMsghdrDarwin15 = C.sizeof_struct_ifma_msghdr - sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 - sizeofIfmaMsghdr2Darwin15 = C.sizeof_struct_ifma_msghdr2 - sizeofIfDataDarwin15 = C.sizeof_struct_if_data - sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 + sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 + sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 - sizeofRtMsghdrDarwin15 = C.sizeof_struct_rt_msghdr sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2 - sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_dragonfly.go b/route/defs_dragonfly.go deleted file mode 100644 index 9bf202dda4..0000000000 --- a/route/defs_dragonfly.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ignore - -package route - -/* -#include -#include - -#include -#include -#include - -#include - -struct ifa_msghdr_dfly4 { - u_short ifam_msglen; - u_char ifam_version; - u_char ifam_type; - int ifam_addrs; - int ifam_flags; - u_short ifam_index; - int ifam_metric; -}; - -struct ifa_msghdr_dfly58 { - u_short ifam_msglen; - u_char ifam_version; - u_char ifam_type; - u_short ifam_index; - int ifam_flags; - int ifam_addrs; - int ifam_addrflags; - int ifam_metric; -}; -*/ -import "C" - -const ( - sizeofIfMsghdrDragonFlyBSD4 = C.sizeof_struct_if_msghdr - sizeofIfaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifa_msghdr_dfly4 - sizeofIfmaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifma_msghdr - sizeofIfAnnouncemsghdrDragonFlyBSD4 = C.sizeof_struct_if_announcemsghdr - - sizeofIfaMsghdrDragonFlyBSD58 = C.sizeof_struct_ifa_msghdr_dfly58 - - sizeofRtMsghdrDragonFlyBSD4 = C.sizeof_struct_rt_msghdr - sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 -) diff --git a/route/defs_freebsd.go b/route/defs_freebsd.go index abb2dc0957..a1318949a3 100644 --- a/route/defs_freebsd.go +++ b/route/defs_freebsd.go @@ -218,12 +218,6 @@ struct if_msghdr_freebsd11 { import "C" const ( - sizeofIfMsghdrlFreeBSD10 = C.sizeof_struct_if_msghdrl - sizeofIfaMsghdrFreeBSD10 = C.sizeof_struct_ifa_msghdr - sizeofIfaMsghdrlFreeBSD10 = C.sizeof_struct_ifa_msghdrl - sizeofIfmaMsghdrFreeBSD10 = C.sizeof_struct_ifma_msghdr - sizeofIfAnnouncemsghdrFreeBSD10 = C.sizeof_struct_if_announcemsghdr - sizeofRtMsghdrFreeBSD10 = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10 = C.sizeof_struct_rt_metrics @@ -239,12 +233,6 @@ const ( sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 - sizeofIfMsghdrlFreeBSD10Emu = C.sizeof_struct_if_msghdrl - sizeofIfaMsghdrFreeBSD10Emu = C.sizeof_struct_ifa_msghdr - sizeofIfaMsghdrlFreeBSD10Emu = C.sizeof_struct_ifa_msghdrl - sizeofIfmaMsghdrFreeBSD10Emu = C.sizeof_struct_ifma_msghdr - sizeofIfAnnouncemsghdrFreeBSD10Emu = C.sizeof_struct_if_announcemsghdr - sizeofRtMsghdrFreeBSD10Emu = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10Emu = C.sizeof_struct_rt_metrics diff --git a/route/defs_netbsd.go b/route/defs_netbsd.go deleted file mode 100644 index 8e89934c5a..0000000000 --- a/route/defs_netbsd.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ignore - -package route - -/* -#include -#include - -#include -#include -#include - -#include -*/ -import "C" - -const ( - sizeofIfMsghdrNetBSD7 = C.sizeof_struct_if_msghdr - sizeofIfaMsghdrNetBSD7 = C.sizeof_struct_ifa_msghdr - sizeofIfAnnouncemsghdrNetBSD7 = C.sizeof_struct_if_announcemsghdr - - sizeofRtMsghdrNetBSD7 = C.sizeof_struct_rt_msghdr - sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 -) diff --git a/route/defs_openbsd.go b/route/defs_openbsd.go deleted file mode 100644 index 8f3218bc63..0000000000 --- a/route/defs_openbsd.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ignore - -package route - -/* -#include -#include - -#include -#include -#include - -#include -*/ -import "C" - -const ( - sizeofRtMsghdr = C.sizeof_struct_rt_msghdr - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 -) diff --git a/route/interface_classic.go b/route/interface_classic.go index be1bf2652e..e1dc4eba58 100644 --- a/route/interface_classic.go +++ b/route/interface_classic.go @@ -8,7 +8,8 @@ package route import ( "runtime" - "syscall" + + "golang.org/x/sys/unix" ) func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { @@ -20,13 +21,13 @@ func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[4:8])) - if attrs&syscall.RTA_IFP == 0 { + if attrs&unix.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ Version: int(b[2]), Type: int(b[3]), - Addrs: make([]Addr, syscall.RTAX_MAX), + Addrs: make([]Addr, unix.RTAX_MAX), Flags: int(nativeEndian.Uint32(b[8:12])), Index: int(nativeEndian.Uint16(b[12:14])), extOff: w.extOff, @@ -36,7 +37,7 @@ func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) if err != nil { return nil, err } - m.Addrs[syscall.RTAX_IFP] = a + m.Addrs[unix.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } diff --git a/route/interface_freebsd.go b/route/interface_freebsd.go index 14d251c94f..7e90b17d33 100644 --- a/route/interface_freebsd.go +++ b/route/interface_freebsd.go @@ -4,11 +4,11 @@ package route -import "syscall" +import "golang.org/x/sys/unix" func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, error) { var extOff, bodyOff int - if typ == syscall.NET_RT_IFLISTL { + if typ == unix.NET_RT_IFLISTL { if len(b) < 20 { return nil, errMessageTooShort } @@ -26,7 +26,7 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[4:8])) - if attrs&syscall.RTA_IFP == 0 { + if attrs&unix.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ @@ -34,7 +34,7 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro Type: int(b[3]), Flags: int(nativeEndian.Uint32(b[8:12])), Index: int(nativeEndian.Uint16(b[12:14])), - Addrs: make([]Addr, syscall.RTAX_MAX), + Addrs: make([]Addr, unix.RTAX_MAX), extOff: extOff, raw: b[:l], } @@ -42,14 +42,14 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro if err != nil { return nil, err } - m.Addrs[syscall.RTAX_IFP] = a + m.Addrs[unix.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } func (w *wireFormat) parseInterfaceAddrMessage(typ RIBType, b []byte) (Message, error) { var bodyOff int - if typ == syscall.NET_RT_IFLISTL { + if typ == unix.NET_RT_IFLISTL { if len(b) < 24 { return nil, errMessageTooShort } diff --git a/route/interface_openbsd.go b/route/interface_openbsd.go index d369409a72..fe003e39de 100644 --- a/route/interface_openbsd.go +++ b/route/interface_openbsd.go @@ -4,7 +4,7 @@ package route -import "syscall" +import "golang.org/x/sys/unix" func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { if len(b) < 32 { @@ -15,7 +15,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[12:16])) - if attrs&syscall.RTA_IFP == 0 { + if attrs&unix.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ @@ -23,7 +23,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { Type: int(b[3]), Flags: int(nativeEndian.Uint32(b[16:20])), Index: int(nativeEndian.Uint16(b[6:8])), - Addrs: make([]Addr, syscall.RTAX_MAX), + Addrs: make([]Addr, unix.RTAX_MAX), raw: b[:l], } ll := int(nativeEndian.Uint16(b[4:6])) @@ -34,7 +34,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { if err != nil { return nil, err } - m.Addrs[syscall.RTAX_IFP] = a + m.Addrs[unix.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } diff --git a/route/message_darwin_test.go b/route/message_darwin_test.go index 7d6a3c75e8..debe2e6b42 100644 --- a/route/message_darwin_test.go +++ b/route/message_darwin_test.go @@ -5,15 +5,16 @@ package route import ( - "syscall" "testing" + + "golang.org/x/sys/unix" ) func TestFetchAndParseRIBOnDarwin(t *testing.T) { - for _, typ := range []RIBType{syscall.NET_RT_FLAGS, syscall.NET_RT_DUMP2, syscall.NET_RT_IFLIST2} { + for _, typ := range []RIBType{unix.NET_RT_FLAGS, unix.NET_RT_DUMP2, unix.NET_RT_IFLIST2} { var lastErr error var ms []Message - for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { + for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err diff --git a/route/message_freebsd_test.go b/route/message_freebsd_test.go index 62677c1e30..9f899c6991 100644 --- a/route/message_freebsd_test.go +++ b/route/message_freebsd_test.go @@ -5,15 +5,16 @@ package route import ( - "syscall" "testing" + + "golang.org/x/sys/unix" ) func TestFetchAndParseRIBOnFreeBSD(t *testing.T) { - for _, typ := range []RIBType{syscall.NET_RT_IFMALIST} { + for _, typ := range []RIBType{unix.NET_RT_IFMALIST} { var lastErr error var ms []Message - for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { + for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err @@ -37,7 +38,7 @@ func TestFetchAndParseRIBOnFreeBSD(t *testing.T) { } func TestFetchAndParseRIBOnFreeBSD10AndAbove(t *testing.T) { - if _, err := FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLISTL, 0); err != nil { + if _, err := FetchRIB(unix.AF_UNSPEC, unix.NET_RT_IFLISTL, 0); err != nil { t.Skip("NET_RT_IFLISTL not supported") } if compatFreeBSD32 { @@ -50,12 +51,12 @@ func TestFetchAndParseRIBOnFreeBSD10AndAbove(t *testing.T) { msgs []Message ss []string }{ - {typ: syscall.NET_RT_IFLIST}, - {typ: syscall.NET_RT_IFLISTL}, + {typ: unix.NET_RT_IFLIST}, + {typ: unix.NET_RT_IFLISTL}, } for i := range tests { var lastErr error - for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { + for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { rs, err := fetchAndParseRIB(af, tests[i].typ) if err != nil { lastErr = err diff --git a/route/message_test.go b/route/message_test.go index 9381f1b2df..74e8c0ade7 100644 --- a/route/message_test.go +++ b/route/message_test.go @@ -8,16 +8,17 @@ package route import ( "os" - "syscall" "testing" "time" + + "golang.org/x/sys/unix" ) func TestFetchAndParseRIB(t *testing.T) { - for _, typ := range []RIBType{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} { + for _, typ := range []RIBType{unix.NET_RT_DUMP, unix.NET_RT_IFLIST} { var lastErr error var ms []Message - for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { + for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err @@ -48,7 +49,7 @@ var ( func init() { // We need to keep rtmonSock alive to avoid treading on // recycled socket descriptors. - rtmonSock, rtmonErr = syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) + rtmonSock, rtmonErr = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) } // TestMonitorAndParseRIB leaks a worker goroutine and a socket @@ -84,7 +85,7 @@ func TestMonitorAndParseRIB(t *testing.T) { // use the net package of standard library due // to the lack of support for routing socket // and circular dependency. - n, err := syscall.Read(rtmonSock, b) + n, err := unix.Read(rtmonSock, b) if err != nil { return } @@ -144,60 +145,60 @@ func TestParseRIBWithFuzz(t *testing.T) { } func TestRouteMessage(t *testing.T) { - s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) + s, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) if err != nil { t.Fatal(err) } - defer syscall.Close(s) + defer unix.Close(s) var ms []RouteMessage - for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { - if _, err := fetchAndParseRIB(af, syscall.NET_RT_DUMP); err != nil { + for _, af := range []int{unix.AF_INET, unix.AF_INET6} { + if _, err := fetchAndParseRIB(af, unix.NET_RT_DUMP); err != nil { t.Log(err) continue } switch af { - case syscall.AF_INET: + case unix.AF_INET: ms = append(ms, []RouteMessage{ { - Type: syscall.RTM_GET, + Type: unix.RTM_GET, Addrs: []Addr{ - syscall.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, - syscall.RTAX_GATEWAY: nil, - syscall.RTAX_NETMASK: nil, - syscall.RTAX_GENMASK: nil, - syscall.RTAX_IFP: &LinkAddr{}, - syscall.RTAX_IFA: &Inet4Addr{}, - syscall.RTAX_AUTHOR: nil, - syscall.RTAX_BRD: &Inet4Addr{}, + unix.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, + unix.RTAX_GATEWAY: nil, + unix.RTAX_NETMASK: nil, + unix.RTAX_GENMASK: nil, + unix.RTAX_IFP: &LinkAddr{}, + unix.RTAX_IFA: &Inet4Addr{}, + unix.RTAX_AUTHOR: nil, + unix.RTAX_BRD: &Inet4Addr{}, }, }, { - Type: syscall.RTM_GET, + Type: unix.RTM_GET, Addrs: []Addr{ - syscall.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, + unix.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, }, }, }...) - case syscall.AF_INET6: + case unix.AF_INET6: ms = append(ms, []RouteMessage{ { - Type: syscall.RTM_GET, + Type: unix.RTM_GET, Addrs: []Addr{ - syscall.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, - syscall.RTAX_GATEWAY: nil, - syscall.RTAX_NETMASK: nil, - syscall.RTAX_GENMASK: nil, - syscall.RTAX_IFP: &LinkAddr{}, - syscall.RTAX_IFA: &Inet6Addr{}, - syscall.RTAX_AUTHOR: nil, - syscall.RTAX_BRD: &Inet6Addr{}, + unix.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + unix.RTAX_GATEWAY: nil, + unix.RTAX_NETMASK: nil, + unix.RTAX_GENMASK: nil, + unix.RTAX_IFP: &LinkAddr{}, + unix.RTAX_IFA: &Inet6Addr{}, + unix.RTAX_AUTHOR: nil, + unix.RTAX_BRD: &Inet6Addr{}, }, }, { - Type: syscall.RTM_GET, + Type: unix.RTM_GET, Addrs: []Addr{ - syscall.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + unix.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, }, }, }...) @@ -210,11 +211,11 @@ func TestRouteMessage(t *testing.T) { if err != nil { t.Fatalf("%v: %v", m, err) } - if _, err := syscall.Write(s, wb); err != nil { + if _, err := unix.Write(s, wb); err != nil { t.Fatalf("%v: %v", m, err) } rb := make([]byte, os.Getpagesize()) - n, err := syscall.Read(s, rb) + n, err := unix.Read(s, rb) if err != nil { t.Fatalf("%v: %v", m, err) } diff --git a/route/route.go b/route/route.go index ca2ce2b887..0a20f507cd 100644 --- a/route/route.go +++ b/route/route.go @@ -15,7 +15,8 @@ package route import ( "errors" "os" - "syscall" + + "golang.org/x/sys/unix" ) var ( @@ -92,8 +93,8 @@ func (m *RouteMessage) Marshal() ([]byte, error) { type RIBType int const ( - RIBTypeRoute RIBType = syscall.NET_RT_DUMP - RIBTypeInterface RIBType = syscall.NET_RT_IFLIST + RIBTypeRoute RIBType = unix.NET_RT_DUMP + RIBTypeInterface RIBType = unix.NET_RT_IFLIST ) // FetchRIB fetches a routing information base from the operating @@ -110,7 +111,7 @@ func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { try := 0 for { try++ - mib := [6]int32{syscall.CTL_NET, syscall.AF_ROUTE, 0, int32(af), int32(typ), int32(arg)} + mib := [6]int32{unix.CTL_NET, unix.AF_ROUTE, 0, int32(af), int32(typ), int32(arg)} n := uintptr(0) if err := sysctl(mib[:], nil, &n, nil, 0); err != nil { return nil, os.NewSyscallError("sysctl", err) @@ -124,7 +125,7 @@ func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { // between the two sysctl calls, try a few times // before failing. (golang.org/issue/45736). const maxTries = 3 - if err == syscall.ENOMEM && try < maxTries { + if err == unix.ENOMEM && try < maxTries { continue } return nil, os.NewSyscallError("sysctl", err) diff --git a/route/route_classic.go b/route/route_classic.go index e273fe39ab..6501378609 100644 --- a/route/route_classic.go +++ b/route/route_classic.go @@ -8,7 +8,8 @@ package route import ( "runtime" - "syscall" + + "golang.org/x/sys/unix" ) func (m *RouteMessage) marshal() ([]byte, error) { @@ -62,7 +63,7 @@ func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) { extOff: w.extOff, raw: b[:l], } - errno := syscall.Errno(nativeEndian.Uint32(b[28:32])) + errno := unix.Errno(nativeEndian.Uint32(b[28:32])) if errno != 0 { m.Err = errno } diff --git a/route/route_openbsd.go b/route/route_openbsd.go index f848fb1f24..c592bd8eee 100644 --- a/route/route_openbsd.go +++ b/route/route_openbsd.go @@ -5,25 +5,25 @@ package route import ( - "syscall" + "golang.org/x/sys/unix" ) func (m *RouteMessage) marshal() ([]byte, error) { - l := sizeofRtMsghdr + addrsSpace(m.Addrs) + l := unix.SizeofRtMsghdr + addrsSpace(m.Addrs) b := make([]byte, l) nativeEndian.PutUint16(b[:2], uint16(l)) if m.Version == 0 { - b[2] = syscall.RTM_VERSION + b[2] = unix.RTM_VERSION } else { b[2] = byte(m.Version) } b[3] = byte(m.Type) - nativeEndian.PutUint16(b[4:6], uint16(sizeofRtMsghdr)) + nativeEndian.PutUint16(b[4:6], uint16(unix.SizeofRtMsghdr)) nativeEndian.PutUint32(b[16:20], uint32(m.Flags)) nativeEndian.PutUint16(b[6:8], uint16(m.Index)) nativeEndian.PutUint32(b[24:28], uint32(m.ID)) nativeEndian.PutUint32(b[28:32], uint32(m.Seq)) - attrs, err := marshalAddrs(b[sizeofRtMsghdr:], m.Addrs) + attrs, err := marshalAddrs(b[unix.SizeofRtMsghdr:], m.Addrs) if err != nil { return nil, err } @@ -34,7 +34,7 @@ func (m *RouteMessage) marshal() ([]byte, error) { } func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { - if len(b) < sizeofRtMsghdr { + if len(b) < unix.SizeofRtMsghdr { return nil, errMessageTooShort } l := int(nativeEndian.Uint16(b[:2])) @@ -54,7 +54,7 @@ func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { if len(b) < ll { return nil, errInvalidMessage } - errno := syscall.Errno(nativeEndian.Uint32(b[32:36])) + errno := unix.Errno(nativeEndian.Uint32(b[32:36])) if errno != 0 { m.Err = errno } diff --git a/route/route_test.go b/route/route_test.go index ba57702178..9f00a498cd 100644 --- a/route/route_test.go +++ b/route/route_test.go @@ -10,7 +10,8 @@ import ( "fmt" "os/exec" "runtime" - "syscall" + + "golang.org/x/sys/unix" ) func (m *RouteMessage) String() string { @@ -176,13 +177,13 @@ type addrFamily int func (af addrFamily) String() string { switch af { - case syscall.AF_UNSPEC: + case unix.AF_UNSPEC: return "unspec" - case syscall.AF_LINK: + case unix.AF_LINK: return "link" - case syscall.AF_INET: + case unix.AF_INET: return "inet4" - case syscall.AF_INET6: + case unix.AF_INET6: return "inet6" default: return fmt.Sprintf("%d", af) @@ -281,24 +282,24 @@ func (as addrs) String() string { func (as addrs) match(attrs addrAttrs) error { var ts addrAttrs - af := syscall.AF_UNSPEC + af := unix.AF_UNSPEC for i := range as { if as[i] != nil { ts |= 1 << uint(i) } switch as[i].(type) { case *Inet4Addr: - if af == syscall.AF_UNSPEC { - af = syscall.AF_INET + if af == unix.AF_UNSPEC { + af = unix.AF_INET } - if af != syscall.AF_INET { + if af != unix.AF_INET { return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) } case *Inet6Addr: - if af == syscall.AF_UNSPEC { - af = syscall.AF_INET6 + if af == unix.AF_UNSPEC { + af = unix.AF_INET6 } - if af != syscall.AF_INET6 { + if af != unix.AF_INET6 { return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) } } diff --git a/route/sys.go b/route/sys.go index fcebee58ec..497819e41d 100644 --- a/route/sys.go +++ b/route/sys.go @@ -7,8 +7,9 @@ package route import ( - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) var ( @@ -27,7 +28,7 @@ func init() { nativeEndian = bigEndian } // might get overridden in probeRoutingStack - rtmVersion = syscall.RTM_VERSION + rtmVersion = unix.RTM_VERSION kernelAlign, wireFormats = probeRoutingStack() } diff --git a/route/sys_darwin.go b/route/sys_darwin.go index c8c4eecb8e..6444a3dcd5 100644 --- a/route/sys_darwin.go +++ b/route/sys_darwin.go @@ -4,11 +4,11 @@ package route -import "syscall" +import "golang.org/x/sys/unix" func (typ RIBType) parseable() bool { switch typ { - case syscall.NET_RT_STAT, syscall.NET_RT_TRASH: + case unix.NET_RT_STAT, unix.NET_RT_TRASH: return false default: return true @@ -52,38 +52,38 @@ func (m *InterfaceMessage) Sys() []Sys { } func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15} + rtm := &wireFormat{extOff: 36, bodyOff: unix.SizeofRtMsghdr} rtm.parse = rtm.parseRouteMessage rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15} rtm2.parse = rtm2.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15} + ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} ifm.parse = ifm.parseInterfaceMessage ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15} ifm2.parse = ifm2.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15} + ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15} + ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15} + ifmam2 := &wireFormat{extOff: unix.SizeofIfmaMsghdr2, bodyOff: unix.SizeofIfmaMsghdr2} ifmam2.parse = ifmam2.parseInterfaceMulticastAddrMessage // Darwin kernels require 32-bit aligned access to routing facilities. return 4, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFINFO2: ifm2, - syscall.RTM_NEWMADDR2: ifmam2, - syscall.RTM_GET2: rtm2, + unix.RTM_ADD: rtm, + unix.RTM_DELETE: rtm, + unix.RTM_CHANGE: rtm, + unix.RTM_GET: rtm, + unix.RTM_LOSING: rtm, + unix.RTM_REDIRECT: rtm, + unix.RTM_MISS: rtm, + unix.RTM_LOCK: rtm, + unix.RTM_RESOLVE: rtm, + unix.RTM_NEWADDR: ifam, + unix.RTM_DELADDR: ifam, + unix.RTM_IFINFO: ifm, + unix.RTM_NEWMADDR: ifmam, + unix.RTM_DELMADDR: ifmam, + unix.RTM_IFINFO2: ifm2, + unix.RTM_NEWMADDR2: ifmam2, + unix.RTM_GET2: rtm2, } } diff --git a/route/sys_dragonfly.go b/route/sys_dragonfly.go index 577fb16eb4..4fb118cd74 100644 --- a/route/sys_dragonfly.go +++ b/route/sys_dragonfly.go @@ -5,8 +5,9 @@ package route import ( - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { return true } @@ -49,40 +50,42 @@ func (m *InterfaceMessage) Sys() []Sys { func probeRoutingStack() (int, map[int]*wireFormat) { var p uintptr - rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4} + rtm := &wireFormat{extOff: 40, bodyOff: unix.SizeofRtMsghdr} rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4} + ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4} + ifam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfaMsghdr} ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4} + ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4} + ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} ifanm.parse = ifanm.parseInterfaceAnnounceMessage - rel, _ := syscall.SysctlUint32("kern.osreldate") - if rel >= 500705 { + rel, _ := unix.SysctlUint32("kern.osreldate") + if rel < 500705 { // https://github.com/DragonFlyBSD/DragonFlyBSD/commit/43a373152df2d405c9940983e584e6a25e76632d - // but only the size of struct ifa_msghdr actually changed + // but only the size of struct ifa_msghdr actually changed. + // The type is not in current header files, + // so we just use constants here. rtmVersion = 7 - ifam.bodyOff = sizeofIfaMsghdrDragonFlyBSD58 + ifam.bodyOff = 0x14 } return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFANNOUNCE: ifanm, + unix.RTM_ADD: rtm, + unix.RTM_DELETE: rtm, + unix.RTM_CHANGE: rtm, + unix.RTM_GET: rtm, + unix.RTM_LOSING: rtm, + unix.RTM_REDIRECT: rtm, + unix.RTM_MISS: rtm, + unix.RTM_LOCK: rtm, + unix.RTM_RESOLVE: rtm, + unix.RTM_NEWADDR: ifam, + unix.RTM_DELADDR: ifam, + unix.RTM_IFINFO: ifm, + unix.RTM_NEWMADDR: ifmam, + unix.RTM_DELMADDR: ifmam, + unix.RTM_IFANNOUNCE: ifanm, } } diff --git a/route/sys_freebsd.go b/route/sys_freebsd.go index 0a66dcedb3..608f1a9307 100644 --- a/route/sys_freebsd.go +++ b/route/sys_freebsd.go @@ -5,8 +5,9 @@ package route import ( - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { return true } @@ -64,7 +65,7 @@ func probeRoutingStack() (int, map[int]*wireFormat) { // to know the underlying kernel's architecture because the // alignment for routing facilities are set at the build time // of the kernel. - conf, _ := syscall.Sysctl("kern.conftxt") + conf, _ := unix.Sysctl("kern.conftxt") for i, j := 0, 0; j < len(conf); j++ { if conf[j] != '\n' { continue @@ -88,21 +89,17 @@ func probeRoutingStack() (int, map[int]*wireFormat) { if align != wordSize { compatFreeBSD32 = true // 386 emulation on amd64 } - var rtm, ifm, ifam, ifmam, ifanm *wireFormat + var rtm *wireFormat + ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} + ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} + ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} + ifm := &wireFormat{extOff: 16} if compatFreeBSD32 { rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10Emu - sizeofRtMetricsFreeBSD10Emu, bodyOff: sizeofRtMsghdrFreeBSD10Emu} - ifm = &wireFormat{extOff: 16} - ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10Emu, bodyOff: sizeofIfaMsghdrFreeBSD10Emu} - ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10Emu, bodyOff: sizeofIfmaMsghdrFreeBSD10Emu} - ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10Emu, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10Emu} } else { rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10 - sizeofRtMetricsFreeBSD10, bodyOff: sizeofRtMsghdrFreeBSD10} - ifm = &wireFormat{extOff: 16} - ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10, bodyOff: sizeofIfaMsghdrFreeBSD10} - ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10, bodyOff: sizeofIfmaMsghdrFreeBSD10} - ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10} } - rel, _ := syscall.SysctlUint32("kern.osreldate") + rel, _ := unix.SysctlUint32("kern.osreldate") switch { case rel < 800000: if compatFreeBSD32 { @@ -141,20 +138,20 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage ifanm.parse = ifanm.parseInterfaceAnnounceMessage return align, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFANNOUNCE: ifanm, + unix.RTM_ADD: rtm, + unix.RTM_DELETE: rtm, + unix.RTM_CHANGE: rtm, + unix.RTM_GET: rtm, + unix.RTM_LOSING: rtm, + unix.RTM_REDIRECT: rtm, + unix.RTM_MISS: rtm, + unix.RTM_LOCK: rtm, + unix.RTM_RESOLVE: rtm, + unix.RTM_NEWADDR: ifam, + unix.RTM_DELADDR: ifam, + unix.RTM_IFINFO: ifm, + unix.RTM_NEWMADDR: ifmam, + unix.RTM_DELMADDR: ifmam, + unix.RTM_IFANNOUNCE: ifanm, } } diff --git a/route/sys_netbsd.go b/route/sys_netbsd.go index be4460e13f..441458180a 100644 --- a/route/sys_netbsd.go +++ b/route/sys_netbsd.go @@ -4,7 +4,7 @@ package route -import "syscall" +import "golang.org/x/sys/unix" func (typ RIBType) parseable() bool { return true } @@ -45,29 +45,29 @@ func (m *InterfaceMessage) Sys() []Sys { } func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7} + rtm := &wireFormat{extOff: 40, bodyOff: unix.SizeofRtMsghdr} rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7} + ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7} + ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} ifam.parse = ifam.parseInterfaceAddrMessage - ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7} + ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} ifanm.parse = ifanm.parseInterfaceAnnounceMessage // NetBSD 6 and above kernels require 64-bit aligned access to // routing facilities. return 8, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFANNOUNCE: ifanm, - syscall.RTM_IFINFO: ifm, + unix.RTM_ADD: rtm, + unix.RTM_DELETE: rtm, + unix.RTM_CHANGE: rtm, + unix.RTM_GET: rtm, + unix.RTM_LOSING: rtm, + unix.RTM_REDIRECT: rtm, + unix.RTM_MISS: rtm, + unix.RTM_LOCK: rtm, + unix.RTM_RESOLVE: rtm, + unix.RTM_NEWADDR: ifam, + unix.RTM_DELADDR: ifam, + unix.RTM_IFANNOUNCE: ifanm, + unix.RTM_IFINFO: ifm, } } diff --git a/route/sys_openbsd.go b/route/sys_openbsd.go index 7f4f93cbea..21f1d3d6e5 100644 --- a/route/sys_openbsd.go +++ b/route/sys_openbsd.go @@ -5,13 +5,14 @@ package route import ( - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { switch typ { - case syscall.NET_RT_STATS, syscall.NET_RT_TABLE: + case unix.NET_RT_STATS, unix.NET_RT_TABLE: return false default: return true @@ -65,18 +66,18 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifanm := &wireFormat{extOff: -1, bodyOff: -1} ifanm.parse = ifanm.parseInterfaceAnnounceMessage return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_IFANNOUNCE: ifanm, - syscall.RTM_DESYNC: rtm, + unix.RTM_ADD: rtm, + unix.RTM_DELETE: rtm, + unix.RTM_CHANGE: rtm, + unix.RTM_GET: rtm, + unix.RTM_LOSING: rtm, + unix.RTM_REDIRECT: rtm, + unix.RTM_MISS: rtm, + unix.RTM_RESOLVE: rtm, + unix.RTM_NEWADDR: ifam, + unix.RTM_DELADDR: ifam, + unix.RTM_IFINFO: ifm, + unix.RTM_IFANNOUNCE: ifanm, + unix.RTM_DESYNC: rtm, } } diff --git a/route/zsys_darwin.go b/route/zsys_darwin.go index 56a0c66f44..521be9f19f 100644 --- a/route/zsys_darwin.go +++ b/route/zsys_darwin.go @@ -4,19 +4,8 @@ package route const ( - sizeofIfMsghdrDarwin15 = 0x70 - sizeofIfaMsghdrDarwin15 = 0x14 - sizeofIfmaMsghdrDarwin15 = 0x10 - sizeofIfMsghdr2Darwin15 = 0xa0 - sizeofIfmaMsghdr2Darwin15 = 0x14 - sizeofIfDataDarwin15 = 0x60 - sizeofIfData64Darwin15 = 0x80 + sizeofIfMsghdr2Darwin15 = 0xa0 + sizeofIfData64Darwin15 = 0x80 - sizeofRtMsghdrDarwin15 = 0x5c sizeofRtMsghdr2Darwin15 = 0x5c - sizeofRtMetricsDarwin15 = 0x38 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_dragonfly.go b/route/zsys_dragonfly.go deleted file mode 100644 index f7c7a60cd6..0000000000 --- a/route/zsys_dragonfly.go +++ /dev/null @@ -1,20 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_dragonfly.go - -package route - -const ( - sizeofIfMsghdrDragonFlyBSD4 = 0xb0 - sizeofIfaMsghdrDragonFlyBSD4 = 0x14 - sizeofIfmaMsghdrDragonFlyBSD4 = 0x10 - sizeofIfAnnouncemsghdrDragonFlyBSD4 = 0x18 - - sizeofIfaMsghdrDragonFlyBSD58 = 0x18 - - sizeofRtMsghdrDragonFlyBSD4 = 0x98 - sizeofRtMetricsDragonFlyBSD4 = 0x70 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/route/zsys_freebsd_386.go b/route/zsys_freebsd_386.go index 3f985c7ee9..37b5c7b3e3 100644 --- a/route/zsys_freebsd_386.go +++ b/route/zsys_freebsd_386.go @@ -4,12 +4,6 @@ package route const ( - sizeofIfMsghdrlFreeBSD10 = 0x68 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - sizeofRtMsghdrFreeBSD10 = 0x5c sizeofRtMetricsFreeBSD10 = 0x38 @@ -28,12 +22,6 @@ const ( // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_amd64.go b/route/zsys_freebsd_amd64.go index 9293393698..2f6cf35ff0 100644 --- a/route/zsys_freebsd_amd64.go +++ b/route/zsys_freebsd_amd64.go @@ -4,12 +4,6 @@ package route const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -25,12 +19,6 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_arm.go b/route/zsys_freebsd_arm.go index a2bdb4ad3b..b6b90d55cf 100644 --- a/route/zsys_freebsd_arm.go +++ b/route/zsys_freebsd_arm.go @@ -4,12 +4,6 @@ package route const ( - sizeofIfMsghdrlFreeBSD10 = 0x68 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - sizeofRtMsghdrFreeBSD10 = 0x5c sizeofRtMetricsFreeBSD10 = 0x38 @@ -25,12 +19,6 @@ const ( sizeofIfDataFreeBSD10 = 0x60 sizeofIfDataFreeBSD11 = 0x98 - sizeofIfMsghdrlFreeBSD10Emu = 0x68 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0x6c - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - sizeofRtMsghdrFreeBSD10Emu = 0x5c sizeofRtMetricsFreeBSD10Emu = 0x38 diff --git a/route/zsys_freebsd_arm64.go b/route/zsys_freebsd_arm64.go index 9293393698..2f6cf35ff0 100644 --- a/route/zsys_freebsd_arm64.go +++ b/route/zsys_freebsd_arm64.go @@ -4,12 +4,6 @@ package route const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -25,12 +19,6 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_riscv64.go b/route/zsys_freebsd_riscv64.go index 9293393698..2f6cf35ff0 100644 --- a/route/zsys_freebsd_riscv64.go +++ b/route/zsys_freebsd_riscv64.go @@ -4,12 +4,6 @@ package route const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -25,12 +19,6 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_netbsd.go b/route/zsys_netbsd.go deleted file mode 100644 index eaffe8c408..0000000000 --- a/route/zsys_netbsd.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_netbsd.go - -package route - -const ( - sizeofIfMsghdrNetBSD7 = 0x98 - sizeofIfaMsghdrNetBSD7 = 0x18 - sizeofIfAnnouncemsghdrNetBSD7 = 0x18 - - sizeofRtMsghdrNetBSD7 = 0x78 - sizeofRtMetricsNetBSD7 = 0x50 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/route/zsys_openbsd.go b/route/zsys_openbsd.go deleted file mode 100644 index b11b812680..0000000000 --- a/route/zsys_openbsd.go +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_openbsd.go - -package route - -const ( - sizeofRtMsghdr = 0x60 - - sizeofSockaddrStorage = 0x100 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) From 6e414109c26d44bf2c146302818fc3bcc49b97b2 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 2 Dec 2024 13:01:47 -0800 Subject: [PATCH 21/31] http2: fix benchmarks using common frame read/write functions CL 586249 unified frame read/write functions used by client and server tests, but inadvertently broke some benchmarks. Fix those benchmarks. This mostly restores the previous behavior of the affected benchmarks (for example, testing only to see that a DATA frame contains an END_STREAM marker, ignoring the number of bytes in the frame). Fixes golang/go#70647 Change-Id: I2b0099c3513ac8754d11c4e37b7d63277a0fb0b1 Reviewed-on: https://go-review.googlesource.com/c/net/+/633055 Auto-Submit: Damien Neil Reviewed-by: Jonathan Amsterdam LUCI-TryBot-Result: Go LUCI Reviewed-by: Antonio Ojea --- http2/server_test.go | 50 +++++++++++++------------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/http2/server_test.go b/http2/server_test.go index 77de1be209..201cf0d00e 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -333,7 +333,9 @@ func newServerTesterWithRealConn(t testing.TB, handler http.HandlerFunc, opts .. // sync waits for all goroutines to idle. func (st *serverTester) sync() { - st.group.Wait() + if st.group != nil { + st.group.Wait() + } } // advance advances synthetic time by a duration. @@ -2896,15 +2898,10 @@ func BenchmarkServerGets(b *testing.B) { EndStream: true, EndHeaders: true, }) - st.wantHeaders(wantHeader{ - streamID: 1, - endStream: true, - }) - st.wantData(wantData{ - streamID: 1, - endStream: true, - size: 0, - }) + st.wantFrameType(FrameHeaders) + if df := readFrame[*DataFrame](b, st); !df.StreamEnded() { + b.Fatalf("DATA didn't have END_STREAM; got %v", df) + } } } @@ -2939,15 +2936,10 @@ func BenchmarkServerPosts(b *testing.B) { EndHeaders: true, }) st.writeData(id, true, nil) - st.wantHeaders(wantHeader{ - streamID: 1, - endStream: false, - }) - st.wantData(wantData{ - streamID: 1, - endStream: true, - size: 0, - }) + st.wantFrameType(FrameHeaders) + if df := readFrame[*DataFrame](b, st); !df.StreamEnded() { + b.Fatalf("DATA didn't have END_STREAM; got %v", df) + } } } @@ -3289,14 +3281,8 @@ func BenchmarkServer_GetRequest(b *testing.B) { EndStream: true, EndHeaders: true, }) - st.wantHeaders(wantHeader{ - streamID: streamID, - endStream: false, - }) - st.wantData(wantData{ - streamID: streamID, - endStream: true, - }) + st.wantFrameType(FrameHeaders) + st.wantFrameType(FrameData) } } @@ -3327,14 +3313,8 @@ func BenchmarkServer_PostRequest(b *testing.B) { EndHeaders: true, }) st.writeData(streamID, true, nil) - st.wantHeaders(wantHeader{ - streamID: streamID, - endStream: false, - }) - st.wantData(wantData{ - streamID: streamID, - endStream: true, - }) + st.wantFrameType(FrameHeaders) + st.wantFrameType(FrameData) } } From d0a1049b7e05d3d6f62570a8381a22301a97ae73 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 3 Dec 2024 13:23:01 +0100 Subject: [PATCH 22/31] route: remove unused sizeof* consts on freebsd The sizeofSockaddr* consts were replaced by the respective x/sys/unix consts by CL 632816. The sizeofIfDataFreeBSD* consts were never used since they were added. Cq-Include-Trybots: luci.golang.try:x_net-gotip-freebsd-amd64 Change-Id: Ieb0e01353199e08659d45cdc3293e817b679fed5 Reviewed-on: https://go-review.googlesource.com/c/net/+/633155 Reviewed-by: Ian Lance Taylor Auto-Submit: Tobias Klauser LUCI-TryBot-Result: Go LUCI Auto-Submit: Damien Neil Reviewed-by: Damien Neil --- route/defs_freebsd.go | 16 ---------------- route/zsys_freebsd_386.go | 16 ---------------- route/zsys_freebsd_amd64.go | 16 ---------------- route/zsys_freebsd_arm.go | 16 ---------------- route/zsys_freebsd_arm64.go | 16 ---------------- route/zsys_freebsd_riscv64.go | 16 ---------------- 6 files changed, 96 deletions(-) diff --git a/route/defs_freebsd.go b/route/defs_freebsd.go index a1318949a3..863e4ea245 100644 --- a/route/defs_freebsd.go +++ b/route/defs_freebsd.go @@ -227,12 +227,6 @@ const ( sizeofIfMsghdrFreeBSD10 = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11 = C.sizeof_struct_if_msghdr_freebsd11 - sizeofIfDataFreeBSD7 = C.sizeof_struct_if_data_freebsd7 - sizeofIfDataFreeBSD8 = C.sizeof_struct_if_data_freebsd8 - sizeofIfDataFreeBSD9 = C.sizeof_struct_if_data_freebsd9 - sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 - sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 - sizeofRtMsghdrFreeBSD10Emu = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10Emu = C.sizeof_struct_rt_metrics @@ -241,14 +235,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = C.sizeof_struct_if_msghdr_freebsd9 sizeofIfMsghdrFreeBSD10Emu = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11Emu = C.sizeof_struct_if_msghdr_freebsd11 - - sizeofIfDataFreeBSD7Emu = C.sizeof_struct_if_data_freebsd7 - sizeofIfDataFreeBSD8Emu = C.sizeof_struct_if_data_freebsd8 - sizeofIfDataFreeBSD9Emu = C.sizeof_struct_if_data_freebsd9 - sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10 - sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11 - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/zsys_freebsd_386.go b/route/zsys_freebsd_386.go index 37b5c7b3e3..a4a6d290e9 100644 --- a/route/zsys_freebsd_386.go +++ b/route/zsys_freebsd_386.go @@ -13,12 +13,6 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x64 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x50 - sizeofIfDataFreeBSD8 = 0x50 - sizeofIfDataFreeBSD9 = 0x50 - sizeofIfDataFreeBSD10 = 0x54 - sizeofIfDataFreeBSD11 = 0x98 - // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT @@ -30,14 +24,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_amd64.go b/route/zsys_freebsd_amd64.go index 2f6cf35ff0..d563b86ac4 100644 --- a/route/zsys_freebsd_amd64.go +++ b/route/zsys_freebsd_amd64.go @@ -13,12 +13,6 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -27,14 +21,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm.go b/route/zsys_freebsd_arm.go index b6b90d55cf..c7f9bbb214 100644 --- a/route/zsys_freebsd_arm.go +++ b/route/zsys_freebsd_arm.go @@ -13,12 +13,6 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x70 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x60 - sizeofIfDataFreeBSD8 = 0x60 - sizeofIfDataFreeBSD9 = 0x60 - sizeofIfDataFreeBSD10 = 0x60 - sizeofIfDataFreeBSD11 = 0x98 - sizeofRtMsghdrFreeBSD10Emu = 0x5c sizeofRtMetricsFreeBSD10Emu = 0x38 @@ -27,14 +21,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0x70 sizeofIfMsghdrFreeBSD10Emu = 0x70 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x60 - sizeofIfDataFreeBSD8Emu = 0x60 - sizeofIfDataFreeBSD9Emu = 0x60 - sizeofIfDataFreeBSD10Emu = 0x60 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm64.go b/route/zsys_freebsd_arm64.go index 2f6cf35ff0..d563b86ac4 100644 --- a/route/zsys_freebsd_arm64.go +++ b/route/zsys_freebsd_arm64.go @@ -13,12 +13,6 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -27,14 +21,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_riscv64.go b/route/zsys_freebsd_riscv64.go index 2f6cf35ff0..d563b86ac4 100644 --- a/route/zsys_freebsd_riscv64.go +++ b/route/zsys_freebsd_riscv64.go @@ -13,12 +13,6 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -27,14 +21,4 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c ) From 285e1cf6650f407805ea8af9255624961b768479 Mon Sep 17 00:00:00 2001 From: Gopher Robot Date: Wed, 4 Dec 2024 20:26:06 +0000 Subject: [PATCH 23/31] go.mod: update golang.org/x dependencies Update golang.org/x dependencies to their latest tagged versions. Change-Id: I5db578f0e340dbf37012f580b307155806d55993 Reviewed-on: https://go-review.googlesource.com/c/net/+/633584 Reviewed-by: Dmitri Shuralyov Auto-Submit: Gopher Robot LUCI-TryBot-Result: Go LUCI Reviewed-by: David Chase --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 2721bac68d..59ca52bc48 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module golang.org/x/net go 1.18 require ( - golang.org/x/crypto v0.29.0 - golang.org/x/sys v0.27.0 - golang.org/x/term v0.26.0 - golang.org/x/text v0.20.0 + golang.org/x/crypto v0.30.0 + golang.org/x/sys v0.28.0 + golang.org/x/term v0.27.0 + golang.org/x/text v0.21.0 ) diff --git a/go.sum b/go.sum index 6868ecce4c..b723d4ccb1 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= From 13a7c0108bd38aad013797cdb95e6bfca0bbcec6 Mon Sep 17 00:00:00 2001 From: Michael Knyszek Date: Fri, 6 Dec 2024 17:48:19 +0000 Subject: [PATCH 24/31] Revert "route: remove unused sizeof* consts on freebsd" This reverts CL 633155. Reason for revert: This is part of a short chain of CLs that causes x/net to depend on x/sys. We have a policy that prevents us from vendoring x/sys into std, but x/net needs to be vendored. Change-Id: Ib773f21dee59fe6f4afb7ba1d7ae01d8859110c6 Reviewed-on: https://go-review.googlesource.com/c/net/+/634255 Auto-Submit: Michael Knyszek Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- route/defs_freebsd.go | 16 ++++++++++++++++ route/zsys_freebsd_386.go | 16 ++++++++++++++++ route/zsys_freebsd_amd64.go | 16 ++++++++++++++++ route/zsys_freebsd_arm.go | 16 ++++++++++++++++ route/zsys_freebsd_arm64.go | 16 ++++++++++++++++ route/zsys_freebsd_riscv64.go | 16 ++++++++++++++++ 6 files changed, 96 insertions(+) diff --git a/route/defs_freebsd.go b/route/defs_freebsd.go index 863e4ea245..a1318949a3 100644 --- a/route/defs_freebsd.go +++ b/route/defs_freebsd.go @@ -227,6 +227,12 @@ const ( sizeofIfMsghdrFreeBSD10 = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11 = C.sizeof_struct_if_msghdr_freebsd11 + sizeofIfDataFreeBSD7 = C.sizeof_struct_if_data_freebsd7 + sizeofIfDataFreeBSD8 = C.sizeof_struct_if_data_freebsd8 + sizeofIfDataFreeBSD9 = C.sizeof_struct_if_data_freebsd9 + sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 + sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 + sizeofRtMsghdrFreeBSD10Emu = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10Emu = C.sizeof_struct_rt_metrics @@ -235,4 +241,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = C.sizeof_struct_if_msghdr_freebsd9 sizeofIfMsghdrFreeBSD10Emu = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11Emu = C.sizeof_struct_if_msghdr_freebsd11 + + sizeofIfDataFreeBSD7Emu = C.sizeof_struct_if_data_freebsd7 + sizeofIfDataFreeBSD8Emu = C.sizeof_struct_if_data_freebsd8 + sizeofIfDataFreeBSD9Emu = C.sizeof_struct_if_data_freebsd9 + sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10 + sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11 + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/zsys_freebsd_386.go b/route/zsys_freebsd_386.go index a4a6d290e9..37b5c7b3e3 100644 --- a/route/zsys_freebsd_386.go +++ b/route/zsys_freebsd_386.go @@ -13,6 +13,12 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x64 sizeofIfMsghdrFreeBSD11 = 0xa8 + sizeofIfDataFreeBSD7 = 0x50 + sizeofIfDataFreeBSD8 = 0x50 + sizeofIfDataFreeBSD9 = 0x50 + sizeofIfDataFreeBSD10 = 0x54 + sizeofIfDataFreeBSD11 = 0x98 + // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT @@ -24,4 +30,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_amd64.go b/route/zsys_freebsd_amd64.go index d563b86ac4..2f6cf35ff0 100644 --- a/route/zsys_freebsd_amd64.go +++ b/route/zsys_freebsd_amd64.go @@ -13,6 +13,12 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 + sizeofIfDataFreeBSD7 = 0x98 + sizeofIfDataFreeBSD8 = 0x98 + sizeofIfDataFreeBSD9 = 0x98 + sizeofIfDataFreeBSD10 = 0x98 + sizeofIfDataFreeBSD11 = 0x98 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -21,4 +27,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm.go b/route/zsys_freebsd_arm.go index c7f9bbb214..b6b90d55cf 100644 --- a/route/zsys_freebsd_arm.go +++ b/route/zsys_freebsd_arm.go @@ -13,6 +13,12 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x70 sizeofIfMsghdrFreeBSD11 = 0xa8 + sizeofIfDataFreeBSD7 = 0x60 + sizeofIfDataFreeBSD8 = 0x60 + sizeofIfDataFreeBSD9 = 0x60 + sizeofIfDataFreeBSD10 = 0x60 + sizeofIfDataFreeBSD11 = 0x98 + sizeofRtMsghdrFreeBSD10Emu = 0x5c sizeofRtMetricsFreeBSD10Emu = 0x38 @@ -21,4 +27,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0x70 sizeofIfMsghdrFreeBSD10Emu = 0x70 sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x60 + sizeofIfDataFreeBSD8Emu = 0x60 + sizeofIfDataFreeBSD9Emu = 0x60 + sizeofIfDataFreeBSD10Emu = 0x60 + sizeofIfDataFreeBSD11Emu = 0x98 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm64.go b/route/zsys_freebsd_arm64.go index d563b86ac4..2f6cf35ff0 100644 --- a/route/zsys_freebsd_arm64.go +++ b/route/zsys_freebsd_arm64.go @@ -13,6 +13,12 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 + sizeofIfDataFreeBSD7 = 0x98 + sizeofIfDataFreeBSD8 = 0x98 + sizeofIfDataFreeBSD9 = 0x98 + sizeofIfDataFreeBSD10 = 0x98 + sizeofIfDataFreeBSD11 = 0x98 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -21,4 +27,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_riscv64.go b/route/zsys_freebsd_riscv64.go index d563b86ac4..2f6cf35ff0 100644 --- a/route/zsys_freebsd_riscv64.go +++ b/route/zsys_freebsd_riscv64.go @@ -13,6 +13,12 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 + sizeofIfDataFreeBSD7 = 0x98 + sizeofIfDataFreeBSD8 = 0x98 + sizeofIfDataFreeBSD9 = 0x98 + sizeofIfDataFreeBSD10 = 0x98 + sizeofIfDataFreeBSD11 = 0x98 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 @@ -21,4 +27,14 @@ const ( sizeofIfMsghdrFreeBSD9Emu = 0xa8 sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) From 552d8ac903a11a9fde71a88732f5b58b6b394178 Mon Sep 17 00:00:00 2001 From: Michael Knyszek Date: Fri, 6 Dec 2024 17:50:43 +0000 Subject: [PATCH 25/31] Revert "route: change from syscall to x/sys/unix" This reverts CL 632816. Reason for revert: This CL causes x/net to depend on x/sys. We have a policy that prevents us from vendoring x/sys into std, but x/net needs to be vendored. Change-Id: I0fe3bc9861d559d888db6fa7febd48a201f060b8 Reviewed-on: https://go-review.googlesource.com/c/net/+/634196 Auto-Submit: Michael Knyszek Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- route/address.go | 45 +++++++++++----------- route/address_darwin_test.go | 13 +++---- route/address_test.go | 7 ++-- route/defs_darwin.go | 15 +++++++- route/defs_dragonfly.go | 56 +++++++++++++++++++++++++++ route/defs_freebsd.go | 12 ++++++ route/defs_netbsd.go | 32 ++++++++++++++++ route/defs_openbsd.go | 27 +++++++++++++ route/interface_classic.go | 9 ++--- route/interface_freebsd.go | 12 +++--- route/interface_openbsd.go | 8 ++-- route/message_darwin_test.go | 7 ++-- route/message_freebsd_test.go | 15 ++++---- route/message_test.go | 71 +++++++++++++++++------------------ route/route.go | 11 +++--- route/route_classic.go | 5 +-- route/route_openbsd.go | 14 +++---- route/route_test.go | 25 ++++++------ route/sys.go | 5 +-- route/sys_darwin.go | 48 +++++++++++------------ route/sys_dragonfly.go | 53 ++++++++++++-------------- route/sys_freebsd.go | 51 +++++++++++++------------ route/sys_netbsd.go | 36 +++++++++--------- route/sys_openbsd.go | 31 ++++++++------- route/zsys_darwin.go | 15 +++++++- route/zsys_dragonfly.go | 20 ++++++++++ route/zsys_freebsd_386.go | 12 ++++++ route/zsys_freebsd_amd64.go | 12 ++++++ route/zsys_freebsd_arm.go | 12 ++++++ route/zsys_freebsd_arm64.go | 12 ++++++ route/zsys_freebsd_riscv64.go | 12 ++++++ route/zsys_netbsd.go | 17 +++++++++ route/zsys_openbsd.go | 12 ++++++ 33 files changed, 489 insertions(+), 243 deletions(-) create mode 100644 route/defs_dragonfly.go create mode 100644 route/defs_netbsd.go create mode 100644 route/defs_openbsd.go create mode 100644 route/zsys_dragonfly.go create mode 100644 route/zsys_netbsd.go create mode 100644 route/zsys_openbsd.go diff --git a/route/address.go b/route/address.go index 6f72c6cc35..b649f43141 100644 --- a/route/address.go +++ b/route/address.go @@ -8,8 +8,7 @@ package route import ( "runtime" - - "golang.org/x/sys/unix" + "syscall" ) // An Addr represents an address associated with packet routing. @@ -26,7 +25,7 @@ type LinkAddr struct { } // Family implements the Family method of Addr interface. -func (a *LinkAddr) Family() int { return unix.AF_LINK } +func (a *LinkAddr) Family() int { return syscall.AF_LINK } func (a *LinkAddr) lenAndSpace() (int, int) { l := 8 + len(a.Name) + len(a.Addr) @@ -43,7 +42,7 @@ func (a *LinkAddr) marshal(b []byte) (int, error) { return 0, errInvalidAddr } b[0] = byte(l) - b[1] = unix.AF_LINK + b[1] = syscall.AF_LINK if a.Index > 0 { nativeEndian.PutUint16(b[2:4], uint16(a.Index)) } @@ -65,7 +64,7 @@ func parseLinkAddr(b []byte) (Addr, error) { if len(b) < 8 { return nil, errInvalidAddr } - _, a, err := parseKernelLinkAddr(unix.AF_LINK, b[4:]) + _, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:]) if err != nil { return nil, err } @@ -125,10 +124,10 @@ type Inet4Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet4Addr) Family() int { return unix.AF_INET } +func (a *Inet4Addr) Family() int { return syscall.AF_INET } func (a *Inet4Addr) lenAndSpace() (int, int) { - return unix.SizeofSockaddrInet4, roundup(unix.SizeofSockaddrInet4) + return sizeofSockaddrInet, roundup(sizeofSockaddrInet) } func (a *Inet4Addr) marshal(b []byte) (int, error) { @@ -137,7 +136,7 @@ func (a *Inet4Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = unix.AF_INET + b[1] = syscall.AF_INET copy(b[4:8], a.IP[:]) return ll, nil } @@ -149,10 +148,10 @@ type Inet6Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet6Addr) Family() int { return unix.AF_INET6 } +func (a *Inet6Addr) Family() int { return syscall.AF_INET6 } func (a *Inet6Addr) lenAndSpace() (int, int) { - return unix.SizeofSockaddrInet6, roundup(unix.SizeofSockaddrInet6) + return sizeofSockaddrInet6, roundup(sizeofSockaddrInet6) } func (a *Inet6Addr) marshal(b []byte) (int, error) { @@ -161,7 +160,7 @@ func (a *Inet6Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = unix.AF_INET6 + b[1] = syscall.AF_INET6 copy(b[8:24], a.IP[:]) if a.ZoneID > 0 { nativeEndian.PutUint32(b[24:28], uint32(a.ZoneID)) @@ -176,7 +175,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { off6 = 8 // offset of in6_addr ) switch af { - case unix.AF_INET: + case syscall.AF_INET: if len(b) < (off4+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } @@ -188,7 +187,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { } copy(a.IP[:], b[off4:n]) return a, nil - case unix.AF_INET6: + case syscall.AF_INET6: if len(b) < (off6+1) || len(b) < int(b[0]) || b[0] == 0 { return nil, errInvalidAddr } @@ -198,7 +197,7 @@ func parseInetAddr(af int, b []byte) (Addr, error) { n = sockAddrLen } a := &Inet6Addr{} - if sockAddrLen == unix.SizeofSockaddrInet6 { + if sockAddrLen == sizeofSockaddrInet6 { a.ZoneID = int(nativeEndian.Uint32(b[24:28])) } copy(a.IP[:], b[off6:n]) @@ -261,11 +260,11 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { off6 = 8 // offset of in6_addr ) switch { - case b[0] == unix.SizeofSockaddrInet6: + case b[0] == sizeofSockaddrInet6: a := &Inet6Addr{} copy(a.IP[:], b[off6:off6+16]) return int(b[0]), a, nil - case af == unix.AF_INET6: + case af == syscall.AF_INET6: a := &Inet6Addr{} if l-1 < off6 { copy(a.IP[:], b[1:l]) @@ -273,7 +272,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { copy(a.IP[:], b[l-off6:l]) } return int(b[0]), a, nil - case b[0] == unix.SizeofSockaddrInet4: + case b[0] == sizeofSockaddrInet: a := &Inet4Addr{} copy(a.IP[:], b[off4:off4+4]) return int(b[0]), a, nil @@ -385,15 +384,15 @@ func marshalAddrs(b []byte, as []Addr) (uint, error) { } func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) { - var as [unix.RTAX_MAX]Addr - af := int(unix.AF_UNSPEC) - for i := uint(0); i < unix.RTAX_MAX && len(b) >= roundup(0); i++ { + var as [syscall.RTAX_MAX]Addr + af := int(syscall.AF_UNSPEC) + for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ { if attrs&(1< 0 { diff --git a/route/address_darwin_test.go b/route/address_darwin_test.go index 08eb8b7d1d..add72e37ec 100644 --- a/route/address_darwin_test.go +++ b/route/address_darwin_test.go @@ -6,9 +6,8 @@ package route import ( "reflect" + "syscall" "testing" - - "golang.org/x/sys/unix" ) type parseAddrsOnDarwinTest struct { @@ -20,7 +19,7 @@ type parseAddrsOnDarwinTest struct { var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x10, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0x56, 0x0, @@ -44,7 +43,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, }, { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x10, 0x02, 0x00, 0x00, 0x64, 0x71, 0x00, 0x00, @@ -70,7 +69,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ // route -n add -inet6 fd84:1b4e:6281:: -prefixlen 48 fe80::f22f:4bff:fe09:3bff%utun4319 // gw fe80:0000:0000:0000:f22f:4bff:fe09:3bff { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -99,7 +98,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, // golang/go#70528, the kernel can produce addresses of length 0 { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x00, 0x1e, 0x00, 0x00, @@ -125,7 +124,7 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{ }, // Additional case: golang/go/issues/70528#issuecomment-2498692877 { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK, parseKernelInetAddr, []byte{ 0x84, 0x00, 0x05, 0x04, 0x01, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x01, 0x15, 0x00, 0x00, 0x00, diff --git a/route/address_test.go b/route/address_test.go index cd0f3ab499..31087576ed 100644 --- a/route/address_test.go +++ b/route/address_test.go @@ -8,9 +8,8 @@ package route import ( "reflect" + "syscall" "testing" - - "golang.org/x/sys/unix" ) type parseAddrsTest struct { @@ -22,7 +21,7 @@ type parseAddrsTest struct { var parseAddrsLittleEndianTests = []parseAddrsTest{ { - unix.RTA_DST | unix.RTA_GATEWAY | unix.RTA_NETMASK | unix.RTA_BRD, + syscall.RTA_DST | syscall.RTA_GATEWAY | syscall.RTA_NETMASK | syscall.RTA_BRD, parseKernelInetAddr, []byte{ 0x38, 0x12, 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, @@ -59,7 +58,7 @@ var parseAddrsLittleEndianTests = []parseAddrsTest{ }, }, { - unix.RTA_NETMASK | unix.RTA_IFP | unix.RTA_IFA, + syscall.RTA_NETMASK | syscall.RTA_IFP | syscall.RTA_IFA, parseKernelInetAddr, []byte{ 0x7, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, diff --git a/route/defs_darwin.go b/route/defs_darwin.go index 0b95479c2e..ec56ca02e1 100644 --- a/route/defs_darwin.go +++ b/route/defs_darwin.go @@ -19,8 +19,19 @@ package route import "C" const ( - sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 - sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 + sizeofIfMsghdrDarwin15 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrDarwin15 = C.sizeof_struct_ifa_msghdr + sizeofIfmaMsghdrDarwin15 = C.sizeof_struct_ifma_msghdr + sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 + sizeofIfmaMsghdr2Darwin15 = C.sizeof_struct_ifma_msghdr2 + sizeofIfDataDarwin15 = C.sizeof_struct_if_data + sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 + sizeofRtMsghdrDarwin15 = C.sizeof_struct_rt_msghdr sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2 + sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_dragonfly.go b/route/defs_dragonfly.go new file mode 100644 index 0000000000..9bf202dda4 --- /dev/null +++ b/route/defs_dragonfly.go @@ -0,0 +1,56 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package route + +/* +#include +#include + +#include +#include +#include + +#include + +struct ifa_msghdr_dfly4 { + u_short ifam_msglen; + u_char ifam_version; + u_char ifam_type; + int ifam_addrs; + int ifam_flags; + u_short ifam_index; + int ifam_metric; +}; + +struct ifa_msghdr_dfly58 { + u_short ifam_msglen; + u_char ifam_version; + u_char ifam_type; + u_short ifam_index; + int ifam_flags; + int ifam_addrs; + int ifam_addrflags; + int ifam_metric; +}; +*/ +import "C" + +const ( + sizeofIfMsghdrDragonFlyBSD4 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifa_msghdr_dfly4 + sizeofIfmaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrDragonFlyBSD4 = C.sizeof_struct_if_announcemsghdr + + sizeofIfaMsghdrDragonFlyBSD58 = C.sizeof_struct_ifa_msghdr_dfly58 + + sizeofRtMsghdrDragonFlyBSD4 = C.sizeof_struct_rt_msghdr + sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/route/defs_freebsd.go b/route/defs_freebsd.go index a1318949a3..abb2dc0957 100644 --- a/route/defs_freebsd.go +++ b/route/defs_freebsd.go @@ -218,6 +218,12 @@ struct if_msghdr_freebsd11 { import "C" const ( + sizeofIfMsghdrlFreeBSD10 = C.sizeof_struct_if_msghdrl + sizeofIfaMsghdrFreeBSD10 = C.sizeof_struct_ifa_msghdr + sizeofIfaMsghdrlFreeBSD10 = C.sizeof_struct_ifa_msghdrl + sizeofIfmaMsghdrFreeBSD10 = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrFreeBSD10 = C.sizeof_struct_if_announcemsghdr + sizeofRtMsghdrFreeBSD10 = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10 = C.sizeof_struct_rt_metrics @@ -233,6 +239,12 @@ const ( sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 + sizeofIfMsghdrlFreeBSD10Emu = C.sizeof_struct_if_msghdrl + sizeofIfaMsghdrFreeBSD10Emu = C.sizeof_struct_ifa_msghdr + sizeofIfaMsghdrlFreeBSD10Emu = C.sizeof_struct_ifa_msghdrl + sizeofIfmaMsghdrFreeBSD10Emu = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrFreeBSD10Emu = C.sizeof_struct_if_announcemsghdr + sizeofRtMsghdrFreeBSD10Emu = C.sizeof_struct_rt_msghdr sizeofRtMetricsFreeBSD10Emu = C.sizeof_struct_rt_metrics diff --git a/route/defs_netbsd.go b/route/defs_netbsd.go new file mode 100644 index 0000000000..8e89934c5a --- /dev/null +++ b/route/defs_netbsd.go @@ -0,0 +1,32 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package route + +/* +#include +#include + +#include +#include +#include + +#include +*/ +import "C" + +const ( + sizeofIfMsghdrNetBSD7 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrNetBSD7 = C.sizeof_struct_ifa_msghdr + sizeofIfAnnouncemsghdrNetBSD7 = C.sizeof_struct_if_announcemsghdr + + sizeofRtMsghdrNetBSD7 = C.sizeof_struct_rt_msghdr + sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/route/defs_openbsd.go b/route/defs_openbsd.go new file mode 100644 index 0000000000..8f3218bc63 --- /dev/null +++ b/route/defs_openbsd.go @@ -0,0 +1,27 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package route + +/* +#include +#include + +#include +#include +#include + +#include +*/ +import "C" + +const ( + sizeofRtMsghdr = C.sizeof_struct_rt_msghdr + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/route/interface_classic.go b/route/interface_classic.go index e1dc4eba58..be1bf2652e 100644 --- a/route/interface_classic.go +++ b/route/interface_classic.go @@ -8,8 +8,7 @@ package route import ( "runtime" - - "golang.org/x/sys/unix" + "syscall" ) func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { @@ -21,13 +20,13 @@ func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[4:8])) - if attrs&unix.RTA_IFP == 0 { + if attrs&syscall.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ Version: int(b[2]), Type: int(b[3]), - Addrs: make([]Addr, unix.RTAX_MAX), + Addrs: make([]Addr, syscall.RTAX_MAX), Flags: int(nativeEndian.Uint32(b[8:12])), Index: int(nativeEndian.Uint16(b[12:14])), extOff: w.extOff, @@ -37,7 +36,7 @@ func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) if err != nil { return nil, err } - m.Addrs[unix.RTAX_IFP] = a + m.Addrs[syscall.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } diff --git a/route/interface_freebsd.go b/route/interface_freebsd.go index 7e90b17d33..14d251c94f 100644 --- a/route/interface_freebsd.go +++ b/route/interface_freebsd.go @@ -4,11 +4,11 @@ package route -import "golang.org/x/sys/unix" +import "syscall" func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, error) { var extOff, bodyOff int - if typ == unix.NET_RT_IFLISTL { + if typ == syscall.NET_RT_IFLISTL { if len(b) < 20 { return nil, errMessageTooShort } @@ -26,7 +26,7 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[4:8])) - if attrs&unix.RTA_IFP == 0 { + if attrs&syscall.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ @@ -34,7 +34,7 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro Type: int(b[3]), Flags: int(nativeEndian.Uint32(b[8:12])), Index: int(nativeEndian.Uint16(b[12:14])), - Addrs: make([]Addr, unix.RTAX_MAX), + Addrs: make([]Addr, syscall.RTAX_MAX), extOff: extOff, raw: b[:l], } @@ -42,14 +42,14 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro if err != nil { return nil, err } - m.Addrs[unix.RTAX_IFP] = a + m.Addrs[syscall.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } func (w *wireFormat) parseInterfaceAddrMessage(typ RIBType, b []byte) (Message, error) { var bodyOff int - if typ == unix.NET_RT_IFLISTL { + if typ == syscall.NET_RT_IFLISTL { if len(b) < 24 { return nil, errMessageTooShort } diff --git a/route/interface_openbsd.go b/route/interface_openbsd.go index fe003e39de..d369409a72 100644 --- a/route/interface_openbsd.go +++ b/route/interface_openbsd.go @@ -4,7 +4,7 @@ package route -import "golang.org/x/sys/unix" +import "syscall" func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { if len(b) < 32 { @@ -15,7 +15,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { return nil, errInvalidMessage } attrs := uint(nativeEndian.Uint32(b[12:16])) - if attrs&unix.RTA_IFP == 0 { + if attrs&syscall.RTA_IFP == 0 { return nil, nil } m := &InterfaceMessage{ @@ -23,7 +23,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { Type: int(b[3]), Flags: int(nativeEndian.Uint32(b[16:20])), Index: int(nativeEndian.Uint16(b[6:8])), - Addrs: make([]Addr, unix.RTAX_MAX), + Addrs: make([]Addr, syscall.RTAX_MAX), raw: b[:l], } ll := int(nativeEndian.Uint16(b[4:6])) @@ -34,7 +34,7 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { if err != nil { return nil, err } - m.Addrs[unix.RTAX_IFP] = a + m.Addrs[syscall.RTAX_IFP] = a m.Name = a.(*LinkAddr).Name return m, nil } diff --git a/route/message_darwin_test.go b/route/message_darwin_test.go index debe2e6b42..7d6a3c75e8 100644 --- a/route/message_darwin_test.go +++ b/route/message_darwin_test.go @@ -5,16 +5,15 @@ package route import ( + "syscall" "testing" - - "golang.org/x/sys/unix" ) func TestFetchAndParseRIBOnDarwin(t *testing.T) { - for _, typ := range []RIBType{unix.NET_RT_FLAGS, unix.NET_RT_DUMP2, unix.NET_RT_IFLIST2} { + for _, typ := range []RIBType{syscall.NET_RT_FLAGS, syscall.NET_RT_DUMP2, syscall.NET_RT_IFLIST2} { var lastErr error var ms []Message - for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { + for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err diff --git a/route/message_freebsd_test.go b/route/message_freebsd_test.go index 9f899c6991..62677c1e30 100644 --- a/route/message_freebsd_test.go +++ b/route/message_freebsd_test.go @@ -5,16 +5,15 @@ package route import ( + "syscall" "testing" - - "golang.org/x/sys/unix" ) func TestFetchAndParseRIBOnFreeBSD(t *testing.T) { - for _, typ := range []RIBType{unix.NET_RT_IFMALIST} { + for _, typ := range []RIBType{syscall.NET_RT_IFMALIST} { var lastErr error var ms []Message - for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { + for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err @@ -38,7 +37,7 @@ func TestFetchAndParseRIBOnFreeBSD(t *testing.T) { } func TestFetchAndParseRIBOnFreeBSD10AndAbove(t *testing.T) { - if _, err := FetchRIB(unix.AF_UNSPEC, unix.NET_RT_IFLISTL, 0); err != nil { + if _, err := FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLISTL, 0); err != nil { t.Skip("NET_RT_IFLISTL not supported") } if compatFreeBSD32 { @@ -51,12 +50,12 @@ func TestFetchAndParseRIBOnFreeBSD10AndAbove(t *testing.T) { msgs []Message ss []string }{ - {typ: unix.NET_RT_IFLIST}, - {typ: unix.NET_RT_IFLISTL}, + {typ: syscall.NET_RT_IFLIST}, + {typ: syscall.NET_RT_IFLISTL}, } for i := range tests { var lastErr error - for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { + for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { rs, err := fetchAndParseRIB(af, tests[i].typ) if err != nil { lastErr = err diff --git a/route/message_test.go b/route/message_test.go index 74e8c0ade7..9381f1b2df 100644 --- a/route/message_test.go +++ b/route/message_test.go @@ -8,17 +8,16 @@ package route import ( "os" + "syscall" "testing" "time" - - "golang.org/x/sys/unix" ) func TestFetchAndParseRIB(t *testing.T) { - for _, typ := range []RIBType{unix.NET_RT_DUMP, unix.NET_RT_IFLIST} { + for _, typ := range []RIBType{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} { var lastErr error var ms []Message - for _, af := range []int{unix.AF_UNSPEC, unix.AF_INET, unix.AF_INET6} { + for _, af := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { rs, err := fetchAndParseRIB(af, typ) if err != nil { lastErr = err @@ -49,7 +48,7 @@ var ( func init() { // We need to keep rtmonSock alive to avoid treading on // recycled socket descriptors. - rtmonSock, rtmonErr = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + rtmonSock, rtmonErr = syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) } // TestMonitorAndParseRIB leaks a worker goroutine and a socket @@ -85,7 +84,7 @@ func TestMonitorAndParseRIB(t *testing.T) { // use the net package of standard library due // to the lack of support for routing socket // and circular dependency. - n, err := unix.Read(rtmonSock, b) + n, err := syscall.Read(rtmonSock, b) if err != nil { return } @@ -145,60 +144,60 @@ func TestParseRIBWithFuzz(t *testing.T) { } func TestRouteMessage(t *testing.T) { - s, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) if err != nil { t.Fatal(err) } - defer unix.Close(s) + defer syscall.Close(s) var ms []RouteMessage - for _, af := range []int{unix.AF_INET, unix.AF_INET6} { - if _, err := fetchAndParseRIB(af, unix.NET_RT_DUMP); err != nil { + for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { + if _, err := fetchAndParseRIB(af, syscall.NET_RT_DUMP); err != nil { t.Log(err) continue } switch af { - case unix.AF_INET: + case syscall.AF_INET: ms = append(ms, []RouteMessage{ { - Type: unix.RTM_GET, + Type: syscall.RTM_GET, Addrs: []Addr{ - unix.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, - unix.RTAX_GATEWAY: nil, - unix.RTAX_NETMASK: nil, - unix.RTAX_GENMASK: nil, - unix.RTAX_IFP: &LinkAddr{}, - unix.RTAX_IFA: &Inet4Addr{}, - unix.RTAX_AUTHOR: nil, - unix.RTAX_BRD: &Inet4Addr{}, + syscall.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, + syscall.RTAX_GATEWAY: nil, + syscall.RTAX_NETMASK: nil, + syscall.RTAX_GENMASK: nil, + syscall.RTAX_IFP: &LinkAddr{}, + syscall.RTAX_IFA: &Inet4Addr{}, + syscall.RTAX_AUTHOR: nil, + syscall.RTAX_BRD: &Inet4Addr{}, }, }, { - Type: unix.RTM_GET, + Type: syscall.RTM_GET, Addrs: []Addr{ - unix.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, + syscall.RTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}}, }, }, }...) - case unix.AF_INET6: + case syscall.AF_INET6: ms = append(ms, []RouteMessage{ { - Type: unix.RTM_GET, + Type: syscall.RTM_GET, Addrs: []Addr{ - unix.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, - unix.RTAX_GATEWAY: nil, - unix.RTAX_NETMASK: nil, - unix.RTAX_GENMASK: nil, - unix.RTAX_IFP: &LinkAddr{}, - unix.RTAX_IFA: &Inet6Addr{}, - unix.RTAX_AUTHOR: nil, - unix.RTAX_BRD: &Inet6Addr{}, + syscall.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + syscall.RTAX_GATEWAY: nil, + syscall.RTAX_NETMASK: nil, + syscall.RTAX_GENMASK: nil, + syscall.RTAX_IFP: &LinkAddr{}, + syscall.RTAX_IFA: &Inet6Addr{}, + syscall.RTAX_AUTHOR: nil, + syscall.RTAX_BRD: &Inet6Addr{}, }, }, { - Type: unix.RTM_GET, + Type: syscall.RTM_GET, Addrs: []Addr{ - unix.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + syscall.RTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, }, }, }...) @@ -211,11 +210,11 @@ func TestRouteMessage(t *testing.T) { if err != nil { t.Fatalf("%v: %v", m, err) } - if _, err := unix.Write(s, wb); err != nil { + if _, err := syscall.Write(s, wb); err != nil { t.Fatalf("%v: %v", m, err) } rb := make([]byte, os.Getpagesize()) - n, err := unix.Read(s, rb) + n, err := syscall.Read(s, rb) if err != nil { t.Fatalf("%v: %v", m, err) } diff --git a/route/route.go b/route/route.go index 0a20f507cd..ca2ce2b887 100644 --- a/route/route.go +++ b/route/route.go @@ -15,8 +15,7 @@ package route import ( "errors" "os" - - "golang.org/x/sys/unix" + "syscall" ) var ( @@ -93,8 +92,8 @@ func (m *RouteMessage) Marshal() ([]byte, error) { type RIBType int const ( - RIBTypeRoute RIBType = unix.NET_RT_DUMP - RIBTypeInterface RIBType = unix.NET_RT_IFLIST + RIBTypeRoute RIBType = syscall.NET_RT_DUMP + RIBTypeInterface RIBType = syscall.NET_RT_IFLIST ) // FetchRIB fetches a routing information base from the operating @@ -111,7 +110,7 @@ func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { try := 0 for { try++ - mib := [6]int32{unix.CTL_NET, unix.AF_ROUTE, 0, int32(af), int32(typ), int32(arg)} + mib := [6]int32{syscall.CTL_NET, syscall.AF_ROUTE, 0, int32(af), int32(typ), int32(arg)} n := uintptr(0) if err := sysctl(mib[:], nil, &n, nil, 0); err != nil { return nil, os.NewSyscallError("sysctl", err) @@ -125,7 +124,7 @@ func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { // between the two sysctl calls, try a few times // before failing. (golang.org/issue/45736). const maxTries = 3 - if err == unix.ENOMEM && try < maxTries { + if err == syscall.ENOMEM && try < maxTries { continue } return nil, os.NewSyscallError("sysctl", err) diff --git a/route/route_classic.go b/route/route_classic.go index 6501378609..e273fe39ab 100644 --- a/route/route_classic.go +++ b/route/route_classic.go @@ -8,8 +8,7 @@ package route import ( "runtime" - - "golang.org/x/sys/unix" + "syscall" ) func (m *RouteMessage) marshal() ([]byte, error) { @@ -63,7 +62,7 @@ func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) { extOff: w.extOff, raw: b[:l], } - errno := unix.Errno(nativeEndian.Uint32(b[28:32])) + errno := syscall.Errno(nativeEndian.Uint32(b[28:32])) if errno != 0 { m.Err = errno } diff --git a/route/route_openbsd.go b/route/route_openbsd.go index c592bd8eee..f848fb1f24 100644 --- a/route/route_openbsd.go +++ b/route/route_openbsd.go @@ -5,25 +5,25 @@ package route import ( - "golang.org/x/sys/unix" + "syscall" ) func (m *RouteMessage) marshal() ([]byte, error) { - l := unix.SizeofRtMsghdr + addrsSpace(m.Addrs) + l := sizeofRtMsghdr + addrsSpace(m.Addrs) b := make([]byte, l) nativeEndian.PutUint16(b[:2], uint16(l)) if m.Version == 0 { - b[2] = unix.RTM_VERSION + b[2] = syscall.RTM_VERSION } else { b[2] = byte(m.Version) } b[3] = byte(m.Type) - nativeEndian.PutUint16(b[4:6], uint16(unix.SizeofRtMsghdr)) + nativeEndian.PutUint16(b[4:6], uint16(sizeofRtMsghdr)) nativeEndian.PutUint32(b[16:20], uint32(m.Flags)) nativeEndian.PutUint16(b[6:8], uint16(m.Index)) nativeEndian.PutUint32(b[24:28], uint32(m.ID)) nativeEndian.PutUint32(b[28:32], uint32(m.Seq)) - attrs, err := marshalAddrs(b[unix.SizeofRtMsghdr:], m.Addrs) + attrs, err := marshalAddrs(b[sizeofRtMsghdr:], m.Addrs) if err != nil { return nil, err } @@ -34,7 +34,7 @@ func (m *RouteMessage) marshal() ([]byte, error) { } func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { - if len(b) < unix.SizeofRtMsghdr { + if len(b) < sizeofRtMsghdr { return nil, errMessageTooShort } l := int(nativeEndian.Uint16(b[:2])) @@ -54,7 +54,7 @@ func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { if len(b) < ll { return nil, errInvalidMessage } - errno := unix.Errno(nativeEndian.Uint32(b[32:36])) + errno := syscall.Errno(nativeEndian.Uint32(b[32:36])) if errno != 0 { m.Err = errno } diff --git a/route/route_test.go b/route/route_test.go index 9f00a498cd..ba57702178 100644 --- a/route/route_test.go +++ b/route/route_test.go @@ -10,8 +10,7 @@ import ( "fmt" "os/exec" "runtime" - - "golang.org/x/sys/unix" + "syscall" ) func (m *RouteMessage) String() string { @@ -177,13 +176,13 @@ type addrFamily int func (af addrFamily) String() string { switch af { - case unix.AF_UNSPEC: + case syscall.AF_UNSPEC: return "unspec" - case unix.AF_LINK: + case syscall.AF_LINK: return "link" - case unix.AF_INET: + case syscall.AF_INET: return "inet4" - case unix.AF_INET6: + case syscall.AF_INET6: return "inet6" default: return fmt.Sprintf("%d", af) @@ -282,24 +281,24 @@ func (as addrs) String() string { func (as addrs) match(attrs addrAttrs) error { var ts addrAttrs - af := unix.AF_UNSPEC + af := syscall.AF_UNSPEC for i := range as { if as[i] != nil { ts |= 1 << uint(i) } switch as[i].(type) { case *Inet4Addr: - if af == unix.AF_UNSPEC { - af = unix.AF_INET + if af == syscall.AF_UNSPEC { + af = syscall.AF_INET } - if af != unix.AF_INET { + if af != syscall.AF_INET { return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) } case *Inet6Addr: - if af == unix.AF_UNSPEC { - af = unix.AF_INET6 + if af == syscall.AF_UNSPEC { + af = syscall.AF_INET6 } - if af != unix.AF_INET6 { + if af != syscall.AF_INET6 { return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) } } diff --git a/route/sys.go b/route/sys.go index 497819e41d..fcebee58ec 100644 --- a/route/sys.go +++ b/route/sys.go @@ -7,9 +7,8 @@ package route import ( + "syscall" "unsafe" - - "golang.org/x/sys/unix" ) var ( @@ -28,7 +27,7 @@ func init() { nativeEndian = bigEndian } // might get overridden in probeRoutingStack - rtmVersion = unix.RTM_VERSION + rtmVersion = syscall.RTM_VERSION kernelAlign, wireFormats = probeRoutingStack() } diff --git a/route/sys_darwin.go b/route/sys_darwin.go index 6444a3dcd5..c8c4eecb8e 100644 --- a/route/sys_darwin.go +++ b/route/sys_darwin.go @@ -4,11 +4,11 @@ package route -import "golang.org/x/sys/unix" +import "syscall" func (typ RIBType) parseable() bool { switch typ { - case unix.NET_RT_STAT, unix.NET_RT_TRASH: + case syscall.NET_RT_STAT, syscall.NET_RT_TRASH: return false default: return true @@ -52,38 +52,38 @@ func (m *InterfaceMessage) Sys() []Sys { } func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 36, bodyOff: unix.SizeofRtMsghdr} + rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15} rtm.parse = rtm.parseRouteMessage rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15} rtm2.parse = rtm2.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15} ifm.parse = ifm.parseInterfaceMessage ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15} ifm2.parse = ifm2.parseInterfaceMessage - ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} + ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15} ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} + ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15} ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifmam2 := &wireFormat{extOff: unix.SizeofIfmaMsghdr2, bodyOff: unix.SizeofIfmaMsghdr2} + ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15} ifmam2.parse = ifmam2.parseInterfaceMulticastAddrMessage // Darwin kernels require 32-bit aligned access to routing facilities. return 4, map[int]*wireFormat{ - unix.RTM_ADD: rtm, - unix.RTM_DELETE: rtm, - unix.RTM_CHANGE: rtm, - unix.RTM_GET: rtm, - unix.RTM_LOSING: rtm, - unix.RTM_REDIRECT: rtm, - unix.RTM_MISS: rtm, - unix.RTM_LOCK: rtm, - unix.RTM_RESOLVE: rtm, - unix.RTM_NEWADDR: ifam, - unix.RTM_DELADDR: ifam, - unix.RTM_IFINFO: ifm, - unix.RTM_NEWMADDR: ifmam, - unix.RTM_DELMADDR: ifmam, - unix.RTM_IFINFO2: ifm2, - unix.RTM_NEWMADDR2: ifmam2, - unix.RTM_GET2: rtm2, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + syscall.RTM_IFINFO2: ifm2, + syscall.RTM_NEWMADDR2: ifmam2, + syscall.RTM_GET2: rtm2, } } diff --git a/route/sys_dragonfly.go b/route/sys_dragonfly.go index 4fb118cd74..577fb16eb4 100644 --- a/route/sys_dragonfly.go +++ b/route/sys_dragonfly.go @@ -5,9 +5,8 @@ package route import ( + "syscall" "unsafe" - - "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { return true } @@ -50,42 +49,40 @@ func (m *InterfaceMessage) Sys() []Sys { func probeRoutingStack() (int, map[int]*wireFormat) { var p uintptr - rtm := &wireFormat{extOff: 40, bodyOff: unix.SizeofRtMsghdr} + rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4} rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4} ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfaMsghdr} + ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4} ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} + ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4} ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} + ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4} ifanm.parse = ifanm.parseInterfaceAnnounceMessage - rel, _ := unix.SysctlUint32("kern.osreldate") - if rel < 500705 { + rel, _ := syscall.SysctlUint32("kern.osreldate") + if rel >= 500705 { // https://github.com/DragonFlyBSD/DragonFlyBSD/commit/43a373152df2d405c9940983e584e6a25e76632d - // but only the size of struct ifa_msghdr actually changed. - // The type is not in current header files, - // so we just use constants here. + // but only the size of struct ifa_msghdr actually changed rtmVersion = 7 - ifam.bodyOff = 0x14 + ifam.bodyOff = sizeofIfaMsghdrDragonFlyBSD58 } return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - unix.RTM_ADD: rtm, - unix.RTM_DELETE: rtm, - unix.RTM_CHANGE: rtm, - unix.RTM_GET: rtm, - unix.RTM_LOSING: rtm, - unix.RTM_REDIRECT: rtm, - unix.RTM_MISS: rtm, - unix.RTM_LOCK: rtm, - unix.RTM_RESOLVE: rtm, - unix.RTM_NEWADDR: ifam, - unix.RTM_DELADDR: ifam, - unix.RTM_IFINFO: ifm, - unix.RTM_NEWMADDR: ifmam, - unix.RTM_DELMADDR: ifmam, - unix.RTM_IFANNOUNCE: ifanm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + syscall.RTM_IFANNOUNCE: ifanm, } } diff --git a/route/sys_freebsd.go b/route/sys_freebsd.go index 608f1a9307..0a66dcedb3 100644 --- a/route/sys_freebsd.go +++ b/route/sys_freebsd.go @@ -5,9 +5,8 @@ package route import ( + "syscall" "unsafe" - - "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { return true } @@ -65,7 +64,7 @@ func probeRoutingStack() (int, map[int]*wireFormat) { // to know the underlying kernel's architecture because the // alignment for routing facilities are set at the build time // of the kernel. - conf, _ := unix.Sysctl("kern.conftxt") + conf, _ := syscall.Sysctl("kern.conftxt") for i, j := 0, 0; j < len(conf); j++ { if conf[j] != '\n' { continue @@ -89,17 +88,21 @@ func probeRoutingStack() (int, map[int]*wireFormat) { if align != wordSize { compatFreeBSD32 = true // 386 emulation on amd64 } - var rtm *wireFormat - ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} - ifmam := &wireFormat{extOff: unix.SizeofIfmaMsghdr, bodyOff: unix.SizeofIfmaMsghdr} - ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} - ifm := &wireFormat{extOff: 16} + var rtm, ifm, ifam, ifmam, ifanm *wireFormat if compatFreeBSD32 { rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10Emu - sizeofRtMetricsFreeBSD10Emu, bodyOff: sizeofRtMsghdrFreeBSD10Emu} + ifm = &wireFormat{extOff: 16} + ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10Emu, bodyOff: sizeofIfaMsghdrFreeBSD10Emu} + ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10Emu, bodyOff: sizeofIfmaMsghdrFreeBSD10Emu} + ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10Emu, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10Emu} } else { rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10 - sizeofRtMetricsFreeBSD10, bodyOff: sizeofRtMsghdrFreeBSD10} + ifm = &wireFormat{extOff: 16} + ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10, bodyOff: sizeofIfaMsghdrFreeBSD10} + ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10, bodyOff: sizeofIfmaMsghdrFreeBSD10} + ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10} } - rel, _ := unix.SysctlUint32("kern.osreldate") + rel, _ := syscall.SysctlUint32("kern.osreldate") switch { case rel < 800000: if compatFreeBSD32 { @@ -138,20 +141,20 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage ifanm.parse = ifanm.parseInterfaceAnnounceMessage return align, map[int]*wireFormat{ - unix.RTM_ADD: rtm, - unix.RTM_DELETE: rtm, - unix.RTM_CHANGE: rtm, - unix.RTM_GET: rtm, - unix.RTM_LOSING: rtm, - unix.RTM_REDIRECT: rtm, - unix.RTM_MISS: rtm, - unix.RTM_LOCK: rtm, - unix.RTM_RESOLVE: rtm, - unix.RTM_NEWADDR: ifam, - unix.RTM_DELADDR: ifam, - unix.RTM_IFINFO: ifm, - unix.RTM_NEWMADDR: ifmam, - unix.RTM_DELMADDR: ifmam, - unix.RTM_IFANNOUNCE: ifanm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + syscall.RTM_IFANNOUNCE: ifanm, } } diff --git a/route/sys_netbsd.go b/route/sys_netbsd.go index 441458180a..be4460e13f 100644 --- a/route/sys_netbsd.go +++ b/route/sys_netbsd.go @@ -4,7 +4,7 @@ package route -import "golang.org/x/sys/unix" +import "syscall" func (typ RIBType) parseable() bool { return true } @@ -45,29 +45,29 @@ func (m *InterfaceMessage) Sys() []Sys { } func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 40, bodyOff: unix.SizeofRtMsghdr} + rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7} rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: unix.SizeofIfMsghdr} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7} ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: unix.SizeofIfaMsghdr, bodyOff: unix.SizeofIfaMsghdr} + ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7} ifam.parse = ifam.parseInterfaceAddrMessage - ifanm := &wireFormat{extOff: unix.SizeofIfAnnounceMsghdr, bodyOff: unix.SizeofIfAnnounceMsghdr} + ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7} ifanm.parse = ifanm.parseInterfaceAnnounceMessage // NetBSD 6 and above kernels require 64-bit aligned access to // routing facilities. return 8, map[int]*wireFormat{ - unix.RTM_ADD: rtm, - unix.RTM_DELETE: rtm, - unix.RTM_CHANGE: rtm, - unix.RTM_GET: rtm, - unix.RTM_LOSING: rtm, - unix.RTM_REDIRECT: rtm, - unix.RTM_MISS: rtm, - unix.RTM_LOCK: rtm, - unix.RTM_RESOLVE: rtm, - unix.RTM_NEWADDR: ifam, - unix.RTM_DELADDR: ifam, - unix.RTM_IFANNOUNCE: ifanm, - unix.RTM_IFINFO: ifm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFANNOUNCE: ifanm, + syscall.RTM_IFINFO: ifm, } } diff --git a/route/sys_openbsd.go b/route/sys_openbsd.go index 21f1d3d6e5..7f4f93cbea 100644 --- a/route/sys_openbsd.go +++ b/route/sys_openbsd.go @@ -5,14 +5,13 @@ package route import ( + "syscall" "unsafe" - - "golang.org/x/sys/unix" ) func (typ RIBType) parseable() bool { switch typ { - case unix.NET_RT_STATS, unix.NET_RT_TABLE: + case syscall.NET_RT_STATS, syscall.NET_RT_TABLE: return false default: return true @@ -66,18 +65,18 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifanm := &wireFormat{extOff: -1, bodyOff: -1} ifanm.parse = ifanm.parseInterfaceAnnounceMessage return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - unix.RTM_ADD: rtm, - unix.RTM_DELETE: rtm, - unix.RTM_CHANGE: rtm, - unix.RTM_GET: rtm, - unix.RTM_LOSING: rtm, - unix.RTM_REDIRECT: rtm, - unix.RTM_MISS: rtm, - unix.RTM_RESOLVE: rtm, - unix.RTM_NEWADDR: ifam, - unix.RTM_DELADDR: ifam, - unix.RTM_IFINFO: ifm, - unix.RTM_IFANNOUNCE: ifanm, - unix.RTM_DESYNC: rtm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_IFANNOUNCE: ifanm, + syscall.RTM_DESYNC: rtm, } } diff --git a/route/zsys_darwin.go b/route/zsys_darwin.go index 521be9f19f..56a0c66f44 100644 --- a/route/zsys_darwin.go +++ b/route/zsys_darwin.go @@ -4,8 +4,19 @@ package route const ( - sizeofIfMsghdr2Darwin15 = 0xa0 - sizeofIfData64Darwin15 = 0x80 + sizeofIfMsghdrDarwin15 = 0x70 + sizeofIfaMsghdrDarwin15 = 0x14 + sizeofIfmaMsghdrDarwin15 = 0x10 + sizeofIfMsghdr2Darwin15 = 0xa0 + sizeofIfmaMsghdr2Darwin15 = 0x14 + sizeofIfDataDarwin15 = 0x60 + sizeofIfData64Darwin15 = 0x80 + sizeofRtMsghdrDarwin15 = 0x5c sizeofRtMsghdr2Darwin15 = 0x5c + sizeofRtMetricsDarwin15 = 0x38 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_dragonfly.go b/route/zsys_dragonfly.go new file mode 100644 index 0000000000..f7c7a60cd6 --- /dev/null +++ b/route/zsys_dragonfly.go @@ -0,0 +1,20 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_dragonfly.go + +package route + +const ( + sizeofIfMsghdrDragonFlyBSD4 = 0xb0 + sizeofIfaMsghdrDragonFlyBSD4 = 0x14 + sizeofIfmaMsghdrDragonFlyBSD4 = 0x10 + sizeofIfAnnouncemsghdrDragonFlyBSD4 = 0x18 + + sizeofIfaMsghdrDragonFlyBSD58 = 0x18 + + sizeofRtMsghdrDragonFlyBSD4 = 0x98 + sizeofRtMetricsDragonFlyBSD4 = 0x70 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/route/zsys_freebsd_386.go b/route/zsys_freebsd_386.go index 37b5c7b3e3..3f985c7ee9 100644 --- a/route/zsys_freebsd_386.go +++ b/route/zsys_freebsd_386.go @@ -4,6 +4,12 @@ package route const ( + sizeofIfMsghdrlFreeBSD10 = 0x68 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0x6c + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + sizeofRtMsghdrFreeBSD10 = 0x5c sizeofRtMetricsFreeBSD10 = 0x38 @@ -22,6 +28,12 @@ const ( // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_amd64.go b/route/zsys_freebsd_amd64.go index 2f6cf35ff0..9293393698 100644 --- a/route/zsys_freebsd_amd64.go +++ b/route/zsys_freebsd_amd64.go @@ -4,6 +4,12 @@ package route const ( + sizeofIfMsghdrlFreeBSD10 = 0xb0 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0xb0 + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -19,6 +25,12 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_arm.go b/route/zsys_freebsd_arm.go index b6b90d55cf..a2bdb4ad3b 100644 --- a/route/zsys_freebsd_arm.go +++ b/route/zsys_freebsd_arm.go @@ -4,6 +4,12 @@ package route const ( + sizeofIfMsghdrlFreeBSD10 = 0x68 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0x6c + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + sizeofRtMsghdrFreeBSD10 = 0x5c sizeofRtMetricsFreeBSD10 = 0x38 @@ -19,6 +25,12 @@ const ( sizeofIfDataFreeBSD10 = 0x60 sizeofIfDataFreeBSD11 = 0x98 + sizeofIfMsghdrlFreeBSD10Emu = 0x68 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0x6c + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + sizeofRtMsghdrFreeBSD10Emu = 0x5c sizeofRtMetricsFreeBSD10Emu = 0x38 diff --git a/route/zsys_freebsd_arm64.go b/route/zsys_freebsd_arm64.go index 2f6cf35ff0..9293393698 100644 --- a/route/zsys_freebsd_arm64.go +++ b/route/zsys_freebsd_arm64.go @@ -4,6 +4,12 @@ package route const ( + sizeofIfMsghdrlFreeBSD10 = 0xb0 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0xb0 + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -19,6 +25,12 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_freebsd_riscv64.go b/route/zsys_freebsd_riscv64.go index 2f6cf35ff0..9293393698 100644 --- a/route/zsys_freebsd_riscv64.go +++ b/route/zsys_freebsd_riscv64.go @@ -4,6 +4,12 @@ package route const ( + sizeofIfMsghdrlFreeBSD10 = 0xb0 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0xb0 + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + sizeofRtMsghdrFreeBSD10 = 0x98 sizeofRtMetricsFreeBSD10 = 0x70 @@ -19,6 +25,12 @@ const ( sizeofIfDataFreeBSD10 = 0x98 sizeofIfDataFreeBSD11 = 0x98 + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + sizeofRtMsghdrFreeBSD10Emu = 0x98 sizeofRtMetricsFreeBSD10Emu = 0x70 diff --git a/route/zsys_netbsd.go b/route/zsys_netbsd.go new file mode 100644 index 0000000000..eaffe8c408 --- /dev/null +++ b/route/zsys_netbsd.go @@ -0,0 +1,17 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_netbsd.go + +package route + +const ( + sizeofIfMsghdrNetBSD7 = 0x98 + sizeofIfaMsghdrNetBSD7 = 0x18 + sizeofIfAnnouncemsghdrNetBSD7 = 0x18 + + sizeofRtMsghdrNetBSD7 = 0x78 + sizeofRtMetricsNetBSD7 = 0x50 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/route/zsys_openbsd.go b/route/zsys_openbsd.go new file mode 100644 index 0000000000..b11b812680 --- /dev/null +++ b/route/zsys_openbsd.go @@ -0,0 +1,12 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_openbsd.go + +package route + +const ( + sizeofRtMsghdr = 0x60 + + sizeofSockaddrStorage = 0x100 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) From 4ef7588d2b3f83775099797baac43c34e2e23200 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Fri, 6 Dec 2024 14:56:39 -0800 Subject: [PATCH 26/31] quic: handle ACK frame in packet which drops number space Avoid incorrectly closing a connection with a protocol violation error when we receive a single packet containing a CRYPTO frame that causes us to drop a packet number space (forgetting what packet numbers we've sent in that space) followed by an ACK frame. Fixes golang/go#70703 Change-Id: I37554cb6a3086736cb9d772f8a3441b544d414dc Reviewed-on: https://go-review.googlesource.com/c/net/+/634616 LUCI-TryBot-Result: Go LUCI Auto-Submit: Damien Neil Reviewed-by: Jonathan Amsterdam --- quic/conn_recv.go | 11 ++++++++ quic/conn_recv_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ quic/tls.go | 6 +---- 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 quic/conn_recv_test.go diff --git a/quic/conn_recv.go b/quic/conn_recv.go index b1354cd3a1..dbfe34a343 100644 --- a/quic/conn_recv.go +++ b/quic/conn_recv.go @@ -285,6 +285,7 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp __01 = packetType0RTT | packetType1RTT ___1 = packetType1RTT ) + hasCrypto := false for len(payload) > 0 { switch payload[0] { case frameTypePadding, frameTypeAck, frameTypeAckECN, @@ -322,6 +323,7 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp if !frameOK(c, ptype, IH_1) { return } + hasCrypto = true n = c.handleCryptoFrame(now, space, payload) case frameTypeNewToken: if !frameOK(c, ptype, ___1) { @@ -406,6 +408,15 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp } payload = payload[n:] } + if hasCrypto { + // Process TLS events after handling all frames in a packet. + // TLS events can cause us to drop state for a number space, + // so do that last, to avoid handling frames differently + // depending on whether they come before or after a CRYPTO frame. + if err := c.handleTLSEvents(now); err != nil { + c.abort(now, err) + } + } return ackEliciting } diff --git a/quic/conn_recv_test.go b/quic/conn_recv_test.go new file mode 100644 index 0000000000..0e94731bf7 --- /dev/null +++ b/quic/conn_recv_test.go @@ -0,0 +1,60 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.21 + +package quic + +import ( + "crypto/tls" + "testing" +) + +func TestConnReceiveAckForUnsentPacket(t *testing.T) { + tc := newTestConn(t, serverSide, permissiveTransportParameters) + tc.handshake() + tc.writeFrames(packetType1RTT, + debugFrameAck{ + ackDelay: 0, + ranges: []i64range[packetNumber]{{0, 10}}, + }) + tc.wantFrame("ACK for unsent packet causes CONNECTION_CLOSE", + packetType1RTT, debugFrameConnectionCloseTransport{ + code: errProtocolViolation, + }) +} + +// Issue #70703: If a packet contains both a CRYPTO frame which causes us to +// drop state for a number space, and also contains a valid ACK frame for that space, +// we shouldn't complain about the ACK. +func TestConnReceiveAckForDroppedSpace(t *testing.T) { + tc := newTestConn(t, serverSide, permissiveTransportParameters) + tc.ignoreFrame(frameTypeAck) + tc.ignoreFrame(frameTypeNewConnectionID) + + tc.writeFrames(packetTypeInitial, + debugFrameCrypto{ + data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial], + }) + tc.wantFrame("send Initial crypto", + packetTypeInitial, debugFrameCrypto{ + data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial], + }) + tc.wantFrame("send Handshake crypto", + packetTypeHandshake, debugFrameCrypto{ + data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake], + }) + + tc.writeFrames(packetTypeHandshake, + debugFrameCrypto{ + data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake], + }, + debugFrameAck{ + ackDelay: 0, + ranges: []i64range[packetNumber]{{0, tc.lastPacket.num + 1}}, + }) + tc.wantFrame("handshake finishes", + packetType1RTT, debugFrameHandshakeDone{}) + tc.wantIdle("connection is idle") +} diff --git a/quic/tls.go b/quic/tls.go index e2f2e5bde1..89b31842cd 100644 --- a/quic/tls.go +++ b/quic/tls.go @@ -119,11 +119,7 @@ func (c *Conn) handleCrypto(now time.Time, space numberSpace, off int64, data [] default: return errors.New("quic: internal error: received CRYPTO frame in unexpected number space") } - err := c.crypto[space].handleCrypto(off, data, func(b []byte) error { + return c.crypto[space].handleCrypto(off, data, func(b []byte) error { return c.tls.HandleData(level, b) }) - if err != nil { - return err - } - return c.handleTLSEvents(now) } From 6705db9a4df8f2cf16aed83e773e7a0213788b7a Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 9 Dec 2024 13:05:39 -0800 Subject: [PATCH 27/31] quic: clean up crypto streams when dropping packet protection keys When dropping packet protection keys for a number space: Check to see if there is unused CRYPTO data received from the peer in the space. If so, close the connection with an error. This can only happen if the peer has sent us data with a gap in it. We can never read the data that fills that gap (because we're dropping the key it would be encrypted with), and this situation cannot happen without the peer sending invalid TLS handshake data. Drop any buffered CRYPTO data being sent to the peer. Under normal operations, we may have data that was sent to the peer but which we haven't received an ACK for yet. The peer has received the data (or we wouldn't be dropping the number space) and we will never see the ACK (because we're dropping the key it would be encrypted with). Fixes golang/go#70704 Change-Id: I53380169cb59a2a6f87e69b38522ba81ad38c2b0 Reviewed-on: https://go-review.googlesource.com/c/net/+/634617 LUCI-TryBot-Result: Go LUCI Reviewed-by: Jonathan Amsterdam Auto-Submit: Damien Neil --- quic/conn.go | 3 +++ quic/crypto_stream.go | 18 ++++++++++++++++++ quic/tls_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/quic/conn.go b/quic/conn.go index fbd8b84348..bf54409bfe 100644 --- a/quic/conn.go +++ b/quic/conn.go @@ -216,6 +216,9 @@ func (c *Conn) confirmHandshake(now time.Time) { // discardKeys discards unused packet protection keys. // https://www.rfc-editor.org/rfc/rfc9001#section-4.9 func (c *Conn) discardKeys(now time.Time, space numberSpace) { + if err := c.crypto[space].discardKeys(); err != nil { + c.abort(now, err) + } switch space { case initialSpace: c.keysInitial.discard() diff --git a/quic/crypto_stream.go b/quic/crypto_stream.go index a4dcb32eb7..806c963943 100644 --- a/quic/crypto_stream.go +++ b/quic/crypto_stream.go @@ -139,3 +139,21 @@ func (s *cryptoStream) sendData(off int64, b []byte) { s.out.copy(off, b) s.outunsent.sub(off, off+int64(len(b))) } + +// discardKeys is called when the packet protection keys for the stream are dropped. +func (s *cryptoStream) discardKeys() error { + if s.in.end-s.in.start != 0 { + // The peer sent some unprocessed CRYPTO data that we're about to discard. + // Close the connetion with a TLS unexpected_message alert. + // https://www.rfc-editor.org/rfc/rfc5246#section-7.2.2 + const unexpectedMessage = 10 + return localTransportError{ + code: errTLSBase + unexpectedMessage, + reason: "excess crypto data", + } + } + // Discard any unacked (but presumably received) data in our output buffer. + s.out.discardBefore(s.out.end) + *s = cryptoStream{} + return nil +} diff --git a/quic/tls_test.go b/quic/tls_test.go index 9c1dd364ec..f4abdda582 100644 --- a/quic/tls_test.go +++ b/quic/tls_test.go @@ -615,3 +615,32 @@ func TestConnAEADLimitReached(t *testing.T) { tc.advance(1 * time.Second) tc.wantIdle("auth failures at limit: conn does not process additional packets") } + +func TestConnKeysDiscardedWithExcessCryptoData(t *testing.T) { + tc := newTestConn(t, serverSide, permissiveTransportParameters) + tc.ignoreFrame(frameTypeAck) + tc.ignoreFrame(frameTypeNewConnectionID) + tc.ignoreFrame(frameTypeCrypto) + + // One byte of excess CRYPTO data, separated from the valid data by a one-byte gap. + tc.writeFrames(packetTypeInitial, + debugFrameCrypto{ + off: int64(len(tc.cryptoDataIn[tls.QUICEncryptionLevelInitial]) + 1), + data: []byte{0}, + }) + tc.writeFrames(packetTypeInitial, + debugFrameCrypto{ + data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial], + }) + + // We don't drop the Initial keys and discover the excess data until the client + // sends a Handshake packet. + tc.writeFrames(packetTypeHandshake, + debugFrameCrypto{ + data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake], + }) + tc.wantFrame("connection closed due to excess Initial CRYPTO data", + packetType1RTT, debugFrameConnectionCloseTransport{ + code: errTLSBase + 10, + }) +} From 9af49ef148d7d8b3e4cbbd9cc0cd37f2a520a4a3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 11 Dec 2024 14:08:51 +0100 Subject: [PATCH 28/31] route: remove unused sizeof* consts These were never used since they were added in CL 22446. Cq-Include-Trybots: luci.golang.try:x_net-gotip-freebsd-amd64,x_net-gotip-darwin-arm64_13 Change-Id: I1d5952ec299ea22f2f0c62da552638ba0438a15b Reviewed-on: https://go-review.googlesource.com/c/net/+/635135 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Reviewed-by: Carlos Amedee Auto-Submit: Tobias Klauser --- route/defs_darwin.go | 8 ++------ route/defs_dragonfly.go | 8 +++----- route/defs_freebsd.go | 20 ++------------------ route/defs_netbsd.go | 8 +++----- route/defs_openbsd.go | 5 ++--- route/zsys_darwin.go | 8 ++------ route/zsys_dragonfly.go | 8 +++----- route/zsys_freebsd_386.go | 20 ++------------------ route/zsys_freebsd_amd64.go | 20 ++------------------ route/zsys_freebsd_arm.go | 20 ++------------------ route/zsys_freebsd_arm64.go | 20 ++------------------ route/zsys_freebsd_riscv64.go | 20 ++------------------ route/zsys_netbsd.go | 8 +++----- route/zsys_openbsd.go | 5 ++--- 14 files changed, 32 insertions(+), 146 deletions(-) diff --git a/route/defs_darwin.go b/route/defs_darwin.go index ec56ca02e1..46a4ed6694 100644 --- a/route/defs_darwin.go +++ b/route/defs_darwin.go @@ -24,14 +24,10 @@ const ( sizeofIfmaMsghdrDarwin15 = C.sizeof_struct_ifma_msghdr sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 sizeofIfmaMsghdr2Darwin15 = C.sizeof_struct_ifma_msghdr2 - sizeofIfDataDarwin15 = C.sizeof_struct_if_data - sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 sizeofRtMsghdrDarwin15 = C.sizeof_struct_rt_msghdr sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2 - sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_dragonfly.go b/route/defs_dragonfly.go index 9bf202dda4..52aa700a6d 100644 --- a/route/defs_dragonfly.go +++ b/route/defs_dragonfly.go @@ -47,10 +47,8 @@ const ( sizeofIfaMsghdrDragonFlyBSD58 = C.sizeof_struct_ifa_msghdr_dfly58 - sizeofRtMsghdrDragonFlyBSD4 = C.sizeof_struct_rt_msghdr - sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics + sizeofRtMsghdrDragonFlyBSD4 = C.sizeof_struct_rt_msghdr - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_freebsd.go b/route/defs_freebsd.go index abb2dc0957..68778f2d16 100644 --- a/route/defs_freebsd.go +++ b/route/defs_freebsd.go @@ -220,7 +220,6 @@ import "C" const ( sizeofIfMsghdrlFreeBSD10 = C.sizeof_struct_if_msghdrl sizeofIfaMsghdrFreeBSD10 = C.sizeof_struct_ifa_msghdr - sizeofIfaMsghdrlFreeBSD10 = C.sizeof_struct_ifa_msghdrl sizeofIfmaMsghdrFreeBSD10 = C.sizeof_struct_ifma_msghdr sizeofIfAnnouncemsghdrFreeBSD10 = C.sizeof_struct_if_announcemsghdr @@ -233,15 +232,7 @@ const ( sizeofIfMsghdrFreeBSD10 = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11 = C.sizeof_struct_if_msghdr_freebsd11 - sizeofIfDataFreeBSD7 = C.sizeof_struct_if_data_freebsd7 - sizeofIfDataFreeBSD8 = C.sizeof_struct_if_data_freebsd8 - sizeofIfDataFreeBSD9 = C.sizeof_struct_if_data_freebsd9 - sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 - sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 - - sizeofIfMsghdrlFreeBSD10Emu = C.sizeof_struct_if_msghdrl sizeofIfaMsghdrFreeBSD10Emu = C.sizeof_struct_ifa_msghdr - sizeofIfaMsghdrlFreeBSD10Emu = C.sizeof_struct_ifa_msghdrl sizeofIfmaMsghdrFreeBSD10Emu = C.sizeof_struct_ifma_msghdr sizeofIfAnnouncemsghdrFreeBSD10Emu = C.sizeof_struct_if_announcemsghdr @@ -254,13 +245,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = C.sizeof_struct_if_msghdr_freebsd10 sizeofIfMsghdrFreeBSD11Emu = C.sizeof_struct_if_msghdr_freebsd11 - sizeofIfDataFreeBSD7Emu = C.sizeof_struct_if_data_freebsd7 - sizeofIfDataFreeBSD8Emu = C.sizeof_struct_if_data_freebsd8 - sizeofIfDataFreeBSD9Emu = C.sizeof_struct_if_data_freebsd9 - sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10 - sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11 - - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_netbsd.go b/route/defs_netbsd.go index 8e89934c5a..fb60f43c83 100644 --- a/route/defs_netbsd.go +++ b/route/defs_netbsd.go @@ -23,10 +23,8 @@ const ( sizeofIfaMsghdrNetBSD7 = C.sizeof_struct_ifa_msghdr sizeofIfAnnouncemsghdrNetBSD7 = C.sizeof_struct_if_announcemsghdr - sizeofRtMsghdrNetBSD7 = C.sizeof_struct_rt_msghdr - sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics + sizeofRtMsghdrNetBSD7 = C.sizeof_struct_rt_msghdr - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/defs_openbsd.go b/route/defs_openbsd.go index 8f3218bc63..471558d9ef 100644 --- a/route/defs_openbsd.go +++ b/route/defs_openbsd.go @@ -21,7 +21,6 @@ import "C" const ( sizeofRtMsghdr = C.sizeof_struct_rt_msghdr - sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage - sizeofSockaddrInet = C.sizeof_struct_sockaddr_in - sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 ) diff --git a/route/zsys_darwin.go b/route/zsys_darwin.go index 56a0c66f44..adaa460026 100644 --- a/route/zsys_darwin.go +++ b/route/zsys_darwin.go @@ -9,14 +9,10 @@ const ( sizeofIfmaMsghdrDarwin15 = 0x10 sizeofIfMsghdr2Darwin15 = 0xa0 sizeofIfmaMsghdr2Darwin15 = 0x14 - sizeofIfDataDarwin15 = 0x60 - sizeofIfData64Darwin15 = 0x80 sizeofRtMsghdrDarwin15 = 0x5c sizeofRtMsghdr2Darwin15 = 0x5c - sizeofRtMetricsDarwin15 = 0x38 - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_dragonfly.go b/route/zsys_dragonfly.go index f7c7a60cd6..209cb20af8 100644 --- a/route/zsys_dragonfly.go +++ b/route/zsys_dragonfly.go @@ -11,10 +11,8 @@ const ( sizeofIfaMsghdrDragonFlyBSD58 = 0x18 - sizeofRtMsghdrDragonFlyBSD4 = 0x98 - sizeofRtMetricsDragonFlyBSD4 = 0x70 + sizeofRtMsghdrDragonFlyBSD4 = 0x98 - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_386.go b/route/zsys_freebsd_386.go index 3f985c7ee9..ec617772b2 100644 --- a/route/zsys_freebsd_386.go +++ b/route/zsys_freebsd_386.go @@ -6,7 +6,6 @@ package route const ( sizeofIfMsghdrlFreeBSD10 = 0x68 sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c sizeofIfmaMsghdrFreeBSD10 = 0x10 sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 @@ -19,18 +18,10 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x64 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x50 - sizeofIfDataFreeBSD8 = 0x50 - sizeofIfDataFreeBSD9 = 0x50 - sizeofIfDataFreeBSD10 = 0x54 - sizeofIfDataFreeBSD11 = 0x98 - // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 sizeofIfmaMsghdrFreeBSD10Emu = 0x10 sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 @@ -43,13 +34,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_amd64.go b/route/zsys_freebsd_amd64.go index 9293393698..3d7f31d13e 100644 --- a/route/zsys_freebsd_amd64.go +++ b/route/zsys_freebsd_amd64.go @@ -6,7 +6,6 @@ package route const ( sizeofIfMsghdrlFreeBSD10 = 0xb0 sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 sizeofIfmaMsghdrFreeBSD10 = 0x10 sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 @@ -19,15 +18,7 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 sizeofIfmaMsghdrFreeBSD10Emu = 0x10 sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 @@ -40,13 +31,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm.go b/route/zsys_freebsd_arm.go index a2bdb4ad3b..931afa3931 100644 --- a/route/zsys_freebsd_arm.go +++ b/route/zsys_freebsd_arm.go @@ -6,7 +6,6 @@ package route const ( sizeofIfMsghdrlFreeBSD10 = 0x68 sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c sizeofIfmaMsghdrFreeBSD10 = 0x10 sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 @@ -19,15 +18,7 @@ const ( sizeofIfMsghdrFreeBSD10 = 0x70 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x60 - sizeofIfDataFreeBSD8 = 0x60 - sizeofIfDataFreeBSD9 = 0x60 - sizeofIfDataFreeBSD10 = 0x60 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0x68 sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0x6c sizeofIfmaMsghdrFreeBSD10Emu = 0x10 sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 @@ -40,13 +31,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = 0x70 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - sizeofIfDataFreeBSD7Emu = 0x60 - sizeofIfDataFreeBSD8Emu = 0x60 - sizeofIfDataFreeBSD9Emu = 0x60 - sizeofIfDataFreeBSD10Emu = 0x60 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_arm64.go b/route/zsys_freebsd_arm64.go index 9293393698..3d7f31d13e 100644 --- a/route/zsys_freebsd_arm64.go +++ b/route/zsys_freebsd_arm64.go @@ -6,7 +6,6 @@ package route const ( sizeofIfMsghdrlFreeBSD10 = 0xb0 sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 sizeofIfmaMsghdrFreeBSD10 = 0x10 sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 @@ -19,15 +18,7 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 sizeofIfmaMsghdrFreeBSD10Emu = 0x10 sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 @@ -40,13 +31,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_freebsd_riscv64.go b/route/zsys_freebsd_riscv64.go index 9293393698..3d7f31d13e 100644 --- a/route/zsys_freebsd_riscv64.go +++ b/route/zsys_freebsd_riscv64.go @@ -6,7 +6,6 @@ package route const ( sizeofIfMsghdrlFreeBSD10 = 0xb0 sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 sizeofIfmaMsghdrFreeBSD10 = 0x10 sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 @@ -19,15 +18,7 @@ const ( sizeofIfMsghdrFreeBSD10 = 0xa8 sizeofIfMsghdrFreeBSD11 = 0xa8 - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 sizeofIfmaMsghdrFreeBSD10Emu = 0x10 sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 @@ -40,13 +31,6 @@ const ( sizeofIfMsghdrFreeBSD10Emu = 0xa8 sizeofIfMsghdrFreeBSD11Emu = 0xa8 - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_netbsd.go b/route/zsys_netbsd.go index eaffe8c408..90ce707d47 100644 --- a/route/zsys_netbsd.go +++ b/route/zsys_netbsd.go @@ -8,10 +8,8 @@ const ( sizeofIfaMsghdrNetBSD7 = 0x18 sizeofIfAnnouncemsghdrNetBSD7 = 0x18 - sizeofRtMsghdrNetBSD7 = 0x78 - sizeofRtMetricsNetBSD7 = 0x50 + sizeofRtMsghdrNetBSD7 = 0x78 - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) diff --git a/route/zsys_openbsd.go b/route/zsys_openbsd.go index b11b812680..64fbdd98fb 100644 --- a/route/zsys_openbsd.go +++ b/route/zsys_openbsd.go @@ -6,7 +6,6 @@ package route const ( sizeofRtMsghdr = 0x60 - sizeofSockaddrStorage = 0x100 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c ) From b935f7b5d723c82894e1a1fd936a94dd2d1eae46 Mon Sep 17 00:00:00 2001 From: yincong Date: Wed, 18 Dec 2024 02:15:59 +0000 Subject: [PATCH 29/31] html: avoid endless loop on error token Fixes #70179 Change-Id: I2a0a1fc2e96f7d8eefd0abdf7ef8ba243a6e8645 GitHub-Last-Rev: a601ecd8498cf5e2e9911d59a194795a21c539ef GitHub-Pull-Request: golang/net#226 Reviewed-on: https://go-review.googlesource.com/c/net/+/624895 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI --- html/parse.go | 4 ++++ html/parse_test.go | 1 + 2 files changed, 5 insertions(+) diff --git a/html/parse.go b/html/parse.go index 46a89eda6c..d67705d9e8 100644 --- a/html/parse.go +++ b/html/parse.go @@ -840,6 +840,10 @@ func afterHeadIM(p *parser) bool { p.parseImpliedToken(StartTagToken, a.Body, a.Body.String()) p.framesetOK = true + if p.tok.Type == ErrorToken { + // Stop parsing. + return true + } return false } diff --git a/html/parse_test.go b/html/parse_test.go index aa91f4c2b3..24b352e2ad 100644 --- a/html/parse_test.go +++ b/html/parse_test.go @@ -468,6 +468,7 @@ func TestParseFragmentForeignContentTemplates(t *testing.T) { srcs := []string{ "", "