save version numbers such that can be written in big endian

This makes the version number representation consistent with the IETF
draft.
This commit is contained in:
Marten Seemann 2017-10-24 10:45:08 +07:00
parent e81795e49b
commit d98a11bb35
18 changed files with 68 additions and 65 deletions

View file

@ -7,7 +7,7 @@ import (
"net"
"net/http"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@ -280,12 +280,11 @@ func (s *Server) SetQuicHeaders(hdr http.Header) error {
}
if s.supportedVersionsAsString == "" {
for i, v := range protocol.SupportedVersions {
s.supportedVersionsAsString += strconv.Itoa(int(v))
if i != len(protocol.SupportedVersions)-1 {
s.supportedVersionsAsString += ","
}
var versions []string
for _, v := range protocol.SupportedVersions {
versions = append(versions, v.ToAltSvc())
}
s.supportedVersionsAsString = strings.Join(versions, ",")
}
hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))

View file

@ -9,7 +9,6 @@ import (
"io"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
@ -360,7 +359,7 @@ var _ = Describe("H2 server", func() {
getExpectedHeader := func(versions []protocol.VersionNumber) http.Header {
var versionsAsString []string
for _, v := range versions {
versionsAsString = append(versionsAsString, strconv.Itoa(int(v)))
versionsAsString = append(versionsAsString, v.ToAltSvc())
}
return http.Header{
"Alt-Svc": {fmt.Sprintf(`quic=":443"; ma=2592000; v="%s"`, strings.Join(versionsAsString, ","))},

View file

@ -140,7 +140,7 @@ func chromeTestImpl(version protocol.VersionNumber, url string, blockUntilDone f
"--no-proxy-server=true",
"--origin-to-force-quic-on=quic.clemente.io:443",
fmt.Sprintf(`--host-resolver-rules=MAP quic.clemente.io:443 localhost:%s`, testserver.Port()),
fmt.Sprintf("--quic-version=QUIC_VERSION_%d", version),
fmt.Sprintf("--quic-version=QUIC_VERSION_%s", version.ToAltSvc()),
url,
}
utils.Infof("Running chrome: %s '%s'", getChromePath(), strings.Join(args, "' '"))

View file

@ -35,7 +35,7 @@ var _ = Describe("Drop tests", func() {
downloadFile := func(version protocol.VersionNumber) {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+strconv.Itoa(proxy.LocalPort()),
"https://quic.clemente.io/prdata",
@ -50,7 +50,7 @@ var _ = Describe("Drop tests", func() {
downloadHello := func(version protocol.VersionNumber) {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+strconv.Itoa(proxy.LocalPort()),
"https://quic.clemente.io/hello",

View file

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"os/exec"
"strconv"
"sync"
"github.com/lucas-clemente/quic-go/integrationtests/tools/testserver"
@ -26,7 +25,7 @@ var _ = Describe("Integration tests", func() {
It("gets a simple file", func() {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+testserver.Port(),
"https://quic.clemente.io/hello",
@ -42,7 +41,7 @@ var _ = Describe("Integration tests", func() {
It("posts and reads a body", func() {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+testserver.Port(),
"--body=foo",
@ -59,7 +58,7 @@ var _ = Describe("Integration tests", func() {
It("gets a file", func() {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+testserver.Port(),
"https://quic.clemente.io/prdata",
@ -80,7 +79,7 @@ var _ = Describe("Integration tests", func() {
defer GinkgoRecover()
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+testserver.Port(),
"https://quic.clemente.io/prdata",

View file

@ -66,7 +66,7 @@ var _ = Describe("Random RTT", func() {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+strconv.Itoa(proxy.LocalPort()),
"https://quic.clemente.io/prdata",

View file

@ -32,7 +32,7 @@ var _ = Describe("non-zero RTT", func() {
command := exec.Command(
clientPath,
"--quic-version="+strconv.Itoa(int(version)),
"--quic-version="+version.ToAltSvc(),
"--host=127.0.0.1",
"--port="+strconv.Itoa(proxy.LocalPort()),
"https://quic.clemente.io/prdata",

View file

@ -288,11 +288,11 @@ func (h *cryptoSetupClient) validateVersionList(verTags []byte) bool {
b := bytes.NewReader(verTags)
for _, negotiatedVersion := range h.negotiatedVersions {
verTag, err := utils.LittleEndian.ReadUint32(b)
verTag, err := utils.BigEndian.ReadUint32(b)
if err != nil { // should never occur, since the length was already checked
return false
}
ver := protocol.VersionTagToNumber(verTag)
ver := protocol.VersionNumber(verTag)
if !protocol.IsSupportedVersion(protocol.SupportedVersions, ver) {
ver = protocol.VersionUnsupported
}
@ -418,7 +418,7 @@ func (h *cryptoSetupClient) getTags() (map[Tag][]byte, error) {
}
versionTag := make([]byte, 4)
binary.LittleEndian.PutUint32(versionTag, protocol.VersionNumberToTag(h.version))
binary.BigEndian.PutUint32(versionTag, uint32(h.version))
tags[TagVER] = versionTag
if len(h.stk) > 0 {

View file

@ -205,7 +205,7 @@ var _ = Describe("Client Crypto Setup", func() {
It("detects a downgrade attack", func() {
cs.negotiatedVersions = []protocol.VersionNumber{12}
b := &bytes.Buffer{}
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(11))
utils.BigEndian.WriteUint32(b, 11)
Expect(cs.validateVersionList(b.Bytes())).To(BeFalse())
})
@ -219,7 +219,7 @@ var _ = Describe("Client Crypto Setup", func() {
cs.negotiatedVersions = []protocol.VersionNumber{protocol.VersionUnsupported, ver, protocol.VersionUnsupported}
b := &bytes.Buffer{}
b.Write([]byte{0, 0, 0, 0})
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(ver))
utils.BigEndian.WriteUint32(b, uint32(ver))
b.Write([]byte{0x13, 0x37, 0x13, 0x37})
Expect(cs.validateVersionList(b.Bytes())).To(BeTrue())
})
@ -415,7 +415,7 @@ var _ = Describe("Client Crypto Setup", func() {
cs.negotiatedVersions = []protocol.VersionNumber{ver}
cs.receivedSecurePacket = true
b := &bytes.Buffer{}
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(ver))
utils.BigEndian.WriteUint32(b, uint32(ver))
shloMap[TagVER] = b.Bytes()
_, err := cs.handleSHLOMessage(shloMap)
Expect(err).ToNot(HaveOccurred())

View file

@ -145,8 +145,7 @@ func (h *cryptoSetupServer) handleMessage(chloData []byte, cryptoData map[Tag][]
if len(verSlice) != 4 {
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "incorrect version tag")
}
verTag := binary.LittleEndian.Uint32(verSlice)
ver := protocol.VersionTagToNumber(verTag)
ver := protocol.VersionNumber(binary.BigEndian.Uint32(verSlice))
// If the client's preferred version is not the version we are currently speaking, then the client went through a version negotiation. In this case, we need to make sure that we actually do not support this version and that it wasn't a downgrade attack.
if ver != h.version && protocol.IsSupportedVersion(h.supportedVersions, ver) {
return false, qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")
@ -429,7 +428,7 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T
// add crypto parameters
verTag := &bytes.Buffer{}
for _, v := range h.supportedVersions {
utils.LittleEndian.WriteUint32(verTag, protocol.VersionNumberToTag(v))
utils.BigEndian.WriteUint32(verTag, uint32(v))
}
replyMap[TagPUBS] = ephermalKex.PublicKey()
replyMap[TagSNO] = serverNonce

View file

@ -197,7 +197,7 @@ var _ = Describe("Server Crypto Setup", func() {
kexs = []byte("C255")
copy(nonce32[4:12], scfg.obit) // set the OBIT value at the right position
versionTag = make([]byte, 4)
binary.LittleEndian.PutUint32(versionTag, protocol.VersionNumberToTag(protocol.VersionWhatever))
binary.BigEndian.PutUint32(versionTag, uint32(protocol.VersionWhatever))
Expect(err).NotTo(HaveOccurred())
version = protocol.SupportedVersions[len(protocol.SupportedVersions)-1]
supportedVersions = []protocol.VersionNumber{version, 98, 99}
@ -348,7 +348,7 @@ var _ = Describe("Server Crypto Setup", func() {
Expect(response).To(ContainSubstring("SNO\x00"))
for _, v := range supportedVersions {
b := &bytes.Buffer{}
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(v))
utils.BigEndian.WriteUint32(b, uint32(v))
Expect(response).To(ContainSubstring(string(b.Bytes())))
}
Expect(cs.secureAEAD).ToNot(BeNil())
@ -477,7 +477,7 @@ var _ = Describe("Server Crypto Setup", func() {
Expect(highestSupportedVersion).ToNot(Equal(lowestSupportedVersion))
cs.version = highestSupportedVersion
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, protocol.VersionNumberToTag(lowestSupportedVersion))
binary.BigEndian.PutUint32(b, uint32(lowestSupportedVersion))
fullCHLO[TagVER] = b
HandshakeMessage{Tag: TagCHLO, Data: fullCHLO}.Write(&stream.dataToRead)
err := cs.HandleCryptoStream()
@ -490,7 +490,7 @@ var _ = Describe("Server Crypto Setup", func() {
Expect(protocol.IsSupportedVersion(supportedVersions, unsupportedVersion)).To(BeFalse())
cs.version = supportedVersion
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, protocol.VersionNumberToTag(unsupportedVersion))
binary.BigEndian.PutUint32(b, uint32(unsupportedVersion))
fullCHLO[TagVER] = b
HandshakeMessage{Tag: TagCHLO, Data: fullCHLO}.Write(&stream.dataToRead)
err := cs.HandleCryptoStream()

View file

@ -1,13 +1,19 @@
package protocol
import "fmt"
import (
"fmt"
)
// VersionNumber is a version number as int
type VersionNumber int
// gquicVersion0 is the "base" for gQUIC versions
// e.g. version 39 is gquicVersion + 0x39
const gquicVersion0 = 0x51303300
// The version numbers, making grepping easier
const (
Version37 VersionNumber = 37 + iota
Version37 VersionNumber = gquicVersion0 + 0x37 + iota
Version38
Version39
VersionTLS VersionNumber = 101
@ -40,19 +46,19 @@ func (vn VersionNumber) String() string {
case VersionTLS:
return "TLS dev version (WIP)"
default:
if vn > gquicVersion0 && vn <= gquicVersion0+0x99 {
return fmt.Sprintf("gQUIC %x", uint32(vn-gquicVersion0))
}
return fmt.Sprintf("%d", vn)
}
}
// VersionNumberToTag maps version numbers ('32') to tags ('Q032')
func VersionNumberToTag(vn VersionNumber) uint32 {
v := uint32(vn)
return 'Q' + ((v/100%10)+'0')<<8 + ((v/10%10)+'0')<<16 + ((v%10)+'0')<<24
}
// VersionTagToNumber is built from VersionNumberToTag in init()
func VersionTagToNumber(v uint32) VersionNumber {
return VersionNumber(((v>>8)&0xff-'0')*100 + ((v>>16)&0xff-'0')*10 + ((v>>24)&0xff - '0'))
// ToAltSvc returns the representation of the version for the H2 Alt-Svc parameters
func (vn VersionNumber) ToAltSvc() string {
if vn > gquicVersion0 && vn <= gquicVersion0+0x99 {
return fmt.Sprintf("%x", uint32(vn-gquicVersion0))
}
return fmt.Sprintf("%d", vn)
}
// IsSupportedVersion returns true if the server supports this version

View file

@ -14,21 +14,20 @@ var _ = Describe("Version", func() {
})
It("has the right string representation", func() {
Expect(Version37.String()).To(Equal("37"))
Expect(Version38.String()).To(Equal("38"))
Expect(Version39.String()).To(Equal("39"))
Expect(Version37.String()).To(Equal("gQUIC 37"))
Expect(Version38.String()).To(Equal("gQUIC 38"))
Expect(Version39.String()).To(Equal("gQUIC 39"))
Expect(VersionTLS.String()).To(ContainSubstring("TLS"))
Expect(VersionWhatever.String()).To(Equal("whatever"))
Expect(VersionUnsupported.String()).To(Equal("unsupported"))
Expect(VersionUnknown.String()).To(Equal("unknown"))
})
It("converts tags to numbers", func() {
Expect(VersionTagToNumber('Q' + '1'<<8 + '2'<<16 + '3'<<24)).To(Equal(VersionNumber(123)))
})
It("converts number to tag", func() {
Expect(VersionNumberToTag(VersionNumber(123))).To(Equal(uint32('Q' + '1'<<8 + '2'<<16 + '3'<<24)))
It("has the right representation for the H2 Alt-Svc tag", func() {
Expect(Version37.ToAltSvc()).To(Equal("37"))
Expect(Version38.ToAltSvc()).To(Equal("38"))
Expect(Version39.ToAltSvc()).To(Equal("39"))
Expect(VersionTLS.ToAltSvc()).To(Equal("101"))
})
It("recognizes supported versions", func() {

View file

@ -61,7 +61,7 @@ func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, v
utils.BigEndian.WriteUint64(b, uint64(h.ConnectionID))
}
if h.VersionFlag && pers == protocol.PerspectiveClient {
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(h.Version))
utils.BigEndian.WriteUint32(b, uint32(h.Version))
}
if len(h.DiversificationNonce) > 0 {
b.Write(h.DiversificationNonce)
@ -163,11 +163,11 @@ func parsePublicHeader(b *bytes.Reader, packetSentBy protocol.Perspective, versi
header.SupportedVersions = make([]protocol.VersionNumber, 0)
for {
var versionTag uint32
versionTag, err = utils.LittleEndian.ReadUint32(b)
versionTag, err = utils.BigEndian.ReadUint32(b)
if err != nil {
break
}
v := protocol.VersionTagToNumber(versionTag)
v := protocol.VersionNumber(versionTag)
header.SupportedVersions = append(header.SupportedVersions, v)
}
// a version negotiation packet doesn't have a packet number
@ -175,11 +175,11 @@ func parsePublicHeader(b *bytes.Reader, packetSentBy protocol.Perspective, versi
}
// packet was sent by the client. Read the version number
var versionTag uint32
versionTag, err = utils.LittleEndian.ReadUint32(b)
versionTag, err = utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, err
}
header.Version = protocol.VersionTagToNumber(versionTag)
header.Version = protocol.VersionNumber(versionTag)
version = header.Version
}

View file

@ -14,13 +14,15 @@ import (
var _ = Describe("Public Header", func() {
Context("when parsing", func() {
It("accepts a sample client header", func() {
b := bytes.NewReader([]byte{0x09, 0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6, 0x51, 0x30, 0x33, 0x34, 0x01})
ver := make([]byte, 4)
binary.BigEndian.PutUint32(ver, uint32(protocol.SupportedVersions[0]))
b := bytes.NewReader(append(append([]byte{0x09, 0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6}, ver...), 0x01))
hdr, err := parsePublicHeader(b, protocol.PerspectiveClient, protocol.VersionUnknown)
Expect(err).ToNot(HaveOccurred())
Expect(hdr.VersionFlag).To(BeTrue())
Expect(hdr.ResetFlag).To(BeFalse())
Expect(hdr.ConnectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
Expect(hdr.Version).To(Equal(protocol.VersionNumber(34)))
Expect(hdr.Version).To(Equal(protocol.SupportedVersions[0]))
Expect(hdr.SupportedVersions).To(BeEmpty())
Expect(hdr.PacketNumber).To(Equal(protocol.PacketNumber(1)))
Expect(b.Len()).To(BeZero())
@ -93,7 +95,7 @@ var _ = Describe("Public Header", func() {
Context("version negotiation packets", func() {
appendVersion := func(data []byte, v protocol.VersionNumber) []byte {
data = append(data, []byte{0, 0, 0, 0}...)
binary.LittleEndian.PutUint32(data[len(data)-4:], protocol.VersionNumberToTag(v))
binary.BigEndian.PutUint32(data[len(data)-4:], uint32(v))
return data
}

View file

@ -21,7 +21,7 @@ func ComposeVersionNegotiation(connectionID protocol.ConnectionID, versions []pr
utils.Errorf("error composing version negotiation packet: %s", err.Error())
}
for _, v := range versions {
utils.LittleEndian.WriteUint32(fullReply, protocol.VersionNumberToTag(v))
utils.BigEndian.WriteUint32(fullReply, uint32(v))
}
return fullReply.Bytes()
}

View file

@ -10,8 +10,8 @@ var _ = Describe("Version Negotiation Packet", func() {
It("composes version negotiation packets", func() {
expected := append(
[]byte{0x01 | 0x08, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
[]byte{'Q', '0', '9', '9'}...,
[]byte{'Q', '0', '3', '9'}...,
)
Expect(ComposeVersionNegotiation(1, []protocol.VersionNumber{99})).To(Equal(expected))
Expect(ComposeVersionNegotiation(1, []protocol.VersionNumber{protocol.Version39})).To(Equal(expected))
})
})

View file

@ -116,7 +116,7 @@ var _ = Describe("Server", func() {
errorChan: make(chan struct{}),
}
b := &bytes.Buffer{}
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(protocol.SupportedVersions[0]))
utils.BigEndian.WriteUint32(b, uint32(protocol.SupportedVersions[0]))
firstPacket = []byte{0x09, 0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6}
firstPacket = append(append(firstPacket, b.Bytes()...), 0x01)
})
@ -282,8 +282,8 @@ var _ = Describe("Server", func() {
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
b := &bytes.Buffer{}
// add an unsupported version
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(protocol.SupportedVersions[0]+1))
data := []byte{0x09, 0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6}
utils.BigEndian.WriteUint32(b, uint32(protocol.SupportedVersions[0]+1))
data = append(append(data, b.Bytes()...), 0x01)
err = serv.handlePacket(nil, nil, data)
Expect(err).ToNot(HaveOccurred())
@ -420,7 +420,7 @@ var _ = Describe("Server", func() {
Eventually(func() int { return conn.dataWritten.Len() }).ShouldNot(BeZero())
Expect(conn.dataWrittenTo).To(Equal(udpAddr))
b = &bytes.Buffer{}
utils.LittleEndian.WriteUint32(b, protocol.VersionNumberToTag(99))
utils.BigEndian.WriteUint32(b, uint32(99))
expected := append(
[]byte{0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0x37},
b.Bytes()...,