1// Copyright 2019 The gRPC Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package http2interop 16 17import ( 18 "crypto/tls" 19 "crypto/x509" 20 "fmt" 21 "io" 22 "net" 23 "testing" 24 "time" 25) 26 27const ( 28 Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 29) 30 31var ( 32 defaultTimeout = 1 * time.Second 33) 34 35type HTTP2InteropCtx struct { 36 // Inputs 37 ServerHost string 38 ServerPort int 39 UseTLS bool 40 UseTestCa bool 41 ServerHostnameOverride string 42 43 T *testing.T 44 45 // Derived 46 serverSpec string 47 authority string 48 rootCAs *x509.CertPool 49} 50 51func parseFrame(r io.Reader) (Frame, error) { 52 fh := FrameHeader{} 53 if err := fh.Parse(r); err != nil { 54 return nil, err 55 } 56 var f Frame 57 switch fh.Type { 58 case PingFrameType: 59 f = &PingFrame{ 60 Header: fh, 61 } 62 case SettingsFrameType: 63 f = &SettingsFrame{ 64 Header: fh, 65 } 66 case HTTP1FrameType: 67 f = &HTTP1Frame{ 68 Header: fh, 69 } 70 default: 71 f = &UnknownFrame{ 72 Header: fh, 73 } 74 } 75 76 if err := f.ParsePayload(r); err != nil { 77 return nil, err 78 } 79 80 return f, nil 81} 82 83func streamFrame(w io.Writer, f Frame) error { 84 raw, err := f.MarshalBinary() 85 if err != nil { 86 return err 87 } 88 if _, err := w.Write(raw); err != nil { 89 return err 90 } 91 return nil 92} 93 94func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error { 95 conn, err := connect(ctx) 96 if err != nil { 97 return err 98 } 99 defer conn.Close() 100 conn.SetDeadline(time.Now().Add(defaultTimeout)) 101 102 if _, err := conn.Write([]byte(Preface)); err != nil { 103 return err 104 } 105 106 // Bad, settings, non multiple of 6 107 sf := &UnknownFrame{ 108 Header: FrameHeader{ 109 Type: SettingsFrameType, 110 }, 111 Data: make([]byte, length), 112 } 113 if err := streamFrame(conn, sf); err != nil { 114 ctx.T.Log("Unable to stream frame", sf) 115 return err 116 } 117 118 if _, err := expectGoAwaySoon(conn); err != nil { 119 return err 120 } 121 122 return nil 123} 124 125func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error { 126 conn, err := connect(ctx) 127 if err != nil { 128 return err 129 } 130 defer conn.Close() 131 conn.SetDeadline(time.Now().Add(defaultTimeout)) 132 133 // Good so far 134 if _, err := conn.Write([]byte(Preface)); err != nil { 135 return err 136 } 137 138 // Bad, settings do not have ids 139 sf := &SettingsFrame{ 140 Header: FrameHeader{ 141 StreamID: 1, 142 }, 143 } 144 if err := streamFrame(conn, sf); err != nil { 145 return err 146 } 147 148 if _, err := expectGoAwaySoon(conn); err != nil { 149 return err 150 } 151 return nil 152} 153 154func testUnknownFrameType(ctx *HTTP2InteropCtx) error { 155 conn, err := connect(ctx) 156 if err != nil { 157 return err 158 } 159 defer conn.Close() 160 conn.SetDeadline(time.Now().Add(defaultTimeout)) 161 162 if err := http2Connect(conn, nil); err != nil { 163 return err 164 } 165 166 // Write a bunch of invalid frame types. 167 // Frame number 11 is the upcoming ALTSVC frame, and should not be tested. 168 for ft := ContinuationFrameType + 2; ft != 0; ft++ { 169 fh := &UnknownFrame{ 170 Header: FrameHeader{ 171 Type: ft, 172 }, 173 } 174 if err := streamFrame(conn, fh); err != nil { 175 ctx.T.Log("Unable to stream frame", fh) 176 return err 177 } 178 } 179 180 pf := &PingFrame{ 181 Data: []byte("01234567"), 182 } 183 if err := streamFrame(conn, pf); err != nil { 184 ctx.T.Log("Unable to stream frame", pf) 185 return err 186 } 187 188 for { 189 frame, err := parseFrame(conn) 190 if err != nil { 191 ctx.T.Log("Unable to parse frame", err) 192 return err 193 } 194 if npf, ok := frame.(*PingFrame); !ok { 195 ctx.T.Log("Got frame", frame.GetHeader().Type) 196 continue 197 } else { 198 if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 { 199 return fmt.Errorf("Bad ping %+v", *npf) 200 } 201 return nil 202 } 203 } 204 205 return nil 206} 207 208func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error { 209 conn, err := connect(ctx) 210 if err != nil { 211 return err 212 } 213 defer conn.Close() 214 conn.SetDeadline(time.Now().Add(defaultTimeout)) 215 216 if _, err := conn.Write([]byte(prefacePrefix)); err != nil { 217 return err 218 } 219 220 if _, err := expectGoAwaySoon(conn); err != nil { 221 return err 222 } 223 224 return nil 225} 226 227func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error { 228 config := buildTlsConfig(ctx) 229 config.MaxVersion = version 230 conn, err := connectWithTls(ctx, config) 231 if err != nil { 232 return err 233 } 234 defer conn.Close() 235 conn.SetDeadline(time.Now().Add(defaultTimeout)) 236 237 if err := http2Connect(conn, nil); err != nil { 238 return err 239 } 240 241 gf, err := expectGoAway(conn) 242 if err != nil { 243 return err 244 } 245 // TODO: make an enum out of this 246 if gf.Code != 0xC { 247 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 248 } 249 return nil 250} 251 252func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error { 253 config := buildTlsConfig(ctx) 254 config.NextProtos = []string{"h2c"} 255 conn, err := connectWithTls(ctx, config) 256 if err != nil { 257 return err 258 } 259 defer conn.Close() 260 conn.SetDeadline(time.Now().Add(defaultTimeout)) 261 262 if err := http2Connect(conn, nil); err != nil { 263 return err 264 } 265 266 gf, err := expectGoAway(conn) 267 if err != nil { 268 return err 269 } 270 // TODO: make an enum out of this 271 if gf.Code != 0xC { 272 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 273 } 274 return nil 275} 276 277func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error { 278 config := buildTlsConfig(ctx) 279 // These are the suites that Go supports, but are forbidden by http2. 280 config.CipherSuites = []uint16{ 281 tls.TLS_RSA_WITH_RC4_128_SHA, 282 tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 283 tls.TLS_RSA_WITH_AES_128_CBC_SHA, 284 tls.TLS_RSA_WITH_AES_256_CBC_SHA, 285 tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 286 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 287 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 288 tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 289 tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 290 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 291 tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 292 } 293 conn, err := connectWithTls(ctx, config) 294 if err != nil { 295 return err 296 } 297 defer conn.Close() 298 conn.SetDeadline(time.Now().Add(defaultTimeout)) 299 300 if err := http2Connect(conn, nil); err != nil { 301 return err 302 } 303 304 gf, err := expectGoAway(conn) 305 if err != nil { 306 return err 307 } 308 // TODO: make an enum out of this 309 if gf.Code != 0xC { 310 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 311 } 312 return nil 313} 314 315func expectGoAway(conn net.Conn) (*GoAwayFrame, error) { 316 f, err := parseFrame(conn) 317 if err != nil { 318 return nil, err 319 } 320 if gf, ok := f.(*GoAwayFrame); !ok { 321 return nil, fmt.Errorf("Expected GoAway Frame %+v", f) 322 } else { 323 return gf, nil 324 } 325} 326 327// expectGoAwaySoon checks that a GOAWAY frame eventually comes. Servers usually send 328// the initial settings frames before any data has actually arrived. This function 329// checks that a go away shows. 330func expectGoAwaySoon(conn net.Conn) (*GoAwayFrame, error) { 331 for { 332 f, err := parseFrame(conn) 333 if err != nil { 334 return nil, err 335 } 336 if gf, ok := f.(*GoAwayFrame); !ok { 337 continue 338 } else { 339 return gf, nil 340 } 341 } 342} 343 344func http2Connect(c net.Conn, sf *SettingsFrame) error { 345 if _, err := c.Write([]byte(Preface)); err != nil { 346 return err 347 } 348 349 if sf == nil { 350 sf = &SettingsFrame{} 351 } 352 if err := streamFrame(c, sf); err != nil { 353 return err 354 } 355 return nil 356} 357 358// CapConn captures connection traffic if Log is non-nil 359type CapConn struct { 360 net.Conn 361 Log func(args ...interface{}) 362} 363 364func (c *CapConn) Write(data []byte) (int, error) { 365 if c.Log != nil { 366 c.Log(" SEND: ", data) 367 } 368 return c.Conn.Write(data) 369} 370 371func (c *CapConn) Read(data []byte) (int, error) { 372 n, err := c.Conn.Read(data) 373 if c.Log != nil { 374 c.Log(" RECV: ", data[:n], err) 375 } 376 return n, err 377} 378 379func connect(ctx *HTTP2InteropCtx) (*CapConn, error) { 380 var conn *CapConn 381 var err error 382 if !ctx.UseTLS { 383 conn, err = connectWithoutTls(ctx) 384 } else { 385 config := buildTlsConfig(ctx) 386 conn, err = connectWithTls(ctx, config) 387 } 388 if err != nil { 389 return nil, err 390 } 391 conn.SetDeadline(time.Now().Add(defaultTimeout)) 392 393 return conn, nil 394} 395 396func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config { 397 return &tls.Config{ 398 RootCAs: ctx.rootCAs, 399 NextProtos: []string{"h2"}, 400 ServerName: ctx.authority, 401 MinVersion: tls.VersionTLS12, 402 } 403} 404 405func connectWithoutTls(ctx *HTTP2InteropCtx) (*CapConn, error) { 406 conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout) 407 if err != nil { 408 return nil, err 409 } 410 return &CapConn{Conn: conn}, nil 411} 412 413func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*CapConn, error) { 414 conn, err := connectWithoutTls(ctx) 415 if err != nil { 416 return nil, err 417 } 418 419 return &CapConn{Conn: tls.Client(conn, config)}, nil 420} 421