mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 11:51:02 +00:00
implement quic server
This commit is contained in:
parent
441a015ca8
commit
2c8dbef79f
151 changed files with 32464 additions and 0 deletions
|
@ -2,16 +2,20 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/h2quic"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
@ -63,6 +67,11 @@ var flags = []cli.Flag{
|
||||||
Name: "lets-encrypt",
|
Name: "lets-encrypt",
|
||||||
Usage: "lets encrypt enabled",
|
Usage: "lets encrypt enabled",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
EnvVar: "DRONE_QUIC",
|
||||||
|
Name: "quic",
|
||||||
|
Usage: "start the server with quic enabled",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
EnvVar: "DRONE_ADMIN",
|
EnvVar: "DRONE_ADMIN",
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
|
@ -526,6 +535,38 @@ func server(c *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if c.Bool("quic") {
|
||||||
|
dir := cacheDir()
|
||||||
|
os.MkdirAll(dir, 0700)
|
||||||
|
|
||||||
|
manager := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist(address.Host),
|
||||||
|
Cache: autocert.DirCache(dir),
|
||||||
|
}
|
||||||
|
httpServer := &http.Server{
|
||||||
|
Addr: ":https",
|
||||||
|
TLSConfig: &tls.Config{GetCertificate: manager.GetCertificate},
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
quicServer := &h2quic.Server{Server: httpServer}
|
||||||
|
quicServer.TLSConfig = httpServer.TLSConfig
|
||||||
|
|
||||||
|
httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
quicServer.SetQuicHeaders(w.Header())
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", ":443")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return quicServer.Serve(conn)
|
||||||
|
}
|
||||||
return http.Serve(autocert.NewListener(address.Host), handler)
|
return http.Serve(autocert.NewListener(address.Host), handler)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -607,3 +648,11 @@ func (a *authorizer) authorize(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
return errors.New("missing agent token")
|
return errors.New("missing agent token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cacheDir() string {
|
||||||
|
const base = "golang-autocert"
|
||||||
|
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||||
|
return filepath.Join(xdg, base)
|
||||||
|
}
|
||||||
|
return filepath.Join(os.Getenv("HOME"), ".cache", base)
|
||||||
|
}
|
||||||
|
|
212
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
Normal file
212
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default2QRecentRatio is the ratio of the 2Q cache dedicated
|
||||||
|
// to recently added entries that have only been accessed once.
|
||||||
|
Default2QRecentRatio = 0.25
|
||||||
|
|
||||||
|
// Default2QGhostEntries is the default ratio of ghost
|
||||||
|
// entries kept to track entries recently evicted
|
||||||
|
Default2QGhostEntries = 0.50
|
||||||
|
)
|
||||||
|
|
||||||
|
// TwoQueueCache is a thread-safe fixed size 2Q cache.
|
||||||
|
// 2Q is an enhancement over the standard LRU cache
|
||||||
|
// in that it tracks both frequently and recently used
|
||||||
|
// entries separately. This avoids a burst in access to new
|
||||||
|
// entries from evicting frequently used entries. It adds some
|
||||||
|
// additional tracking overhead to the standard LRU cache, and is
|
||||||
|
// computationally about 2x the cost, and adds some metadata over
|
||||||
|
// head. The ARCCache is similar, but does not require setting any
|
||||||
|
// parameters.
|
||||||
|
type TwoQueueCache struct {
|
||||||
|
size int
|
||||||
|
recentSize int
|
||||||
|
|
||||||
|
recent *simplelru.LRU
|
||||||
|
frequent *simplelru.LRU
|
||||||
|
recentEvict *simplelru.LRU
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New2Q creates a new TwoQueueCache using the default
|
||||||
|
// values for the parameters.
|
||||||
|
func New2Q(size int) (*TwoQueueCache, error) {
|
||||||
|
return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New2QParams creates a new TwoQueueCache using the provided
|
||||||
|
// parameter values.
|
||||||
|
func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil, fmt.Errorf("invalid size")
|
||||||
|
}
|
||||||
|
if recentRatio < 0.0 || recentRatio > 1.0 {
|
||||||
|
return nil, fmt.Errorf("invalid recent ratio")
|
||||||
|
}
|
||||||
|
if ghostRatio < 0.0 || ghostRatio > 1.0 {
|
||||||
|
return nil, fmt.Errorf("invalid ghost ratio")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the sub-sizes
|
||||||
|
recentSize := int(float64(size) * recentRatio)
|
||||||
|
evictSize := int(float64(size) * ghostRatio)
|
||||||
|
|
||||||
|
// Allocate the LRUs
|
||||||
|
recent, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frequent, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
recentEvict, err := simplelru.NewLRU(evictSize, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the cache
|
||||||
|
c := &TwoQueueCache{
|
||||||
|
size: size,
|
||||||
|
recentSize: recentSize,
|
||||||
|
recent: recent,
|
||||||
|
frequent: frequent,
|
||||||
|
recentEvict: recentEvict,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if this is a frequent value
|
||||||
|
if val, ok := c.frequent.Get(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is contained in recent, then we
|
||||||
|
// promote it to frequent
|
||||||
|
if val, ok := c.recent.Peek(key); ok {
|
||||||
|
c.recent.Remove(key)
|
||||||
|
c.frequent.Add(key, val)
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// No hit
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Add(key, value interface{}) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if the value is frequently used already,
|
||||||
|
// and just update the value
|
||||||
|
if c.frequent.Contains(key) {
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is recently used, and promote
|
||||||
|
// the value into the frequent list
|
||||||
|
if c.recent.Contains(key) {
|
||||||
|
c.recent.Remove(key)
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value was recently evicted, add it to the
|
||||||
|
// frequently used list
|
||||||
|
if c.recentEvict.Contains(key) {
|
||||||
|
c.ensureSpace(true)
|
||||||
|
c.recentEvict.Remove(key)
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the recently seen list
|
||||||
|
c.ensureSpace(false)
|
||||||
|
c.recent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureSpace is used to ensure we have space in the cache
|
||||||
|
func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
|
||||||
|
// If we have space, nothing to do
|
||||||
|
recentLen := c.recent.Len()
|
||||||
|
freqLen := c.frequent.Len()
|
||||||
|
if recentLen+freqLen < c.size {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the recent buffer is larger than
|
||||||
|
// the target, evict from there
|
||||||
|
if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
|
||||||
|
k, _, _ := c.recent.RemoveOldest()
|
||||||
|
c.recentEvict.Add(k, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the frequent list otherwise
|
||||||
|
c.frequent.RemoveOldest()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.recent.Len() + c.frequent.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Keys() []interface{} {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
k1 := c.frequent.Keys()
|
||||||
|
k2 := c.recent.Keys()
|
||||||
|
return append(k1, k2...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Remove(key interface{}) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
if c.frequent.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.recent.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.recentEvict.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Purge() {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
c.recent.Purge()
|
||||||
|
c.frequent.Purge()
|
||||||
|
c.recentEvict.Purge()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Contains(key interface{}) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.frequent.Contains(key) || c.recent.Contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
if val, ok := c.frequent.Peek(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
return c.recent.Peek(key)
|
||||||
|
}
|
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
25
vendor/github.com/hashicorp/golang-lru/README.md
generated
vendored
Normal file
25
vendor/github.com/hashicorp/golang-lru/README.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
golang-lru
|
||||||
|
==========
|
||||||
|
|
||||||
|
This provides the `lru` package which implements a fixed-size
|
||||||
|
thread safe LRU cache. It is based on the cache in Groupcache.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru)
|
||||||
|
|
||||||
|
Example
|
||||||
|
=======
|
||||||
|
|
||||||
|
Using the LRU is very simple:
|
||||||
|
|
||||||
|
```go
|
||||||
|
l, _ := New(128)
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
l.Add(i, nil)
|
||||||
|
}
|
||||||
|
if l.Len() != 128 {
|
||||||
|
panic(fmt.Sprintf("bad len: %v", l.Len()))
|
||||||
|
}
|
||||||
|
```
|
257
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
Normal file
257
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC).
|
||||||
|
// ARC is an enhancement over the standard LRU cache in that tracks both
|
||||||
|
// frequency and recency of use. This avoids a burst in access to new
|
||||||
|
// entries from evicting the frequently used older entries. It adds some
|
||||||
|
// additional tracking overhead to a standard LRU cache, computationally
|
||||||
|
// it is roughly 2x the cost, and the extra memory overhead is linear
|
||||||
|
// with the size of the cache. ARC has been patented by IBM, but is
|
||||||
|
// similar to the TwoQueueCache (2Q) which requires setting parameters.
|
||||||
|
type ARCCache struct {
|
||||||
|
size int // Size is the total capacity of the cache
|
||||||
|
p int // P is the dynamic preference towards T1 or T2
|
||||||
|
|
||||||
|
t1 *simplelru.LRU // T1 is the LRU for recently accessed items
|
||||||
|
b1 *simplelru.LRU // B1 is the LRU for evictions from t1
|
||||||
|
|
||||||
|
t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
|
||||||
|
b2 *simplelru.LRU // B2 is the LRU for evictions from t2
|
||||||
|
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewARC creates an ARC of the given size
|
||||||
|
func NewARC(size int) (*ARCCache, error) {
|
||||||
|
// Create the sub LRUs
|
||||||
|
b1, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b2, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t1, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t2, err := simplelru.NewLRU(size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the ARC
|
||||||
|
c := &ARCCache{
|
||||||
|
size: size,
|
||||||
|
p: 0,
|
||||||
|
t1: t1,
|
||||||
|
b1: b1,
|
||||||
|
t2: t2,
|
||||||
|
b2: b2,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Ff the value is contained in T1 (recent), then
|
||||||
|
// promote it to T2 (frequent)
|
||||||
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
|
c.t1.Remove(key)
|
||||||
|
c.t2.Add(key, val)
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is contained in T2 (frequent)
|
||||||
|
if val, ok := c.t2.Get(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// No hit
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache.
|
||||||
|
func (c *ARCCache) Add(key, value interface{}) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if the value is contained in T1 (recent), and potentially
|
||||||
|
// promote it to frequent T2
|
||||||
|
if c.t1.Contains(key) {
|
||||||
|
c.t1.Remove(key)
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is already in T2 (frequent) and update it
|
||||||
|
if c.t2.Contains(key) {
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this value was recently evicted as part of the
|
||||||
|
// recently used list
|
||||||
|
if c.b1.Contains(key) {
|
||||||
|
// T1 set is too small, increase P appropriately
|
||||||
|
delta := 1
|
||||||
|
b1Len := c.b1.Len()
|
||||||
|
b2Len := c.b2.Len()
|
||||||
|
if b2Len > b1Len {
|
||||||
|
delta = b2Len / b1Len
|
||||||
|
}
|
||||||
|
if c.p+delta >= c.size {
|
||||||
|
c.p = c.size
|
||||||
|
} else {
|
||||||
|
c.p += delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from B1
|
||||||
|
c.b1.Remove(key)
|
||||||
|
|
||||||
|
// Add the key to the frequently used list
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this value was recently evicted as part of the
|
||||||
|
// frequently used list
|
||||||
|
if c.b2.Contains(key) {
|
||||||
|
// T2 set is too small, decrease P appropriately
|
||||||
|
delta := 1
|
||||||
|
b1Len := c.b1.Len()
|
||||||
|
b2Len := c.b2.Len()
|
||||||
|
if b1Len > b2Len {
|
||||||
|
delta = b1Len / b2Len
|
||||||
|
}
|
||||||
|
if delta >= c.p {
|
||||||
|
c.p = 0
|
||||||
|
} else {
|
||||||
|
c.p -= delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from B2
|
||||||
|
c.b2.Remove(key)
|
||||||
|
|
||||||
|
// Add the key to the frequntly used list
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the size of the ghost buffers trim
|
||||||
|
if c.b1.Len() > c.size-c.p {
|
||||||
|
c.b1.RemoveOldest()
|
||||||
|
}
|
||||||
|
if c.b2.Len() > c.p {
|
||||||
|
c.b2.RemoveOldest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the recently seen list
|
||||||
|
c.t1.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace is used to adaptively evict from either T1 or T2
|
||||||
|
// based on the current learned value of P
|
||||||
|
func (c *ARCCache) replace(b2ContainsKey bool) {
|
||||||
|
t1Len := c.t1.Len()
|
||||||
|
if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) {
|
||||||
|
k, _, ok := c.t1.RemoveOldest()
|
||||||
|
if ok {
|
||||||
|
c.b1.Add(k, nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
k, _, ok := c.t2.RemoveOldest()
|
||||||
|
if ok {
|
||||||
|
c.b2.Add(k, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of cached entries
|
||||||
|
func (c *ARCCache) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.t1.Len() + c.t2.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns all the cached keys
|
||||||
|
func (c *ARCCache) Keys() []interface{} {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
k1 := c.t1.Keys()
|
||||||
|
k2 := c.t2.Keys()
|
||||||
|
return append(k1, k2...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove is used to purge a key from the cache
|
||||||
|
func (c *ARCCache) Remove(key interface{}) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
if c.t1.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.t2.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.b1.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.b2.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to clear the cache
|
||||||
|
func (c *ARCCache) Purge() {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
c.t1.Purge()
|
||||||
|
c.t2.Purge()
|
||||||
|
c.b1.Purge()
|
||||||
|
c.b2.Purge()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains is used to check if the cache contains a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *ARCCache) Contains(key interface{}) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.t1.Contains(key) || c.t2.Contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek is used to inspect the cache value of a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
return c.t2.Peek(key)
|
||||||
|
}
|
114
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
Normal file
114
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// This package provides a simple LRU cache. It is based on the
|
||||||
|
// LRU implementation in groupcache:
|
||||||
|
// https://github.com/golang/groupcache/tree/master/lru
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cache is a thread-safe fixed size LRU cache.
|
||||||
|
type Cache struct {
|
||||||
|
lru *simplelru.LRU
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates an LRU of the given size
|
||||||
|
func New(size int) (*Cache, error) {
|
||||||
|
return NewWithEvict(size, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithEvict constructs a fixed size cache with the given eviction
|
||||||
|
// callback.
|
||||||
|
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
|
||||||
|
lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := &Cache{
|
||||||
|
lru: lru,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache
|
||||||
|
func (c *Cache) Purge() {
|
||||||
|
c.lock.Lock()
|
||||||
|
c.lru.Purge()
|
||||||
|
c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
|
func (c *Cache) Add(key, value interface{}) bool {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
return c.lru.Add(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *Cache) Get(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
return c.lru.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a key is in the cache, without updating the recent-ness
|
||||||
|
// or deleting it for being stale.
|
||||||
|
func (c *Cache) Contains(key interface{}) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.lru.Contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the key value (or undefined if not found) without updating
|
||||||
|
// the "recently used"-ness of the key.
|
||||||
|
func (c *Cache) Peek(key interface{}) (interface{}, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.lru.Peek(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||||
|
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||||
|
// Returns whether found and whether an eviction occurred.
|
||||||
|
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
if c.lru.Contains(key) {
|
||||||
|
return true, false
|
||||||
|
} else {
|
||||||
|
evict := c.lru.Add(key, value)
|
||||||
|
return false, evict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache.
|
||||||
|
func (c *Cache) Remove(key interface{}) {
|
||||||
|
c.lock.Lock()
|
||||||
|
c.lru.Remove(key)
|
||||||
|
c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
|
func (c *Cache) RemoveOldest() {
|
||||||
|
c.lock.Lock()
|
||||||
|
c.lru.RemoveOldest()
|
||||||
|
c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
func (c *Cache) Keys() []interface{} {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.lru.Keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (c *Cache) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.lru.Len()
|
||||||
|
}
|
160
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
160
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package simplelru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvictCallback is used to get a callback when a cache entry is evicted
|
||||||
|
type EvictCallback func(key interface{}, value interface{})
|
||||||
|
|
||||||
|
// LRU implements a non-thread safe fixed size LRU cache
|
||||||
|
type LRU struct {
|
||||||
|
size int
|
||||||
|
evictList *list.List
|
||||||
|
items map[interface{}]*list.Element
|
||||||
|
onEvict EvictCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry is used to hold a value in the evictList
|
||||||
|
type entry struct {
|
||||||
|
key interface{}
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLRU constructs an LRU of the given size
|
||||||
|
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil, errors.New("Must provide a positive size")
|
||||||
|
}
|
||||||
|
c := &LRU{
|
||||||
|
size: size,
|
||||||
|
evictList: list.New(),
|
||||||
|
items: make(map[interface{}]*list.Element),
|
||||||
|
onEvict: onEvict,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache
|
||||||
|
func (c *LRU) Purge() {
|
||||||
|
for k, v := range c.items {
|
||||||
|
if c.onEvict != nil {
|
||||||
|
c.onEvict(k, v.Value.(*entry).value)
|
||||||
|
}
|
||||||
|
delete(c.items, k)
|
||||||
|
}
|
||||||
|
c.evictList.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
|
func (c *LRU) Add(key, value interface{}) bool {
|
||||||
|
// Check for existing item
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.evictList.MoveToFront(ent)
|
||||||
|
ent.Value.(*entry).value = value
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new item
|
||||||
|
ent := &entry{key, value}
|
||||||
|
entry := c.evictList.PushFront(ent)
|
||||||
|
c.items[key] = entry
|
||||||
|
|
||||||
|
evict := c.evictList.Len() > c.size
|
||||||
|
// Verify size not exceeded
|
||||||
|
if evict {
|
||||||
|
c.removeOldest()
|
||||||
|
}
|
||||||
|
return evict
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.evictList.MoveToFront(ent)
|
||||||
|
return ent.Value.(*entry).value, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a key is in the cache, without updating the recent-ness
|
||||||
|
// or deleting it for being stale.
|
||||||
|
func (c *LRU) Contains(key interface{}) (ok bool) {
|
||||||
|
_, ok = c.items[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the key value (or undefined if not found) without updating
|
||||||
|
// the "recently used"-ness of the key.
|
||||||
|
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
return ent.Value.(*entry).value, true
|
||||||
|
}
|
||||||
|
return nil, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache, returning if the
|
||||||
|
// key was contained.
|
||||||
|
func (c *LRU) Remove(key interface{}) bool {
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.removeElement(ent)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
|
func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
|
||||||
|
ent := c.evictList.Back()
|
||||||
|
if ent != nil {
|
||||||
|
c.removeElement(ent)
|
||||||
|
kv := ent.Value.(*entry)
|
||||||
|
return kv.key, kv.value, true
|
||||||
|
}
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOldest returns the oldest entry
|
||||||
|
func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
|
||||||
|
ent := c.evictList.Back()
|
||||||
|
if ent != nil {
|
||||||
|
kv := ent.Value.(*entry)
|
||||||
|
return kv.key, kv.value, true
|
||||||
|
}
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
func (c *LRU) Keys() []interface{} {
|
||||||
|
keys := make([]interface{}, len(c.items))
|
||||||
|
i := 0
|
||||||
|
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||||
|
keys[i] = ent.Value.(*entry).key
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (c *LRU) Len() int {
|
||||||
|
return c.evictList.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeOldest removes the oldest item from the cache.
|
||||||
|
func (c *LRU) removeOldest() {
|
||||||
|
ent := c.evictList.Back()
|
||||||
|
if ent != nil {
|
||||||
|
c.removeElement(ent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeElement is used to remove a given list element from the cache
|
||||||
|
func (c *LRU) removeElement(e *list.Element) {
|
||||||
|
c.evictList.Remove(e)
|
||||||
|
kv := e.Value.(*entry)
|
||||||
|
delete(c.items, kv.key)
|
||||||
|
if c.onEvict != nil {
|
||||||
|
c.onEvict(kv.key, kv.value)
|
||||||
|
}
|
||||||
|
}
|
21
vendor/github.com/lucas-clemente/aes12/LICENSE
generated
vendored
Normal file
21
vendor/github.com/lucas-clemente/aes12/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Lucas Clemente
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
28
vendor/github.com/lucas-clemente/aes12/Readme.md
generated
vendored
Normal file
28
vendor/github.com/lucas-clemente/aes12/Readme.md
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# aes12
|
||||||
|
|
||||||
|
This package modifies the AES-GCM implementation from Go's standard library to use 12 byte tag sizes. It is not intended for a general audience, and used in [quic-go](https://github.com/lucas-clemente/quic-go).
|
||||||
|
|
||||||
|
To make use of the in-place encryption / decryption feature, the `dst` parameter to `Seal` and `Open` should be 16 bytes longer than plaintext, not 12.
|
||||||
|
|
||||||
|
Command for testing:
|
||||||
|
|
||||||
|
```
|
||||||
|
go test . --bench=. && GOARCH=386 go test . --bench=.
|
||||||
|
```
|
||||||
|
|
||||||
|
The output (on my machine):
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkAESGCMSeal1K-8 3000000 467 ns/op 2192.37 MB/s
|
||||||
|
BenchmarkAESGCMOpen1K-8 3000000 416 ns/op 2456.72 MB/s
|
||||||
|
BenchmarkAESGCMSeal8K-8 500000 2742 ns/op 2986.53 MB/s
|
||||||
|
BenchmarkAESGCMOpen8K-8 500000 2791 ns/op 2934.65 MB/s
|
||||||
|
PASS
|
||||||
|
ok github.com/lucas-clemente/aes12 6.383s
|
||||||
|
BenchmarkAESGCMSeal1K-8 50000 35233 ns/op 29.06 MB/s
|
||||||
|
BenchmarkAESGCMOpen1K-8 50000 34529 ns/op 29.66 MB/s
|
||||||
|
BenchmarkAESGCMSeal8K-8 5000 262678 ns/op 31.19 MB/s
|
||||||
|
BenchmarkAESGCMOpen8K-8 5000 267296 ns/op 30.65 MB/s
|
||||||
|
PASS
|
||||||
|
ok github.com/lucas-clemente/aes12 6.972s
|
||||||
|
```
|
148
vendor/github.com/lucas-clemente/aes12/aes_gcm.go
generated
vendored
Normal file
148
vendor/github.com/lucas-clemente/aes12/aes_gcm.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
package aes12
|
||||||
|
|
||||||
|
import "crypto/subtle"
|
||||||
|
|
||||||
|
// The following functions are defined in gcm_amd64.s.
|
||||||
|
func hasGCMAsm() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func aesEncBlock(dst, src *[16]byte, ks []uint32)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func gcmAesInit(productTable *[256]byte, ks []uint32)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64)
|
||||||
|
|
||||||
|
// aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
|
||||||
|
// will use the optimised implementation in this file when possible. Instances
|
||||||
|
// of this type only exist when hasGCMAsm returns true.
|
||||||
|
type aesCipherGCM struct {
|
||||||
|
aesCipherAsm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that aesCipherGCM implements the gcmAble interface.
|
||||||
|
var _ gcmAble = (*aesCipherGCM)(nil)
|
||||||
|
|
||||||
|
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
|
||||||
|
// called by crypto/cipher.NewGCM via the gcmAble interface.
|
||||||
|
func (c *aesCipherGCM) NewGCM(nonceSize int) (AEAD, error) {
|
||||||
|
g := &gcmAsm{ks: c.enc, nonceSize: nonceSize}
|
||||||
|
gcmAesInit(&g.productTable, g.ks)
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type gcmAsm struct {
|
||||||
|
// ks is the key schedule, the length of which depends on the size of
|
||||||
|
// the AES key.
|
||||||
|
ks []uint32
|
||||||
|
// productTable contains pre-computed multiples of the binary-field
|
||||||
|
// element used in GHASH.
|
||||||
|
productTable [256]byte
|
||||||
|
// nonceSize contains the expected size of the nonce, in bytes.
|
||||||
|
nonceSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gcmAsm) NonceSize() int {
|
||||||
|
return g.nonceSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*gcmAsm) Overhead() int {
|
||||||
|
return gcmTagSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal encrypts and authenticates plaintext. See the AEAD interface for
|
||||||
|
// details.
|
||||||
|
func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||||
|
if len(nonce) != g.nonceSize {
|
||||||
|
panic("cipher: incorrect nonce length given to GCM")
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter, tagMask [gcmBlockSize]byte
|
||||||
|
|
||||||
|
if len(nonce) == gcmStandardNonceSize {
|
||||||
|
// Init counter to nonce||1
|
||||||
|
copy(counter[:], nonce)
|
||||||
|
counter[gcmBlockSize-1] = 1
|
||||||
|
} else {
|
||||||
|
// Otherwise counter = GHASH(nonce)
|
||||||
|
gcmAesData(&g.productTable, nonce, &counter)
|
||||||
|
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
aesEncBlock(&tagMask, &counter, g.ks)
|
||||||
|
|
||||||
|
var tagOut [16]byte
|
||||||
|
gcmAesData(&g.productTable, data, &tagOut)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
|
||||||
|
if len(plaintext) > 0 {
|
||||||
|
gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks)
|
||||||
|
}
|
||||||
|
gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
|
||||||
|
copy(out[len(plaintext):], tagOut[:gcmTagSize])
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open authenticates and decrypts ciphertext. See the AEAD interface
|
||||||
|
// for details.
|
||||||
|
func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||||
|
if len(nonce) != g.nonceSize {
|
||||||
|
panic("cipher: incorrect nonce length given to GCM")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < gcmTagSize {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
tag := ciphertext[len(ciphertext)-gcmTagSize:]
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
|
||||||
|
|
||||||
|
// See GCM spec, section 7.1.
|
||||||
|
var counter, tagMask [gcmBlockSize]byte
|
||||||
|
|
||||||
|
if len(nonce) == gcmStandardNonceSize {
|
||||||
|
// Init counter to nonce||1
|
||||||
|
copy(counter[:], nonce)
|
||||||
|
counter[gcmBlockSize-1] = 1
|
||||||
|
} else {
|
||||||
|
// Otherwise counter = GHASH(nonce)
|
||||||
|
gcmAesData(&g.productTable, nonce, &counter)
|
||||||
|
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
aesEncBlock(&tagMask, &counter, g.ks)
|
||||||
|
|
||||||
|
var expectedTag [16]byte
|
||||||
|
gcmAesData(&g.productTable, data, &expectedTag)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
if len(ciphertext) > 0 {
|
||||||
|
gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks)
|
||||||
|
}
|
||||||
|
gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare(expectedTag[:12], tag) != 1 {
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
285
vendor/github.com/lucas-clemente/aes12/asm_amd64.s
generated
vendored
Normal file
285
vendor/github.com/lucas-clemente/aes12/asm_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func hasAsm() bool
|
||||||
|
// returns whether AES-NI is supported
|
||||||
|
TEXT ·hasAsm(SB),NOSPLIT,$0
|
||||||
|
XORQ AX, AX
|
||||||
|
INCL AX
|
||||||
|
CPUID
|
||||||
|
SHRQ $25, CX
|
||||||
|
ANDQ $1, CX
|
||||||
|
MOVB CX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
TEXT ·encryptBlockAsm(SB),NOSPLIT,$0
|
||||||
|
MOVQ nr+0(FP), CX
|
||||||
|
MOVQ xk+8(FP), AX
|
||||||
|
MOVQ dst+16(FP), DX
|
||||||
|
MOVQ src+24(FP), BX
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
MOVUPS 0(BX), X0
|
||||||
|
ADDQ $16, AX
|
||||||
|
PXOR X1, X0
|
||||||
|
SUBQ $12, CX
|
||||||
|
JE Lenc196
|
||||||
|
JB Lenc128
|
||||||
|
Lenc256:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
ADDQ $32, AX
|
||||||
|
Lenc196:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
ADDQ $32, AX
|
||||||
|
Lenc128:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 32(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 48(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 64(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 80(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 96(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 112(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 128(AX), X1
|
||||||
|
AESENC X1, X0
|
||||||
|
MOVUPS 144(AX), X1
|
||||||
|
AESENCLAST X1, X0
|
||||||
|
MOVUPS X0, 0(DX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
TEXT ·decryptBlockAsm(SB),NOSPLIT,$0
|
||||||
|
MOVQ nr+0(FP), CX
|
||||||
|
MOVQ xk+8(FP), AX
|
||||||
|
MOVQ dst+16(FP), DX
|
||||||
|
MOVQ src+24(FP), BX
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
MOVUPS 0(BX), X0
|
||||||
|
ADDQ $16, AX
|
||||||
|
PXOR X1, X0
|
||||||
|
SUBQ $12, CX
|
||||||
|
JE Ldec196
|
||||||
|
JB Ldec128
|
||||||
|
Ldec256:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
ADDQ $32, AX
|
||||||
|
Ldec196:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
ADDQ $32, AX
|
||||||
|
Ldec128:
|
||||||
|
MOVUPS 0(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 16(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 32(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 48(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 64(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 80(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 96(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 112(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 128(AX), X1
|
||||||
|
AESDEC X1, X0
|
||||||
|
MOVUPS 144(AX), X1
|
||||||
|
AESDECLAST X1, X0
|
||||||
|
MOVUPS X0, 0(DX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func expandKeyAsm(nr int, key *byte, enc, dec *uint32) {
|
||||||
|
// Note that round keys are stored in uint128 format, not uint32
|
||||||
|
TEXT ·expandKeyAsm(SB),NOSPLIT,$0
|
||||||
|
MOVQ nr+0(FP), CX
|
||||||
|
MOVQ key+8(FP), AX
|
||||||
|
MOVQ enc+16(FP), BX
|
||||||
|
MOVQ dec+24(FP), DX
|
||||||
|
MOVUPS (AX), X0
|
||||||
|
// enc
|
||||||
|
MOVUPS X0, (BX)
|
||||||
|
ADDQ $16, BX
|
||||||
|
PXOR X4, X4 // _expand_key_* expect X4 to be zero
|
||||||
|
CMPL CX, $12
|
||||||
|
JE Lexp_enc196
|
||||||
|
JB Lexp_enc128
|
||||||
|
Lexp_enc256:
|
||||||
|
MOVUPS 16(AX), X2
|
||||||
|
MOVUPS X2, (BX)
|
||||||
|
ADDQ $16, BX
|
||||||
|
AESKEYGENASSIST $0x01, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x01, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x02, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x02, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x04, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x04, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x08, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x08, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x10, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x10, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x20, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x20, X0, X1
|
||||||
|
CALL _expand_key_256b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x40, X2, X1
|
||||||
|
CALL _expand_key_256a<>(SB)
|
||||||
|
JMP Lexp_dec
|
||||||
|
Lexp_enc196:
|
||||||
|
MOVQ 16(AX), X2
|
||||||
|
AESKEYGENASSIST $0x01, X2, X1
|
||||||
|
CALL _expand_key_192a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x02, X2, X1
|
||||||
|
CALL _expand_key_192b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x04, X2, X1
|
||||||
|
CALL _expand_key_192a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x08, X2, X1
|
||||||
|
CALL _expand_key_192b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x10, X2, X1
|
||||||
|
CALL _expand_key_192a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x20, X2, X1
|
||||||
|
CALL _expand_key_192b<>(SB)
|
||||||
|
AESKEYGENASSIST $0x40, X2, X1
|
||||||
|
CALL _expand_key_192a<>(SB)
|
||||||
|
AESKEYGENASSIST $0x80, X2, X1
|
||||||
|
CALL _expand_key_192b<>(SB)
|
||||||
|
JMP Lexp_dec
|
||||||
|
Lexp_enc128:
|
||||||
|
AESKEYGENASSIST $0x01, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x02, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x04, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x08, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x10, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x20, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x40, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x80, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x1b, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
AESKEYGENASSIST $0x36, X0, X1
|
||||||
|
CALL _expand_key_128<>(SB)
|
||||||
|
Lexp_dec:
|
||||||
|
// dec
|
||||||
|
SUBQ $16, BX
|
||||||
|
MOVUPS (BX), X1
|
||||||
|
MOVUPS X1, (DX)
|
||||||
|
DECQ CX
|
||||||
|
Lexp_dec_loop:
|
||||||
|
MOVUPS -16(BX), X1
|
||||||
|
AESIMC X1, X0
|
||||||
|
MOVUPS X0, 16(DX)
|
||||||
|
SUBQ $16, BX
|
||||||
|
ADDQ $16, DX
|
||||||
|
DECQ CX
|
||||||
|
JNZ Lexp_dec_loop
|
||||||
|
MOVUPS -16(BX), X0
|
||||||
|
MOVUPS X0, 16(DX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT _expand_key_128<>(SB),NOSPLIT,$0
|
||||||
|
PSHUFD $0xff, X1, X1
|
||||||
|
SHUFPS $0x10, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
SHUFPS $0x8c, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
PXOR X1, X0
|
||||||
|
MOVUPS X0, (BX)
|
||||||
|
ADDQ $16, BX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT _expand_key_192a<>(SB),NOSPLIT,$0
|
||||||
|
PSHUFD $0x55, X1, X1
|
||||||
|
SHUFPS $0x10, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
SHUFPS $0x8c, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
PXOR X1, X0
|
||||||
|
|
||||||
|
MOVAPS X2, X5
|
||||||
|
MOVAPS X2, X6
|
||||||
|
PSLLDQ $0x4, X5
|
||||||
|
PSHUFD $0xff, X0, X3
|
||||||
|
PXOR X3, X2
|
||||||
|
PXOR X5, X2
|
||||||
|
|
||||||
|
MOVAPS X0, X1
|
||||||
|
SHUFPS $0x44, X0, X6
|
||||||
|
MOVUPS X6, (BX)
|
||||||
|
SHUFPS $0x4e, X2, X1
|
||||||
|
MOVUPS X1, 16(BX)
|
||||||
|
ADDQ $32, BX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT _expand_key_192b<>(SB),NOSPLIT,$0
|
||||||
|
PSHUFD $0x55, X1, X1
|
||||||
|
SHUFPS $0x10, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
SHUFPS $0x8c, X0, X4
|
||||||
|
PXOR X4, X0
|
||||||
|
PXOR X1, X0
|
||||||
|
|
||||||
|
MOVAPS X2, X5
|
||||||
|
PSLLDQ $0x4, X5
|
||||||
|
PSHUFD $0xff, X0, X3
|
||||||
|
PXOR X3, X2
|
||||||
|
PXOR X5, X2
|
||||||
|
|
||||||
|
MOVUPS X0, (BX)
|
||||||
|
ADDQ $16, BX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT _expand_key_256a<>(SB),NOSPLIT,$0
|
||||||
|
JMP _expand_key_128<>(SB)
|
||||||
|
|
||||||
|
TEXT _expand_key_256b<>(SB),NOSPLIT,$0
|
||||||
|
PSHUFD $0xaa, X1, X1
|
||||||
|
SHUFPS $0x10, X2, X4
|
||||||
|
PXOR X4, X2
|
||||||
|
SHUFPS $0x8c, X2, X4
|
||||||
|
PXOR X4, X2
|
||||||
|
PXOR X1, X2
|
||||||
|
|
||||||
|
MOVUPS X2, (BX)
|
||||||
|
ADDQ $16, BX
|
||||||
|
RET
|
176
vendor/github.com/lucas-clemente/aes12/block.go
generated
vendored
Normal file
176
vendor/github.com/lucas-clemente/aes12/block.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// This Go implementation is derived in part from the reference
|
||||||
|
// ANSI C implementation, which carries the following notice:
|
||||||
|
//
|
||||||
|
// rijndael-alg-fst.c
|
||||||
|
//
|
||||||
|
// @version 3.0 (December 2000)
|
||||||
|
//
|
||||||
|
// Optimised ANSI C code for the Rijndael cipher (now AES)
|
||||||
|
//
|
||||||
|
// @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
|
||||||
|
// @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
|
||||||
|
// @author Paulo Barreto <paulo.barreto@terra.com.br>
|
||||||
|
//
|
||||||
|
// This code is hereby placed in the public domain.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
|
||||||
|
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
||||||
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// See FIPS 197 for specification, and see Daemen and Rijmen's Rijndael submission
|
||||||
|
// for implementation details.
|
||||||
|
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
// http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf
|
||||||
|
|
||||||
|
package aes12
|
||||||
|
|
||||||
|
// Encrypt one block from src into dst, using the expanded key xk.
|
||||||
|
func encryptBlockGo(xk []uint32, dst, src []byte) {
|
||||||
|
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
|
||||||
|
|
||||||
|
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11])
|
||||||
|
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
|
||||||
|
|
||||||
|
// First round just XORs input with key.
|
||||||
|
s0 ^= xk[0]
|
||||||
|
s1 ^= xk[1]
|
||||||
|
s2 ^= xk[2]
|
||||||
|
s3 ^= xk[3]
|
||||||
|
|
||||||
|
// Middle rounds shuffle using tables.
|
||||||
|
// Number of rounds is set by length of expanded key.
|
||||||
|
nr := len(xk)/4 - 2 // - 2: one above, one more below
|
||||||
|
k := 4
|
||||||
|
for r := 0; r < nr; r++ {
|
||||||
|
t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)]
|
||||||
|
t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)]
|
||||||
|
t2 = xk[k+2] ^ te0[uint8(s2>>24)] ^ te1[uint8(s3>>16)] ^ te2[uint8(s0>>8)] ^ te3[uint8(s1)]
|
||||||
|
t3 = xk[k+3] ^ te0[uint8(s3>>24)] ^ te1[uint8(s0>>16)] ^ te2[uint8(s1>>8)] ^ te3[uint8(s2)]
|
||||||
|
k += 4
|
||||||
|
s0, s1, s2, s3 = t0, t1, t2, t3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last round uses s-box directly and XORs to produce output.
|
||||||
|
s0 = uint32(sbox0[t0>>24])<<24 | uint32(sbox0[t1>>16&0xff])<<16 | uint32(sbox0[t2>>8&0xff])<<8 | uint32(sbox0[t3&0xff])
|
||||||
|
s1 = uint32(sbox0[t1>>24])<<24 | uint32(sbox0[t2>>16&0xff])<<16 | uint32(sbox0[t3>>8&0xff])<<8 | uint32(sbox0[t0&0xff])
|
||||||
|
s2 = uint32(sbox0[t2>>24])<<24 | uint32(sbox0[t3>>16&0xff])<<16 | uint32(sbox0[t0>>8&0xff])<<8 | uint32(sbox0[t1&0xff])
|
||||||
|
s3 = uint32(sbox0[t3>>24])<<24 | uint32(sbox0[t0>>16&0xff])<<16 | uint32(sbox0[t1>>8&0xff])<<8 | uint32(sbox0[t2&0xff])
|
||||||
|
|
||||||
|
s0 ^= xk[k+0]
|
||||||
|
s1 ^= xk[k+1]
|
||||||
|
s2 ^= xk[k+2]
|
||||||
|
s3 ^= xk[k+3]
|
||||||
|
|
||||||
|
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0)
|
||||||
|
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1)
|
||||||
|
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2)
|
||||||
|
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt one block from src into dst, using the expanded key xk.
|
||||||
|
func decryptBlockGo(xk []uint32, dst, src []byte) {
|
||||||
|
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
|
||||||
|
|
||||||
|
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11])
|
||||||
|
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
|
||||||
|
|
||||||
|
// First round just XORs input with key.
|
||||||
|
s0 ^= xk[0]
|
||||||
|
s1 ^= xk[1]
|
||||||
|
s2 ^= xk[2]
|
||||||
|
s3 ^= xk[3]
|
||||||
|
|
||||||
|
// Middle rounds shuffle using tables.
|
||||||
|
// Number of rounds is set by length of expanded key.
|
||||||
|
nr := len(xk)/4 - 2 // - 2: one above, one more below
|
||||||
|
k := 4
|
||||||
|
for r := 0; r < nr; r++ {
|
||||||
|
t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)]
|
||||||
|
t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)]
|
||||||
|
t2 = xk[k+2] ^ td0[uint8(s2>>24)] ^ td1[uint8(s1>>16)] ^ td2[uint8(s0>>8)] ^ td3[uint8(s3)]
|
||||||
|
t3 = xk[k+3] ^ td0[uint8(s3>>24)] ^ td1[uint8(s2>>16)] ^ td2[uint8(s1>>8)] ^ td3[uint8(s0)]
|
||||||
|
k += 4
|
||||||
|
s0, s1, s2, s3 = t0, t1, t2, t3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last round uses s-box directly and XORs to produce output.
|
||||||
|
s0 = uint32(sbox1[t0>>24])<<24 | uint32(sbox1[t3>>16&0xff])<<16 | uint32(sbox1[t2>>8&0xff])<<8 | uint32(sbox1[t1&0xff])
|
||||||
|
s1 = uint32(sbox1[t1>>24])<<24 | uint32(sbox1[t0>>16&0xff])<<16 | uint32(sbox1[t3>>8&0xff])<<8 | uint32(sbox1[t2&0xff])
|
||||||
|
s2 = uint32(sbox1[t2>>24])<<24 | uint32(sbox1[t1>>16&0xff])<<16 | uint32(sbox1[t0>>8&0xff])<<8 | uint32(sbox1[t3&0xff])
|
||||||
|
s3 = uint32(sbox1[t3>>24])<<24 | uint32(sbox1[t2>>16&0xff])<<16 | uint32(sbox1[t1>>8&0xff])<<8 | uint32(sbox1[t0&0xff])
|
||||||
|
|
||||||
|
s0 ^= xk[k+0]
|
||||||
|
s1 ^= xk[k+1]
|
||||||
|
s2 ^= xk[k+2]
|
||||||
|
s3 ^= xk[k+3]
|
||||||
|
|
||||||
|
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0)
|
||||||
|
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1)
|
||||||
|
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2)
|
||||||
|
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply sbox0 to each byte in w.
|
||||||
|
func subw(w uint32) uint32 {
|
||||||
|
return uint32(sbox0[w>>24])<<24 |
|
||||||
|
uint32(sbox0[w>>16&0xff])<<16 |
|
||||||
|
uint32(sbox0[w>>8&0xff])<<8 |
|
||||||
|
uint32(sbox0[w&0xff])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate
|
||||||
|
func rotw(w uint32) uint32 { return w<<8 | w>>24 }
|
||||||
|
|
||||||
|
// Key expansion algorithm. See FIPS-197, Figure 11.
|
||||||
|
// Their rcon[i] is our powx[i-1] << 24.
|
||||||
|
func expandKeyGo(key []byte, enc, dec []uint32) {
|
||||||
|
// Encryption key setup.
|
||||||
|
var i int
|
||||||
|
nk := len(key) / 4
|
||||||
|
for i = 0; i < nk; i++ {
|
||||||
|
enc[i] = uint32(key[4*i])<<24 | uint32(key[4*i+1])<<16 | uint32(key[4*i+2])<<8 | uint32(key[4*i+3])
|
||||||
|
}
|
||||||
|
for ; i < len(enc); i++ {
|
||||||
|
t := enc[i-1]
|
||||||
|
if i%nk == 0 {
|
||||||
|
t = subw(rotw(t)) ^ (uint32(powx[i/nk-1]) << 24)
|
||||||
|
} else if nk > 6 && i%nk == 4 {
|
||||||
|
t = subw(t)
|
||||||
|
}
|
||||||
|
enc[i] = enc[i-nk] ^ t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive decryption key from encryption key.
|
||||||
|
// Reverse the 4-word round key sets from enc to produce dec.
|
||||||
|
// All sets but the first and last get the MixColumn transform applied.
|
||||||
|
if dec == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := len(enc)
|
||||||
|
for i := 0; i < n; i += 4 {
|
||||||
|
ei := n - i - 4
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
x := enc[ei+j]
|
||||||
|
if i > 0 && i+4 < n {
|
||||||
|
x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]]
|
||||||
|
}
|
||||||
|
dec[i+j] = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
vendor/github.com/lucas-clemente/aes12/cipher.go
generated
vendored
Normal file
68
vendor/github.com/lucas-clemente/aes12/cipher.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2009 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 aes12
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// The AES block size in bytes.
|
||||||
|
const BlockSize = 16
|
||||||
|
|
||||||
|
// A cipher is an instance of AES encryption using a particular key.
|
||||||
|
type aesCipher struct {
|
||||||
|
enc []uint32
|
||||||
|
dec []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeySizeError int
|
||||||
|
|
||||||
|
func (k KeySizeError) Error() string {
|
||||||
|
return "crypto/aes: invalid key size " + strconv.Itoa(int(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCipher creates and returns a new Block.
|
||||||
|
// The key argument should be the AES key,
|
||||||
|
// either 16, 24, or 32 bytes to select
|
||||||
|
// AES-128, AES-192, or AES-256.
|
||||||
|
func NewCipher(key []byte) (Block, error) {
|
||||||
|
k := len(key)
|
||||||
|
switch k {
|
||||||
|
default:
|
||||||
|
return nil, KeySizeError(k)
|
||||||
|
case 16, 24, 32:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return newCipher(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCipherGeneric creates and returns a new Block
|
||||||
|
// implemented in pure Go.
|
||||||
|
func newCipherGeneric(key []byte) (Block, error) {
|
||||||
|
n := len(key) + 28
|
||||||
|
c := aesCipher{make([]uint32, n), make([]uint32, n)}
|
||||||
|
expandKeyGo(key, c.enc, c.dec)
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aesCipher) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
|
func (c *aesCipher) Encrypt(dst, src []byte) {
|
||||||
|
if len(src) < BlockSize {
|
||||||
|
panic("crypto/aes: input not full block")
|
||||||
|
}
|
||||||
|
if len(dst) < BlockSize {
|
||||||
|
panic("crypto/aes: output not full block")
|
||||||
|
}
|
||||||
|
encryptBlockGo(c.enc, dst, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aesCipher) Decrypt(dst, src []byte) {
|
||||||
|
if len(src) < BlockSize {
|
||||||
|
panic("crypto/aes: input not full block")
|
||||||
|
}
|
||||||
|
if len(dst) < BlockSize {
|
||||||
|
panic("crypto/aes: output not full block")
|
||||||
|
}
|
||||||
|
decryptBlockGo(c.dec, dst, src)
|
||||||
|
}
|
56
vendor/github.com/lucas-clemente/aes12/cipher_2.go
generated
vendored
Normal file
56
vendor/github.com/lucas-clemente/aes12/cipher_2.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2010 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 aes12 implements standard block cipher modes that can be wrapped
|
||||||
|
// around low-level block cipher implementations.
|
||||||
|
// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html
|
||||||
|
// and NIST Special Publication 800-38A.
|
||||||
|
package aes12
|
||||||
|
|
||||||
|
// A Block represents an implementation of block cipher
|
||||||
|
// using a given key. It provides the capability to encrypt
|
||||||
|
// or decrypt individual blocks. The mode implementations
|
||||||
|
// extend that capability to streams of blocks.
|
||||||
|
type Block interface {
|
||||||
|
// BlockSize returns the cipher's block size.
|
||||||
|
BlockSize() int
|
||||||
|
|
||||||
|
// Encrypt encrypts the first block in src into dst.
|
||||||
|
// Dst and src may point at the same memory.
|
||||||
|
Encrypt(dst, src []byte)
|
||||||
|
|
||||||
|
// Decrypt decrypts the first block in src into dst.
|
||||||
|
// Dst and src may point at the same memory.
|
||||||
|
Decrypt(dst, src []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Stream represents a stream cipher.
|
||||||
|
type Stream interface {
|
||||||
|
// XORKeyStream XORs each byte in the given slice with a byte from the
|
||||||
|
// cipher's key stream. Dst and src may point to the same memory.
|
||||||
|
// If len(dst) < len(src), XORKeyStream should panic. It is acceptable
|
||||||
|
// to pass a dst bigger than src, and in that case, XORKeyStream will
|
||||||
|
// only update dst[:len(src)] and will not touch the rest of dst.
|
||||||
|
XORKeyStream(dst, src []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A BlockMode represents a block cipher running in a block-based mode (CBC,
|
||||||
|
// ECB etc).
|
||||||
|
type BlockMode interface {
|
||||||
|
// BlockSize returns the mode's block size.
|
||||||
|
BlockSize() int
|
||||||
|
|
||||||
|
// CryptBlocks encrypts or decrypts a number of blocks. The length of
|
||||||
|
// src must be a multiple of the block size. Dst and src may point to
|
||||||
|
// the same memory.
|
||||||
|
CryptBlocks(dst, src []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility routines
|
||||||
|
|
||||||
|
func dup(p []byte) []byte {
|
||||||
|
q := make([]byte, len(p))
|
||||||
|
copy(q, p)
|
||||||
|
return q
|
||||||
|
}
|
79
vendor/github.com/lucas-clemente/aes12/cipher_amd64.go
generated
vendored
Normal file
79
vendor/github.com/lucas-clemente/aes12/cipher_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2012 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 aes12
|
||||||
|
|
||||||
|
// defined in asm_amd64.s
|
||||||
|
func hasAsm() bool
|
||||||
|
func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
|
||||||
|
|
||||||
|
type aesCipherAsm struct {
|
||||||
|
aesCipher
|
||||||
|
}
|
||||||
|
|
||||||
|
var useAsm = hasAsm()
|
||||||
|
|
||||||
|
func newCipher(key []byte) (Block, error) {
|
||||||
|
if !useAsm {
|
||||||
|
return newCipherGeneric(key)
|
||||||
|
}
|
||||||
|
n := len(key) + 28
|
||||||
|
c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}}
|
||||||
|
rounds := 10
|
||||||
|
switch len(key) {
|
||||||
|
case 128 / 8:
|
||||||
|
rounds = 10
|
||||||
|
case 192 / 8:
|
||||||
|
rounds = 12
|
||||||
|
case 256 / 8:
|
||||||
|
rounds = 14
|
||||||
|
}
|
||||||
|
expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0])
|
||||||
|
if hasGCMAsm() {
|
||||||
|
return &aesCipherGCM{c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aesCipherAsm) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
|
func (c *aesCipherAsm) Encrypt(dst, src []byte) {
|
||||||
|
if len(src) < BlockSize {
|
||||||
|
panic("crypto/aes: input not full block")
|
||||||
|
}
|
||||||
|
if len(dst) < BlockSize {
|
||||||
|
panic("crypto/aes: output not full block")
|
||||||
|
}
|
||||||
|
encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aesCipherAsm) Decrypt(dst, src []byte) {
|
||||||
|
if len(src) < BlockSize {
|
||||||
|
panic("crypto/aes: input not full block")
|
||||||
|
}
|
||||||
|
if len(dst) < BlockSize {
|
||||||
|
panic("crypto/aes: output not full block")
|
||||||
|
}
|
||||||
|
decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandKey is used by BenchmarkExpand to ensure that the asm implementation
|
||||||
|
// of key expansion is used for the benchmark when it is available.
|
||||||
|
func expandKey(key []byte, enc, dec []uint32) {
|
||||||
|
if useAsm {
|
||||||
|
rounds := 10 // rounds needed for AES128
|
||||||
|
switch len(key) {
|
||||||
|
case 192 / 8:
|
||||||
|
rounds = 12
|
||||||
|
case 256 / 8:
|
||||||
|
rounds = 14
|
||||||
|
}
|
||||||
|
expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
|
||||||
|
} else {
|
||||||
|
expandKeyGo(key, enc, dec)
|
||||||
|
}
|
||||||
|
}
|
22
vendor/github.com/lucas-clemente/aes12/cipher_generic.go
generated
vendored
Normal file
22
vendor/github.com/lucas-clemente/aes12/cipher_generic.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build !amd64,!s390x
|
||||||
|
|
||||||
|
package aes12
|
||||||
|
|
||||||
|
// newCipher calls the newCipherGeneric function
|
||||||
|
// directly. Platforms with hardware accelerated
|
||||||
|
// implementations of AES should implement their
|
||||||
|
// own version of newCipher (which may then call
|
||||||
|
// newCipherGeneric if needed).
|
||||||
|
func newCipher(key []byte) (Block, error) {
|
||||||
|
return newCipherGeneric(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandKey is used by BenchmarkExpand and should
|
||||||
|
// call an assembly implementation if one is available.
|
||||||
|
func expandKey(key []byte, enc, dec []uint32) {
|
||||||
|
expandKeyGo(key, enc, dec)
|
||||||
|
}
|
358
vendor/github.com/lucas-clemente/aes12/const.go
generated
vendored
Normal file
358
vendor/github.com/lucas-clemente/aes12/const.go
generated
vendored
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
// Copyright 2009 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 aes12 implements AES encryption (formerly Rijndael), as defined in
|
||||||
|
// U.S. Federal Information Processing Standards Publication 197.
|
||||||
|
package aes12
|
||||||
|
|
||||||
|
// This file contains AES constants - 8720 bytes of initialized data.
|
||||||
|
|
||||||
|
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
|
||||||
|
// AES is based on the mathematical behavior of binary polynomials
|
||||||
|
// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1.
|
||||||
|
// Addition of these binary polynomials corresponds to binary xor.
|
||||||
|
// Reducing mod poly corresponds to binary xor with poly every
|
||||||
|
// time a 0x100 bit appears.
|
||||||
|
const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x³ + x + 1
|
||||||
|
|
||||||
|
// Powers of x mod poly in GF(2).
|
||||||
|
var powx = [16]byte{
|
||||||
|
0x01,
|
||||||
|
0x02,
|
||||||
|
0x04,
|
||||||
|
0x08,
|
||||||
|
0x10,
|
||||||
|
0x20,
|
||||||
|
0x40,
|
||||||
|
0x80,
|
||||||
|
0x1b,
|
||||||
|
0x36,
|
||||||
|
0x6c,
|
||||||
|
0xd8,
|
||||||
|
0xab,
|
||||||
|
0x4d,
|
||||||
|
0x9a,
|
||||||
|
0x2f,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIPS-197 Figure 7. S-box substitution values in hexadecimal format.
|
||||||
|
var sbox0 = [256]byte{
|
||||||
|
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||||
|
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||||
|
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||||
|
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||||
|
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||||
|
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||||
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||||
|
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||||
|
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||||
|
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||||
|
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||||
|
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||||
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||||
|
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||||
|
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||||
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIPS-197 Figure 14. Inverse S-box substitution values in hexadecimal format.
|
||||||
|
var sbox1 = [256]byte{
|
||||||
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||||
|
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||||
|
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||||
|
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||||
|
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||||
|
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||||
|
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||||
|
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||||
|
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||||
|
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||||
|
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||||
|
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||||
|
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||||
|
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||||
|
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||||
|
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup tables for encryption.
|
||||||
|
// These can be recomputed by adapting the tests in aes_test.go.
|
||||||
|
|
||||||
|
var te0 = [256]uint32{
|
||||||
|
0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
|
||||||
|
0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
|
||||||
|
0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
|
||||||
|
0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
|
||||||
|
0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
|
||||||
|
0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
|
||||||
|
0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
|
||||||
|
0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
|
||||||
|
0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
|
||||||
|
0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
|
||||||
|
0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
|
||||||
|
0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
|
||||||
|
0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
|
||||||
|
0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
|
||||||
|
0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
|
||||||
|
0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
|
||||||
|
0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
|
||||||
|
0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
|
||||||
|
0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
|
||||||
|
0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
|
||||||
|
0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
|
||||||
|
0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
|
||||||
|
0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
|
||||||
|
0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
|
||||||
|
0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
|
||||||
|
0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
|
||||||
|
0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
|
||||||
|
0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
|
||||||
|
0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
|
||||||
|
0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
|
||||||
|
0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
|
||||||
|
0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
|
||||||
|
}
|
||||||
|
var te1 = [256]uint32{
|
||||||
|
0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
|
||||||
|
0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
|
||||||
|
0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
|
||||||
|
0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
|
||||||
|
0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,
|
||||||
|
0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
|
||||||
|
0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,
|
||||||
|
0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
|
||||||
|
0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
|
||||||
|
0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
|
||||||
|
0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
|
||||||
|
0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
|
||||||
|
0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
|
||||||
|
0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
|
||||||
|
0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
|
||||||
|
0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,
|
||||||
|
0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
|
||||||
|
0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
|
||||||
|
0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
|
||||||
|
0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
|
||||||
|
0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
|
||||||
|
0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
|
||||||
|
0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
|
||||||
|
0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
|
||||||
|
0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
|
||||||
|
0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
|
||||||
|
0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
|
||||||
|
0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
|
||||||
|
0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
|
||||||
|
0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
|
||||||
|
0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
|
||||||
|
0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
|
||||||
|
}
|
||||||
|
var te2 = [256]uint32{
|
||||||
|
0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
|
||||||
|
0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
|
||||||
|
0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
|
||||||
|
0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
|
||||||
|
0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
|
||||||
|
0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
|
||||||
|
0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,
|
||||||
|
0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
|
||||||
|
0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
|
||||||
|
0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
|
||||||
|
0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
|
||||||
|
0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
|
||||||
|
0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
|
||||||
|
0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
|
||||||
|
0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
|
||||||
|
0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
|
||||||
|
0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
|
||||||
|
0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
|
||||||
|
0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
|
||||||
|
0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,
|
||||||
|
0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
|
||||||
|
0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
|
||||||
|
0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
|
||||||
|
0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
|
||||||
|
0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
|
||||||
|
0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
|
||||||
|
0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
|
||||||
|
0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
|
||||||
|
0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
|
||||||
|
0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
|
||||||
|
0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
|
||||||
|
0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
|
||||||
|
}
|
||||||
|
var te3 = [256]uint32{
|
||||||
|
0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
|
||||||
|
0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
|
||||||
|
0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
|
||||||
|
0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
|
||||||
|
0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
|
||||||
|
0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
|
||||||
|
0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,
|
||||||
|
0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
|
||||||
|
0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
|
||||||
|
0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
|
||||||
|
0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
|
||||||
|
0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
|
||||||
|
0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
|
||||||
|
0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
|
||||||
|
0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
|
||||||
|
0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
|
||||||
|
0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
|
||||||
|
0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
|
||||||
|
0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
|
||||||
|
0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,
|
||||||
|
0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
|
||||||
|
0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
|
||||||
|
0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
|
||||||
|
0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
|
||||||
|
0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
|
||||||
|
0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
|
||||||
|
0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
|
||||||
|
0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
|
||||||
|
0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
|
||||||
|
0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
|
||||||
|
0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
|
||||||
|
0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup tables for decryption.
|
||||||
|
// These can be recomputed by adapting the tests in aes_test.go.
|
||||||
|
|
||||||
|
var td0 = [256]uint32{
|
||||||
|
0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
|
||||||
|
0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
|
||||||
|
0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
|
||||||
|
0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
|
||||||
|
0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
|
||||||
|
0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
|
||||||
|
0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
|
||||||
|
0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
|
||||||
|
0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
|
||||||
|
0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
|
||||||
|
0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
|
||||||
|
0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
|
||||||
|
0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
|
||||||
|
0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
|
||||||
|
0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
|
||||||
|
0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
|
||||||
|
0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
|
||||||
|
0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
|
||||||
|
0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
|
||||||
|
0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
|
||||||
|
0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
|
||||||
|
0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
|
||||||
|
0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
|
||||||
|
0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
|
||||||
|
0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
|
||||||
|
0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
|
||||||
|
0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
|
||||||
|
0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
|
||||||
|
0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
|
||||||
|
0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
|
||||||
|
0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
|
||||||
|
0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,
|
||||||
|
}
|
||||||
|
var td1 = [256]uint32{
|
||||||
|
0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
|
||||||
|
0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
|
||||||
|
0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
|
||||||
|
0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
|
||||||
|
0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
|
||||||
|
0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
|
||||||
|
0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
|
||||||
|
0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
|
||||||
|
0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
|
||||||
|
0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,
|
||||||
|
0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,
|
||||||
|
0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
|
||||||
|
0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
|
||||||
|
0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
|
||||||
|
0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
|
||||||
|
0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
|
||||||
|
0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
|
||||||
|
0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
|
||||||
|
0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
|
||||||
|
0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
|
||||||
|
0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
|
||||||
|
0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
|
||||||
|
0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
|
||||||
|
0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
|
||||||
|
0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
|
||||||
|
0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,
|
||||||
|
0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
|
||||||
|
0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
|
||||||
|
0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
|
||||||
|
0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
|
||||||
|
0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,
|
||||||
|
0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,
|
||||||
|
}
|
||||||
|
var td2 = [256]uint32{
|
||||||
|
0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,
|
||||||
|
0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
|
||||||
|
0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
|
||||||
|
0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
|
||||||
|
0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
|
||||||
|
0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
|
||||||
|
0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,
|
||||||
|
0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
|
||||||
|
0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
|
||||||
|
0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
|
||||||
|
0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
|
||||||
|
0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
|
||||||
|
0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
|
||||||
|
0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
|
||||||
|
0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
|
||||||
|
0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
|
||||||
|
0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
|
||||||
|
0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
|
||||||
|
0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
|
||||||
|
0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
|
||||||
|
0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
|
||||||
|
0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
|
||||||
|
0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
|
||||||
|
0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
|
||||||
|
0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
|
||||||
|
0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
|
||||||
|
0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
|
||||||
|
0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
|
||||||
|
0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
|
||||||
|
0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
|
||||||
|
0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
|
||||||
|
0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,
|
||||||
|
}
|
||||||
|
var td3 = [256]uint32{
|
||||||
|
0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
|
||||||
|
0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
|
||||||
|
0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
|
||||||
|
0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
|
||||||
|
0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
|
||||||
|
0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
|
||||||
|
0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
|
||||||
|
0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,
|
||||||
|
0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
|
||||||
|
0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
|
||||||
|
0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,
|
||||||
|
0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
|
||||||
|
0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
|
||||||
|
0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
|
||||||
|
0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
|
||||||
|
0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
|
||||||
|
0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
|
||||||
|
0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
|
||||||
|
0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
|
||||||
|
0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
|
||||||
|
0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
|
||||||
|
0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
|
||||||
|
0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
|
||||||
|
0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
|
||||||
|
0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
|
||||||
|
0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
|
||||||
|
0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
|
||||||
|
0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
|
||||||
|
0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
|
||||||
|
0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
|
||||||
|
0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,
|
||||||
|
0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
|
||||||
|
}
|
401
vendor/github.com/lucas-clemente/aes12/gcm.go
generated
vendored
Normal file
401
vendor/github.com/lucas-clemente/aes12/gcm.go
generated
vendored
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
// Copyright 2013 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 aes12
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AEAD is a cipher mode providing authenticated encryption with associated
|
||||||
|
// data. For a description of the methodology, see
|
||||||
|
// https://en.wikipedia.org/wiki/Authenticated_encryption
|
||||||
|
type AEAD interface {
|
||||||
|
// NonceSize returns the size of the nonce that must be passed to Seal
|
||||||
|
// and Open.
|
||||||
|
NonceSize() int
|
||||||
|
|
||||||
|
// Overhead returns the maximum difference between the lengths of a
|
||||||
|
// plaintext and its ciphertext.
|
||||||
|
Overhead() int
|
||||||
|
|
||||||
|
// Seal encrypts and authenticates plaintext, authenticates the
|
||||||
|
// additional data and appends the result to dst, returning the updated
|
||||||
|
// slice. The nonce must be NonceSize() bytes long and unique for all
|
||||||
|
// time, for a given key.
|
||||||
|
//
|
||||||
|
// The plaintext and dst may alias exactly or not at all. To reuse
|
||||||
|
// plaintext's storage for the encrypted output, use plaintext[:0] as dst.
|
||||||
|
Seal(dst, nonce, plaintext, additionalData []byte) []byte
|
||||||
|
|
||||||
|
// Open decrypts and authenticates ciphertext, authenticates the
|
||||||
|
// additional data and, if successful, appends the resulting plaintext
|
||||||
|
// to dst, returning the updated slice. The nonce must be NonceSize()
|
||||||
|
// bytes long and both it and the additional data must match the
|
||||||
|
// value passed to Seal.
|
||||||
|
//
|
||||||
|
// The ciphertext and dst may alias exactly or not at all. To reuse
|
||||||
|
// ciphertext's storage for the decrypted output, use ciphertext[:0] as dst.
|
||||||
|
//
|
||||||
|
// Even if the function fails, the contents of dst, up to its capacity,
|
||||||
|
// may be overwritten.
|
||||||
|
Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcmAble is an interface implemented by ciphers that have a specific optimized
|
||||||
|
// implementation of GCM, like crypto/aes. NewGCM will check for this interface
|
||||||
|
// and return the specific AEAD if found.
|
||||||
|
type gcmAble interface {
|
||||||
|
NewGCM(int) (AEAD, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
|
||||||
|
// standard and make getUint64 suitable for marshaling these values, the bits
|
||||||
|
// are stored backwards. For example:
|
||||||
|
// the coefficient of x⁰ can be obtained by v.low >> 63.
|
||||||
|
// the coefficient of x⁶³ can be obtained by v.low & 1.
|
||||||
|
// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
|
||||||
|
// the coefficient of x¹²⁷ can be obtained by v.high & 1.
|
||||||
|
type gcmFieldElement struct {
|
||||||
|
low, high uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcm represents a Galois Counter Mode with a specific key. See
|
||||||
|
// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
|
||||||
|
type gcm struct {
|
||||||
|
cipher Block
|
||||||
|
nonceSize int
|
||||||
|
// productTable contains the first sixteen powers of the key, H.
|
||||||
|
// However, they are in bit reversed order. See NewGCMWithNonceSize.
|
||||||
|
productTable [16]gcmFieldElement
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
|
||||||
|
// with the standard nonce length.
|
||||||
|
func NewGCM(cipher Block) (AEAD, error) {
|
||||||
|
return NewGCMWithNonceSize(cipher, gcmStandardNonceSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
|
||||||
|
// Counter Mode, which accepts nonces of the given length.
|
||||||
|
//
|
||||||
|
// Only use this function if you require compatibility with an existing
|
||||||
|
// cryptosystem that uses non-standard nonce lengths. All other users should use
|
||||||
|
// NewGCM, which is faster and more resistant to misuse.
|
||||||
|
func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
|
||||||
|
if cipher, ok := cipher.(gcmAble); ok {
|
||||||
|
return cipher.NewGCM(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cipher.BlockSize() != gcmBlockSize {
|
||||||
|
return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
|
||||||
|
}
|
||||||
|
|
||||||
|
var key [gcmBlockSize]byte
|
||||||
|
cipher.Encrypt(key[:], key[:])
|
||||||
|
|
||||||
|
g := &gcm{cipher: cipher, nonceSize: size}
|
||||||
|
|
||||||
|
// We precompute 16 multiples of |key|. However, when we do lookups
|
||||||
|
// into this table we'll be using bits from a field element and
|
||||||
|
// therefore the bits will be in the reverse order. So normally one
|
||||||
|
// would expect, say, 4*key to be in index 4 of the table but due to
|
||||||
|
// this bit ordering it will actually be in index 0010 (base 2) = 2.
|
||||||
|
x := gcmFieldElement{
|
||||||
|
getUint64(key[:8]),
|
||||||
|
getUint64(key[8:]),
|
||||||
|
}
|
||||||
|
g.productTable[reverseBits(1)] = x
|
||||||
|
|
||||||
|
for i := 2; i < 16; i += 2 {
|
||||||
|
g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)])
|
||||||
|
g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
gcmBlockSize = 16
|
||||||
|
gcmTagSize = 12
|
||||||
|
gcmStandardNonceSize = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *gcm) NonceSize() int {
|
||||||
|
return g.nonceSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*gcm) Overhead() int {
|
||||||
|
return gcmTagSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||||
|
if len(nonce) != g.nonceSize {
|
||||||
|
panic("cipher: incorrect nonce length given to GCM")
|
||||||
|
}
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
|
||||||
|
|
||||||
|
var counter, tagMask [gcmBlockSize]byte
|
||||||
|
g.deriveCounter(&counter, nonce)
|
||||||
|
|
||||||
|
g.cipher.Encrypt(tagMask[:], counter[:])
|
||||||
|
gcmInc32(&counter)
|
||||||
|
|
||||||
|
g.counterCrypt(out, plaintext, &counter)
|
||||||
|
|
||||||
|
tag := make([]byte, 16)
|
||||||
|
g.auth(tag, out[:len(plaintext)], data, &tagMask)
|
||||||
|
copy(ret[len(ret)-12:], tag)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var errOpen = errors.New("cipher: message authentication failed")
|
||||||
|
|
||||||
|
func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||||
|
if len(nonce) != g.nonceSize {
|
||||||
|
panic("cipher: incorrect nonce length given to GCM")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < gcmTagSize {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
tag := ciphertext[len(ciphertext)-gcmTagSize:]
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
|
||||||
|
|
||||||
|
var counter, tagMask [gcmBlockSize]byte
|
||||||
|
g.deriveCounter(&counter, nonce)
|
||||||
|
|
||||||
|
g.cipher.Encrypt(tagMask[:], counter[:])
|
||||||
|
gcmInc32(&counter)
|
||||||
|
|
||||||
|
var expectedTag [gcmBlockSize]byte
|
||||||
|
g.auth(expectedTag[:], ciphertext, data, &tagMask)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare(expectedTag[:gcmTagSize], tag) != 1 {
|
||||||
|
// The AESNI code decrypts and authenticates concurrently, and
|
||||||
|
// so overwrites dst in the event of a tag mismatch. That
|
||||||
|
// behaviour is mimicked here in order to be consistent across
|
||||||
|
// platforms.
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
g.counterCrypt(out, ciphertext, &counter)
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverseBits reverses the order of the bits of 4-bit number in i.
|
||||||
|
func reverseBits(i int) int {
|
||||||
|
i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
|
||||||
|
i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcmAdd adds two elements of GF(2¹²⁸) and returns the sum.
|
||||||
|
func gcmAdd(x, y *gcmFieldElement) gcmFieldElement {
|
||||||
|
// Addition in a characteristic 2 field is just XOR.
|
||||||
|
return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcmDouble returns the result of doubling an element of GF(2¹²⁸).
|
||||||
|
func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) {
|
||||||
|
msbSet := x.high&1 == 1
|
||||||
|
|
||||||
|
// Because of the bit-ordering, doubling is actually a right shift.
|
||||||
|
double.high = x.high >> 1
|
||||||
|
double.high |= x.low << 63
|
||||||
|
double.low = x.low >> 1
|
||||||
|
|
||||||
|
// If the most-significant bit was set before shifting then it,
|
||||||
|
// conceptually, becomes a term of x^128. This is greater than the
|
||||||
|
// irreducible polynomial so the result has to be reduced. The
|
||||||
|
// irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
|
||||||
|
// eliminate the term at x^128 which also means subtracting the other
|
||||||
|
// four terms. In characteristic 2 fields, subtraction == addition ==
|
||||||
|
// XOR.
|
||||||
|
if msbSet {
|
||||||
|
double.low ^= 0xe100000000000000
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var gcmReductionTable = []uint16{
|
||||||
|
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
|
||||||
|
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul sets y to y*H, where H is the GCM key, fixed during NewGCMWithNonceSize.
|
||||||
|
func (g *gcm) mul(y *gcmFieldElement) {
|
||||||
|
var z gcmFieldElement
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
word := y.high
|
||||||
|
if i == 1 {
|
||||||
|
word = y.low
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplication works by multiplying z by 16 and adding in
|
||||||
|
// one of the precomputed multiples of H.
|
||||||
|
for j := 0; j < 64; j += 4 {
|
||||||
|
msw := z.high & 0xf
|
||||||
|
z.high >>= 4
|
||||||
|
z.high |= z.low << 60
|
||||||
|
z.low >>= 4
|
||||||
|
z.low ^= uint64(gcmReductionTable[msw]) << 48
|
||||||
|
|
||||||
|
// the values in |table| are ordered for
|
||||||
|
// little-endian bit positions. See the comment
|
||||||
|
// in NewGCMWithNonceSize.
|
||||||
|
t := &g.productTable[word&0xf]
|
||||||
|
|
||||||
|
z.low ^= t.low
|
||||||
|
z.high ^= t.high
|
||||||
|
word >>= 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*y = z
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateBlocks extends y with more polynomial terms from blocks, based on
|
||||||
|
// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks.
|
||||||
|
func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
|
||||||
|
for len(blocks) > 0 {
|
||||||
|
y.low ^= getUint64(blocks)
|
||||||
|
y.high ^= getUint64(blocks[8:])
|
||||||
|
g.mul(y)
|
||||||
|
blocks = blocks[gcmBlockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update extends y with more polynomial terms from data. If data is not a
|
||||||
|
// multiple of gcmBlockSize bytes long then the remainder is zero padded.
|
||||||
|
func (g *gcm) update(y *gcmFieldElement, data []byte) {
|
||||||
|
fullBlocks := (len(data) >> 4) << 4
|
||||||
|
g.updateBlocks(y, data[:fullBlocks])
|
||||||
|
|
||||||
|
if len(data) != fullBlocks {
|
||||||
|
var partialBlock [gcmBlockSize]byte
|
||||||
|
copy(partialBlock[:], data[fullBlocks:])
|
||||||
|
g.updateBlocks(y, partialBlock[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcmInc32 treats the final four bytes of counterBlock as a big-endian value
|
||||||
|
// and increments it.
|
||||||
|
func gcmInc32(counterBlock *[16]byte) {
|
||||||
|
for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- {
|
||||||
|
counterBlock[i]++
|
||||||
|
if counterBlock[i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||||
|
// slice with the contents of the given slice followed by that many bytes and a
|
||||||
|
// second slice that aliases into it and contains only the extra bytes. If the
|
||||||
|
// original slice has sufficient capacity then no allocation is performed.
|
||||||
|
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||||
|
if total := len(in) + n; cap(in) >= total {
|
||||||
|
head = in[:total]
|
||||||
|
} else {
|
||||||
|
head = make([]byte, total)
|
||||||
|
copy(head, in)
|
||||||
|
}
|
||||||
|
tail = head[len(in):]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// counterCrypt crypts in to out using g.cipher in counter mode.
|
||||||
|
func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
|
||||||
|
var mask [gcmBlockSize]byte
|
||||||
|
|
||||||
|
for len(in) >= gcmBlockSize {
|
||||||
|
g.cipher.Encrypt(mask[:], counter[:])
|
||||||
|
gcmInc32(counter)
|
||||||
|
|
||||||
|
xorWords(out, in, mask[:])
|
||||||
|
out = out[gcmBlockSize:]
|
||||||
|
in = in[gcmBlockSize:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(in) > 0 {
|
||||||
|
g.cipher.Encrypt(mask[:], counter[:])
|
||||||
|
gcmInc32(counter)
|
||||||
|
xorBytes(out, in, mask[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deriveCounter computes the initial GCM counter state from the given nonce.
|
||||||
|
// See NIST SP 800-38D, section 7.1. This assumes that counter is filled with
|
||||||
|
// zeros on entry.
|
||||||
|
func (g *gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
|
||||||
|
// GCM has two modes of operation with respect to the initial counter
|
||||||
|
// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
|
||||||
|
// for nonces of other lengths. For a 96-bit nonce, the nonce, along
|
||||||
|
// with a four-byte big-endian counter starting at one, is used
|
||||||
|
// directly as the starting counter. For other nonce sizes, the counter
|
||||||
|
// is computed by passing it through the GHASH function.
|
||||||
|
if len(nonce) == gcmStandardNonceSize {
|
||||||
|
copy(counter[:], nonce)
|
||||||
|
counter[gcmBlockSize-1] = 1
|
||||||
|
} else {
|
||||||
|
var y gcmFieldElement
|
||||||
|
g.update(&y, nonce)
|
||||||
|
y.high ^= uint64(len(nonce)) * 8
|
||||||
|
g.mul(&y)
|
||||||
|
putUint64(counter[:8], y.low)
|
||||||
|
putUint64(counter[8:], y.high)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// auth calculates GHASH(ciphertext, additionalData), masks the result with
|
||||||
|
// tagMask and writes the result to out.
|
||||||
|
func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmBlockSize]byte) {
|
||||||
|
var y gcmFieldElement
|
||||||
|
g.update(&y, additionalData)
|
||||||
|
g.update(&y, ciphertext)
|
||||||
|
|
||||||
|
y.low ^= uint64(len(additionalData)) * 8
|
||||||
|
y.high ^= uint64(len(ciphertext)) * 8
|
||||||
|
|
||||||
|
g.mul(&y)
|
||||||
|
|
||||||
|
putUint64(out, y.low)
|
||||||
|
putUint64(out[8:], y.high)
|
||||||
|
|
||||||
|
xorWords(out, out, tagMask[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUint64(data []byte) uint64 {
|
||||||
|
r := uint64(data[0])<<56 |
|
||||||
|
uint64(data[1])<<48 |
|
||||||
|
uint64(data[2])<<40 |
|
||||||
|
uint64(data[3])<<32 |
|
||||||
|
uint64(data[4])<<24 |
|
||||||
|
uint64(data[5])<<16 |
|
||||||
|
uint64(data[6])<<8 |
|
||||||
|
uint64(data[7])
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func putUint64(out []byte, v uint64) {
|
||||||
|
out[0] = byte(v >> 56)
|
||||||
|
out[1] = byte(v >> 48)
|
||||||
|
out[2] = byte(v >> 40)
|
||||||
|
out[3] = byte(v >> 32)
|
||||||
|
out[4] = byte(v >> 24)
|
||||||
|
out[5] = byte(v >> 16)
|
||||||
|
out[6] = byte(v >> 8)
|
||||||
|
out[7] = byte(v)
|
||||||
|
}
|
1277
vendor/github.com/lucas-clemente/aes12/gcm_amd64.s
generated
vendored
Normal file
1277
vendor/github.com/lucas-clemente/aes12/gcm_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
84
vendor/github.com/lucas-clemente/aes12/xor.go
generated
vendored
Normal file
84
vendor/github.com/lucas-clemente/aes12/xor.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright 2013 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 aes12
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
|
||||||
|
|
||||||
|
// fastXORBytes xors in bulk. It only works on architectures that
|
||||||
|
// support unaligned read/writes.
|
||||||
|
func fastXORBytes(dst, a, b []byte) int {
|
||||||
|
n := len(a)
|
||||||
|
if len(b) < n {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := n / wordSize
|
||||||
|
if w > 0 {
|
||||||
|
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
||||||
|
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
||||||
|
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
||||||
|
for i := 0; i < w; i++ {
|
||||||
|
dw[i] = aw[i] ^ bw[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := (n - n%wordSize); i < n; i++ {
|
||||||
|
dst[i] = a[i] ^ b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeXORBytes(dst, a, b []byte) int {
|
||||||
|
n := len(a)
|
||||||
|
if len(b) < n {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
dst[i] = a[i] ^ b[i]
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
|
||||||
|
// space. Returns the number of bytes xor'd.
|
||||||
|
func xorBytes(dst, a, b []byte) int {
|
||||||
|
if supportsUnaligned {
|
||||||
|
return fastXORBytes(dst, a, b)
|
||||||
|
} else {
|
||||||
|
// TODO(hanwen): if (dst, a, b) have common alignment
|
||||||
|
// we could still try fastXORBytes. It is not clear
|
||||||
|
// how often this happens, and it's only worth it if
|
||||||
|
// the block encryption itself is hardware
|
||||||
|
// accelerated.
|
||||||
|
return safeXORBytes(dst, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
|
||||||
|
// The arguments are assumed to be of equal length.
|
||||||
|
func fastXORWords(dst, a, b []byte) {
|
||||||
|
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
||||||
|
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
||||||
|
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
||||||
|
n := len(b) / wordSize
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
dw[i] = aw[i] ^ bw[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func xorWords(dst, a, b []byte) {
|
||||||
|
if supportsUnaligned {
|
||||||
|
fastXORWords(dst, a, b)
|
||||||
|
} else {
|
||||||
|
safeXORBytes(dst, a, b)
|
||||||
|
}
|
||||||
|
}
|
21
vendor/github.com/lucas-clemente/fnv128a/LICENSE
generated
vendored
Normal file
21
vendor/github.com/lucas-clemente/fnv128a/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Lucas Clemente
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
3
vendor/github.com/lucas-clemente/fnv128a/README.md
generated
vendored
Normal file
3
vendor/github.com/lucas-clemente/fnv128a/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# fnv128a
|
||||||
|
|
||||||
|
Implementation of the FNV-1a 128bit hash in go
|
87
vendor/github.com/lucas-clemente/fnv128a/fnv128a.go
generated
vendored
Normal file
87
vendor/github.com/lucas-clemente/fnv128a/fnv128a.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// Package fnv128a implements FNV-1 and FNV-1a, non-cryptographic hash functions
|
||||||
|
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
|
||||||
|
// See https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function.
|
||||||
|
//
|
||||||
|
// Write() algorithm taken and modified from github.com/romain-jacotin/quic
|
||||||
|
package fnv128a
|
||||||
|
|
||||||
|
import "hash"
|
||||||
|
|
||||||
|
// Hash128 is the common interface implemented by all 128-bit hash functions.
|
||||||
|
type Hash128 interface {
|
||||||
|
hash.Hash
|
||||||
|
Sum128() (uint64, uint64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sum128a struct {
|
||||||
|
v0, v1, v2, v3 uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Hash128 = &sum128a{}
|
||||||
|
|
||||||
|
// New1 returns a new 128-bit FNV-1a hash.Hash.
|
||||||
|
func New() Hash128 {
|
||||||
|
s := &sum128a{}
|
||||||
|
s.Reset()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sum128a) Reset() {
|
||||||
|
s.v0 = 0x6295C58D
|
||||||
|
s.v1 = 0x62B82175
|
||||||
|
s.v2 = 0x07BB0142
|
||||||
|
s.v3 = 0x6C62272E
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sum128a) Sum128() (uint64, uint64) {
|
||||||
|
return s.v3<<32 | s.v2, s.v1<<32 | s.v0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sum128a) Write(data []byte) (int, error) {
|
||||||
|
var t0, t1, t2, t3 uint64
|
||||||
|
const fnv128PrimeLow = 0x0000013B
|
||||||
|
const fnv128PrimeShift = 24
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
// xor the bottom with the current octet
|
||||||
|
s.v0 ^= uint64(v)
|
||||||
|
|
||||||
|
// multiply by the 128 bit FNV magic prime mod 2^128
|
||||||
|
// fnv_prime = 309485009821345068724781371 (decimal)
|
||||||
|
// = 0x0000000001000000000000000000013B (hexadecimal)
|
||||||
|
// = 0x00000000 0x01000000 0x00000000 0x0000013B (in 4*32 words)
|
||||||
|
// = 0x0 1<<fnv128PrimeShift 0x0 fnv128PrimeLow
|
||||||
|
//
|
||||||
|
// fnv128PrimeLow = 0x0000013B
|
||||||
|
// fnv128PrimeShift = 24
|
||||||
|
|
||||||
|
// multiply by the lowest order digit base 2^32 and by the other non-zero digit
|
||||||
|
t0 = s.v0 * fnv128PrimeLow
|
||||||
|
t1 = s.v1 * fnv128PrimeLow
|
||||||
|
t2 = s.v2*fnv128PrimeLow + s.v0<<fnv128PrimeShift
|
||||||
|
t3 = s.v3*fnv128PrimeLow + s.v1<<fnv128PrimeShift
|
||||||
|
|
||||||
|
// propagate carries
|
||||||
|
t1 += (t0 >> 32)
|
||||||
|
t2 += (t1 >> 32)
|
||||||
|
t3 += (t2 >> 32)
|
||||||
|
|
||||||
|
s.v0 = t0 & 0xffffffff
|
||||||
|
s.v1 = t1 & 0xffffffff
|
||||||
|
s.v2 = t2 & 0xffffffff
|
||||||
|
s.v3 = t3 // & 0xffffffff
|
||||||
|
// Doing a s.v3 &= 0xffffffff is not really needed since it simply
|
||||||
|
// removes multiples of 2^128. We can discard these excess bits
|
||||||
|
// outside of the loop when writing the hash in Little Endian.
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sum128a) Size() int { return 16 }
|
||||||
|
|
||||||
|
func (s *sum128a) BlockSize() int { return 1 }
|
||||||
|
|
||||||
|
func (s *sum128a) Sum(in []byte) []byte {
|
||||||
|
panic("FNV: not supported")
|
||||||
|
}
|
21
vendor/github.com/lucas-clemente/quic-go-certificates/LICENSE
generated
vendored
Normal file
21
vendor/github.com/lucas-clemente/quic-go-certificates/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Lucas Clemente
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
3
vendor/github.com/lucas-clemente/quic-go-certificates/README.md
generated
vendored
Normal file
3
vendor/github.com/lucas-clemente/quic-go-certificates/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# certsets
|
||||||
|
|
||||||
|
Common certificate sets for quic-go
|
5824
vendor/github.com/lucas-clemente/quic-go-certificates/cert_set_2.go
generated
vendored
Normal file
5824
vendor/github.com/lucas-clemente/quic-go-certificates/cert_set_2.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
5456
vendor/github.com/lucas-clemente/quic-go-certificates/cert_set_3.go
generated
vendored
Normal file
5456
vendor/github.com/lucas-clemente/quic-go-certificates/cert_set_3.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
34
vendor/github.com/lucas-clemente/quic-go-certificates/createCertSets.rb
generated
vendored
Executable file
34
vendor/github.com/lucas-clemente/quic-go-certificates/createCertSets.rb
generated
vendored
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
#
|
||||||
|
# Extract the common certificate sets from the chromium source to go
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# createCertSets.rb 1 ~/src/chromium/src/net/quic/crypto/common_cert_set_1*
|
||||||
|
# createCertSets.rb 2 ~/src/chromium/src/net/quic/crypto/common_cert_set_2*
|
||||||
|
|
||||||
|
n = ARGV.shift
|
||||||
|
mainFile = ARGV.shift
|
||||||
|
dataFiles = ARGV
|
||||||
|
|
||||||
|
data = "package certsets\n"
|
||||||
|
data += File.read(mainFile)
|
||||||
|
data += (dataFiles.map{|p| File.read(p)}).join
|
||||||
|
|
||||||
|
# Good enough
|
||||||
|
data.gsub!(/\/\*(.*?)\*\//m, '')
|
||||||
|
data.gsub!(/^#include.+/, '')
|
||||||
|
data.gsub!(/^#if 0(.*?)\n#endif/m, '')
|
||||||
|
|
||||||
|
data.gsub!(/^static const size_t kNumCerts.+/, '')
|
||||||
|
data.gsub!(/static const size_t kLens[^}]+};/m, '')
|
||||||
|
|
||||||
|
data.gsub!('static const unsigned char* const kCerts[] = {', "var CertSet#{n} = [][]byte{")
|
||||||
|
data.gsub!('static const uint64_t kHash = UINT64_C', "const CertSet#{n}Hash uint64 = ")
|
||||||
|
|
||||||
|
data.gsub!(/static const unsigned char kDERCert(\d+)\[\] = /, "var kDERCert\\1 = []byte")
|
||||||
|
|
||||||
|
data.gsub!(/kDERCert(\d+)/, "certSet#{n}Cert\\1")
|
||||||
|
|
||||||
|
File.write("cert_set_#{n}.go", data)
|
||||||
|
|
||||||
|
system("gofmt -w -s cert_set_#{n}.go")
|
17
vendor/github.com/lucas-clemente/quic-go/Changelog.md
generated
vendored
Normal file
17
vendor/github.com/lucas-clemente/quic-go/Changelog.md
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v0.6.0 (unreleased)
|
||||||
|
|
||||||
|
- Added `quic.Config` options for maximal flow control windows
|
||||||
|
- Add a `quic.Config` option for QUIC versions
|
||||||
|
- Add a `quic.Config` option to request truncation of the connection ID from a server
|
||||||
|
- Add a `quic.Config` option to configure the source address validation
|
||||||
|
- Add a `quic.Config` option to configure the handshake timeout
|
||||||
|
- Add a `quic.Config` option to configure keep-alive
|
||||||
|
- Implement `net.Conn`-style deadlines for streams
|
||||||
|
- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/lucas-clemente/quic-go) for details.
|
||||||
|
- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/lucas-clemente/quic-go/wiki/Logging) for more details.
|
||||||
|
- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper`
|
||||||
|
- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn`
|
||||||
|
- Drop support for Go 1.7.
|
||||||
|
- Various bugfixes
|
21
vendor/github.com/lucas-clemente/quic-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/lucas-clemente/quic-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 the quic-go authors & Google, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
72
vendor/github.com/lucas-clemente/quic-go/README.md
generated
vendored
Normal file
72
vendor/github.com/lucas-clemente/quic-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# A QUIC implementation in pure Go
|
||||||
|
|
||||||
|
<img src="docs/quic.png" width=303 height=124>
|
||||||
|
|
||||||
|
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/lucas-clemente/quic-go)
|
||||||
|
[![Linux Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=linux+build)](https://travis-ci.org/lucas-clemente/quic-go)
|
||||||
|
[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
|
||||||
|
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||||
|
|
||||||
|
quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
quic-go is compatible with the current version(s) of Google Chrome and QUIC as deployed on Google's servers. We're actively tracking the development of the Chrome code to ensure compatibility as the protocol evolves. In that process, we're dropping support for old QUIC versions.
|
||||||
|
As Google's QUIC versions are expected to converge towards the [IETF QUIC draft](https://github.com/quicwg/base-drafts), quic-go will eventually implement that draft.
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
|
||||||
|
We currently support Go 1.8+.
|
||||||
|
|
||||||
|
Installing and updating dependencies:
|
||||||
|
|
||||||
|
go get -t -u ./...
|
||||||
|
|
||||||
|
Running tests:
|
||||||
|
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
### Running the example server
|
||||||
|
|
||||||
|
go run example/main.go -www /var/www/
|
||||||
|
|
||||||
|
Using the `quic_client` from chromium:
|
||||||
|
|
||||||
|
quic_client --host=127.0.0.1 --port=6121 --v=1 https://quic.clemente.io
|
||||||
|
|
||||||
|
Using Chrome:
|
||||||
|
|
||||||
|
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/chrome --no-proxy-server --enable-quic --origin-to-force-quic-on=quic.clemente.io:443 --host-resolver-rules='MAP quic.clemente.io:443 127.0.0.1:6121' https://quic.clemente.io
|
||||||
|
|
||||||
|
### QUIC without HTTP/2
|
||||||
|
|
||||||
|
Take a look at [this echo example](example/echo/echo.go).
|
||||||
|
|
||||||
|
### Using the example client
|
||||||
|
|
||||||
|
go run example/client/main.go https://clemente.io
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### As a server
|
||||||
|
|
||||||
|
See the [example server](example/main.go) or try out [Caddy](https://github.com/mholt/caddy) (from version 0.9, [instructions here](https://github.com/mholt/caddy/wiki/QUIC)). Starting a QUIC server is very similar to the standard lib http in go:
|
||||||
|
|
||||||
|
```go
|
||||||
|
http.Handle("/", http.FileServer(http.Dir(wwwDir)))
|
||||||
|
h2quic.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
### As a client
|
||||||
|
|
||||||
|
See the [example client](example/client/main.go). Use a `h2quic.RoundTripper` as a `Transport` in a `http.Client`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
http.Client{
|
||||||
|
Transport: &h2quic.RoundTripper{},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with [want-help](https://github.com/lucas-clemente/quic-go/issues?q=is%3Aopen+is%3Aissue+label%3Awant-help). If you have any questions, please feel free to reach out by opening an issue or leaving a comment.
|
7
vendor/github.com/lucas-clemente/quic-go/ackhandler/_gen.go
generated
vendored
Normal file
7
vendor/github.com/lucas-clemente/quic-go/ackhandler/_gen.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/clipperhouse/linkedlist"
|
||||||
|
_ "github.com/clipperhouse/slice"
|
||||||
|
_ "github.com/clipperhouse/stringer"
|
||||||
|
)
|
32
vendor/github.com/lucas-clemente/quic-go/ackhandler/interfaces.go
generated
vendored
Normal file
32
vendor/github.com/lucas-clemente/quic-go/ackhandler/interfaces.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SentPacketHandler handles ACKs received for outgoing packets
|
||||||
|
type SentPacketHandler interface {
|
||||||
|
// SentPacket may modify the packet
|
||||||
|
SentPacket(packet *Packet) error
|
||||||
|
ReceivedAck(ackFrame *frames.AckFrame, withPacketNumber protocol.PacketNumber, recvTime time.Time) error
|
||||||
|
|
||||||
|
SendingAllowed() bool
|
||||||
|
GetStopWaitingFrame(force bool) *frames.StopWaitingFrame
|
||||||
|
DequeuePacketForRetransmission() (packet *Packet)
|
||||||
|
GetLeastUnacked() protocol.PacketNumber
|
||||||
|
|
||||||
|
GetAlarmTimeout() time.Time
|
||||||
|
OnAlarm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceivedPacketHandler handles ACKs needed to send for incoming packets
|
||||||
|
type ReceivedPacketHandler interface {
|
||||||
|
ReceivedPacket(packetNumber protocol.PacketNumber, shouldInstigateAck bool) error
|
||||||
|
ReceivedStopWaiting(*frames.StopWaitingFrame) error
|
||||||
|
|
||||||
|
GetAlarmTimeout() time.Time
|
||||||
|
GetAckFrame() *frames.AckFrame
|
||||||
|
}
|
34
vendor/github.com/lucas-clemente/quic-go/ackhandler/packet.go
generated
vendored
Normal file
34
vendor/github.com/lucas-clemente/quic-go/ackhandler/packet.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Packet is a packet
|
||||||
|
// +gen linkedlist
|
||||||
|
type Packet struct {
|
||||||
|
PacketNumber protocol.PacketNumber
|
||||||
|
Frames []frames.Frame
|
||||||
|
Length protocol.ByteCount
|
||||||
|
EncryptionLevel protocol.EncryptionLevel
|
||||||
|
|
||||||
|
SendTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFramesForRetransmission gets all the frames for retransmission
|
||||||
|
func (p *Packet) GetFramesForRetransmission() []frames.Frame {
|
||||||
|
var fs []frames.Frame
|
||||||
|
for _, frame := range p.Frames {
|
||||||
|
switch frame.(type) {
|
||||||
|
case *frames.AckFrame:
|
||||||
|
continue
|
||||||
|
case *frames.StopWaitingFrame:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fs = append(fs, frame)
|
||||||
|
}
|
||||||
|
return fs
|
||||||
|
}
|
214
vendor/github.com/lucas-clemente/quic-go/ackhandler/packet_linkedlist.go
generated
vendored
Normal file
214
vendor/github.com/lucas-clemente/quic-go/ackhandler/packet_linkedlist.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// Generated by: main
|
||||||
|
// TypeWriter: linkedlist
|
||||||
|
// Directive: +gen on Packet
|
||||||
|
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
// List is a modification of http://golang.org/pkg/container/list/
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// PacketElement is an element of a linked list.
|
||||||
|
type PacketElement struct {
|
||||||
|
// Next and previous pointers in the doubly-linked list of elements.
|
||||||
|
// To simplify the implementation, internally a list l is implemented
|
||||||
|
// as a ring, such that &l.root is both the next element of the last
|
||||||
|
// list element (l.Back()) and the previous element of the first list
|
||||||
|
// element (l.Front()).
|
||||||
|
next, prev *PacketElement
|
||||||
|
|
||||||
|
// The list to which this element belongs.
|
||||||
|
list *PacketList
|
||||||
|
|
||||||
|
// The value stored with this element.
|
||||||
|
Value Packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next list element or nil.
|
||||||
|
func (e *PacketElement) Next() *PacketElement {
|
||||||
|
if p := e.next; e.list != nil && p != &e.list.root {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev returns the previous list element or nil.
|
||||||
|
func (e *PacketElement) Prev() *PacketElement {
|
||||||
|
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketList represents a doubly linked list.
|
||||||
|
// The zero value for PacketList is an empty list ready to use.
|
||||||
|
type PacketList struct {
|
||||||
|
root PacketElement // sentinel list element, only &root, root.prev, and root.next are used
|
||||||
|
len int // current list length excluding (this) sentinel element
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes or clears list l.
|
||||||
|
func (l *PacketList) Init() *PacketList {
|
||||||
|
l.root.next = &l.root
|
||||||
|
l.root.prev = &l.root
|
||||||
|
l.len = 0
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPacketList returns an initialized list.
|
||||||
|
func NewPacketList() *PacketList { return new(PacketList).Init() }
|
||||||
|
|
||||||
|
// Len returns the number of elements of list l.
|
||||||
|
// The complexity is O(1).
|
||||||
|
func (l *PacketList) Len() int { return l.len }
|
||||||
|
|
||||||
|
// Front returns the first element of list l or nil.
|
||||||
|
func (l *PacketList) Front() *PacketElement {
|
||||||
|
if l.len == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.root.next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back returns the last element of list l or nil.
|
||||||
|
func (l *PacketList) Back() *PacketElement {
|
||||||
|
if l.len == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.root.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazyInit lazily initializes a zero PacketList value.
|
||||||
|
func (l *PacketList) lazyInit() {
|
||||||
|
if l.root.next == nil {
|
||||||
|
l.Init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert inserts e after at, increments l.len, and returns e.
|
||||||
|
func (l *PacketList) insert(e, at *PacketElement) *PacketElement {
|
||||||
|
n := at.next
|
||||||
|
at.next = e
|
||||||
|
e.prev = at
|
||||||
|
e.next = n
|
||||||
|
n.prev = e
|
||||||
|
e.list = l
|
||||||
|
l.len++
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertValue is a convenience wrapper for insert(&PacketElement{Value: v}, at).
|
||||||
|
func (l *PacketList) insertValue(v Packet, at *PacketElement) *PacketElement {
|
||||||
|
return l.insert(&PacketElement{Value: v}, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove removes e from its list, decrements l.len, and returns e.
|
||||||
|
func (l *PacketList) remove(e *PacketElement) *PacketElement {
|
||||||
|
e.prev.next = e.next
|
||||||
|
e.next.prev = e.prev
|
||||||
|
e.next = nil // avoid memory leaks
|
||||||
|
e.prev = nil // avoid memory leaks
|
||||||
|
e.list = nil
|
||||||
|
l.len--
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes e from l if e is an element of list l.
|
||||||
|
// It returns the element value e.Value.
|
||||||
|
func (l *PacketList) Remove(e *PacketElement) Packet {
|
||||||
|
if e.list == l {
|
||||||
|
// if e.list == l, l must have been initialized when e was inserted
|
||||||
|
// in l or l == nil (e is a zero PacketElement) and l.remove will crash
|
||||||
|
l.remove(e)
|
||||||
|
}
|
||||||
|
return e.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront inserts a new element e with value v at the front of list l and returns e.
|
||||||
|
func (l *PacketList) PushFront(v Packet) *PacketElement {
|
||||||
|
l.lazyInit()
|
||||||
|
return l.insertValue(v, &l.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack inserts a new element e with value v at the back of list l and returns e.
|
||||||
|
func (l *PacketList) PushBack(v Packet) *PacketElement {
|
||||||
|
l.lazyInit()
|
||||||
|
return l.insertValue(v, l.root.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
|
||||||
|
// If mark is not an element of l, the list is not modified.
|
||||||
|
func (l *PacketList) InsertBefore(v Packet, mark *PacketElement) *PacketElement {
|
||||||
|
if mark.list != l {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// see comment in PacketList.Remove about initialization of l
|
||||||
|
return l.insertValue(v, mark.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
|
||||||
|
// If mark is not an element of l, the list is not modified.
|
||||||
|
func (l *PacketList) InsertAfter(v Packet, mark *PacketElement) *PacketElement {
|
||||||
|
if mark.list != l {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// see comment in PacketList.Remove about initialization of l
|
||||||
|
return l.insertValue(v, mark)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveToFront moves element e to the front of list l.
|
||||||
|
// If e is not an element of l, the list is not modified.
|
||||||
|
func (l *PacketList) MoveToFront(e *PacketElement) {
|
||||||
|
if e.list != l || l.root.next == e {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// see comment in PacketList.Remove about initialization of l
|
||||||
|
l.insert(l.remove(e), &l.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveToBack moves element e to the back of list l.
|
||||||
|
// If e is not an element of l, the list is not modified.
|
||||||
|
func (l *PacketList) MoveToBack(e *PacketElement) {
|
||||||
|
if e.list != l || l.root.prev == e {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// see comment in PacketList.Remove about initialization of l
|
||||||
|
l.insert(l.remove(e), l.root.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveBefore moves element e to its new position before mark.
|
||||||
|
// If e or mark is not an element of l, or e == mark, the list is not modified.
|
||||||
|
func (l *PacketList) MoveBefore(e, mark *PacketElement) {
|
||||||
|
if e.list != l || e == mark || mark.list != l {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.insert(l.remove(e), mark.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveAfter moves element e to its new position after mark.
|
||||||
|
// If e is not an element of l, or e == mark, the list is not modified.
|
||||||
|
func (l *PacketList) MoveAfter(e, mark *PacketElement) {
|
||||||
|
if e.list != l || e == mark || mark.list != l {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.insert(l.remove(e), mark)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBackList inserts a copy of an other list at the back of list l.
|
||||||
|
// The lists l and other may be the same.
|
||||||
|
func (l *PacketList) PushBackList(other *PacketList) {
|
||||||
|
l.lazyInit()
|
||||||
|
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
|
||||||
|
l.insertValue(e.Value, l.root.prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFrontList inserts a copy of an other list at the front of list l.
|
||||||
|
// The lists l and other may be the same.
|
||||||
|
func (l *PacketList) PushFrontList(other *PacketList) {
|
||||||
|
l.lazyInit()
|
||||||
|
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
|
||||||
|
l.insertValue(e.Value, &l.root)
|
||||||
|
}
|
||||||
|
}
|
139
vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_handler.go
generated
vendored
Normal file
139
vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_handler.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidPacketNumber = errors.New("ReceivedPacketHandler: Invalid packet number")
|
||||||
|
|
||||||
|
type receivedPacketHandler struct {
|
||||||
|
largestObserved protocol.PacketNumber
|
||||||
|
ignorePacketsBelow protocol.PacketNumber
|
||||||
|
largestObservedReceivedTime time.Time
|
||||||
|
|
||||||
|
packetHistory *receivedPacketHistory
|
||||||
|
|
||||||
|
ackSendDelay time.Duration
|
||||||
|
|
||||||
|
packetsReceivedSinceLastAck int
|
||||||
|
retransmittablePacketsReceivedSinceLastAck int
|
||||||
|
ackQueued bool
|
||||||
|
ackAlarm time.Time
|
||||||
|
lastAck *frames.AckFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReceivedPacketHandler creates a new receivedPacketHandler
|
||||||
|
func NewReceivedPacketHandler() ReceivedPacketHandler {
|
||||||
|
return &receivedPacketHandler{
|
||||||
|
packetHistory: newReceivedPacketHistory(),
|
||||||
|
ackSendDelay: protocol.AckSendDelay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHandler) ReceivedPacket(packetNumber protocol.PacketNumber, shouldInstigateAck bool) error {
|
||||||
|
if packetNumber == 0 {
|
||||||
|
return errInvalidPacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetNumber > h.ignorePacketsBelow {
|
||||||
|
if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetNumber > h.largestObserved {
|
||||||
|
h.largestObserved = packetNumber
|
||||||
|
h.largestObservedReceivedTime = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
h.maybeQueueAck(packetNumber, shouldInstigateAck)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHandler) ReceivedStopWaiting(f *frames.StopWaitingFrame) error {
|
||||||
|
// ignore if StopWaiting is unneeded, because we already received a StopWaiting with a higher LeastUnacked
|
||||||
|
if h.ignorePacketsBelow >= f.LeastUnacked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h.ignorePacketsBelow = f.LeastUnacked - 1
|
||||||
|
|
||||||
|
h.packetHistory.DeleteBelow(f.LeastUnacked)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHandler) maybeQueueAck(packetNumber protocol.PacketNumber, shouldInstigateAck bool) {
|
||||||
|
h.packetsReceivedSinceLastAck++
|
||||||
|
|
||||||
|
if shouldInstigateAck {
|
||||||
|
h.retransmittablePacketsReceivedSinceLastAck++
|
||||||
|
}
|
||||||
|
|
||||||
|
// always ack the first packet
|
||||||
|
if h.lastAck == nil {
|
||||||
|
h.ackQueued = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always send an ack every 20 packets in order to allow the peer to discard
|
||||||
|
// information from the SentPacketManager and provide an RTT measurement.
|
||||||
|
if h.packetsReceivedSinceLastAck >= protocol.MaxPacketsReceivedBeforeAckSend {
|
||||||
|
h.ackQueued = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the packet number is smaller than the largest acked packet, it must have been reported missing with the last ACK
|
||||||
|
// note that it cannot be a duplicate because they're already filtered out by ReceivedPacket()
|
||||||
|
if h.lastAck != nil && packetNumber < h.lastAck.LargestAcked {
|
||||||
|
h.ackQueued = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a new missing range above the previously was created
|
||||||
|
if h.lastAck != nil && h.packetHistory.GetHighestAckRange().FirstPacketNumber > h.lastAck.LargestAcked {
|
||||||
|
h.ackQueued = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.ackQueued && shouldInstigateAck {
|
||||||
|
if h.retransmittablePacketsReceivedSinceLastAck >= protocol.RetransmittablePacketsBeforeAck {
|
||||||
|
h.ackQueued = true
|
||||||
|
} else {
|
||||||
|
if h.ackAlarm.IsZero() {
|
||||||
|
h.ackAlarm = time.Now().Add(h.ackSendDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.ackQueued {
|
||||||
|
// cancel the ack alarm
|
||||||
|
h.ackAlarm = time.Time{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHandler) GetAckFrame() *frames.AckFrame {
|
||||||
|
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(time.Now())) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ackRanges := h.packetHistory.GetAckRanges()
|
||||||
|
ack := &frames.AckFrame{
|
||||||
|
LargestAcked: h.largestObserved,
|
||||||
|
LowestAcked: ackRanges[len(ackRanges)-1].FirstPacketNumber,
|
||||||
|
PacketReceivedTime: h.largestObservedReceivedTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ackRanges) > 1 {
|
||||||
|
ack.AckRanges = ackRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
h.lastAck = ack
|
||||||
|
h.ackAlarm = time.Time{}
|
||||||
|
h.ackQueued = false
|
||||||
|
h.packetsReceivedSinceLastAck = 0
|
||||||
|
h.retransmittablePacketsReceivedSinceLastAck = 0
|
||||||
|
|
||||||
|
return ack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { return h.ackAlarm }
|
145
vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_history.go
generated
vendored
Normal file
145
vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_history.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type receivedPacketHistory struct {
|
||||||
|
ranges *utils.PacketIntervalList
|
||||||
|
|
||||||
|
// the map is used as a replacement for a set here. The bool is always supposed to be set to true
|
||||||
|
receivedPacketNumbers map[protocol.PacketNumber]bool
|
||||||
|
lowestInReceivedPacketNumbers protocol.PacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errTooManyOutstandingReceivedAckRanges = qerr.Error(qerr.TooManyOutstandingReceivedPackets, "Too many outstanding received ACK ranges")
|
||||||
|
errTooManyOutstandingReceivedPackets = qerr.Error(qerr.TooManyOutstandingReceivedPackets, "Too many outstanding received packets")
|
||||||
|
)
|
||||||
|
|
||||||
|
// newReceivedPacketHistory creates a new received packet history
|
||||||
|
func newReceivedPacketHistory() *receivedPacketHistory {
|
||||||
|
return &receivedPacketHistory{
|
||||||
|
ranges: utils.NewPacketIntervalList(),
|
||||||
|
receivedPacketNumbers: make(map[protocol.PacketNumber]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
|
||||||
|
func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) error {
|
||||||
|
if h.ranges.Len() >= protocol.MaxTrackedReceivedAckRanges {
|
||||||
|
return errTooManyOutstandingReceivedAckRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(h.receivedPacketNumbers) >= protocol.MaxTrackedReceivedPackets {
|
||||||
|
return errTooManyOutstandingReceivedPackets
|
||||||
|
}
|
||||||
|
|
||||||
|
h.receivedPacketNumbers[p] = true
|
||||||
|
|
||||||
|
if h.ranges.Len() == 0 {
|
||||||
|
h.ranges.PushBack(utils.PacketInterval{Start: p, End: p})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||||
|
// p already included in an existing range. Nothing to do here
|
||||||
|
if p >= el.Value.Start && p <= el.Value.End {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rangeExtended bool
|
||||||
|
if el.Value.End == p-1 { // extend a range at the end
|
||||||
|
rangeExtended = true
|
||||||
|
el.Value.End = p
|
||||||
|
} else if el.Value.Start == p+1 { // extend a range at the beginning
|
||||||
|
rangeExtended = true
|
||||||
|
el.Value.Start = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a range was extended (either at the beginning or at the end, maybe it is possible to merge two ranges into one)
|
||||||
|
if rangeExtended {
|
||||||
|
prev := el.Prev()
|
||||||
|
if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
|
||||||
|
prev.Value.End = el.Value.End
|
||||||
|
h.ranges.Remove(el)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil // if the two ranges were not merge, we're done here
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new range at the end
|
||||||
|
if p > el.Value.End {
|
||||||
|
h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new range at the beginning
|
||||||
|
h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBelow deletes all entries below the leastUnacked packet number
|
||||||
|
func (h *receivedPacketHistory) DeleteBelow(leastUnacked protocol.PacketNumber) {
|
||||||
|
h.lowestInReceivedPacketNumbers = utils.MaxPacketNumber(h.lowestInReceivedPacketNumbers, leastUnacked)
|
||||||
|
|
||||||
|
nextEl := h.ranges.Front()
|
||||||
|
for el := h.ranges.Front(); nextEl != nil; el = nextEl {
|
||||||
|
nextEl = el.Next()
|
||||||
|
|
||||||
|
if leastUnacked > el.Value.Start && leastUnacked <= el.Value.End {
|
||||||
|
for i := el.Value.Start; i < leastUnacked; i++ { // adjust start value of a range
|
||||||
|
delete(h.receivedPacketNumbers, i)
|
||||||
|
}
|
||||||
|
el.Value.Start = leastUnacked
|
||||||
|
} else if el.Value.End < leastUnacked { // delete a whole range
|
||||||
|
for i := el.Value.Start; i <= el.Value.End; i++ {
|
||||||
|
delete(h.receivedPacketNumbers, i)
|
||||||
|
}
|
||||||
|
h.ranges.Remove(el)
|
||||||
|
} else { // no ranges affected. Nothing to do
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDuplicate determines if a packet should be regarded as a duplicate packet
|
||||||
|
// note that after receiving a StopWaitingFrame, all packets below the LeastUnacked should be regarded as duplicates, even if the packet was just delayed
|
||||||
|
func (h *receivedPacketHistory) IsDuplicate(p protocol.PacketNumber) bool {
|
||||||
|
if p < h.lowestInReceivedPacketNumbers {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := h.receivedPacketNumbers[p]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame
|
||||||
|
func (h *receivedPacketHistory) GetAckRanges() []frames.AckRange {
|
||||||
|
if h.ranges.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ackRanges []frames.AckRange
|
||||||
|
|
||||||
|
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||||
|
ackRanges = append(ackRanges, frames.AckRange{FirstPacketNumber: el.Value.Start, LastPacketNumber: el.Value.End})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ackRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *receivedPacketHistory) GetHighestAckRange() frames.AckRange {
|
||||||
|
ackRange := frames.AckRange{}
|
||||||
|
if h.ranges.Len() > 0 {
|
||||||
|
r := h.ranges.Back().Value
|
||||||
|
ackRange.FirstPacketNumber = r.Start
|
||||||
|
ackRange.LastPacketNumber = r.End
|
||||||
|
}
|
||||||
|
return ackRange
|
||||||
|
}
|
38
vendor/github.com/lucas-clemente/quic-go/ackhandler/retransmittable.go
generated
vendored
Normal file
38
vendor/github.com/lucas-clemente/quic-go/ackhandler/retransmittable.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns a new slice with all non-retransmittable frames deleted.
|
||||||
|
func stripNonRetransmittableFrames(fs []frames.Frame) []frames.Frame {
|
||||||
|
res := make([]frames.Frame, 0, len(fs))
|
||||||
|
for _, f := range fs {
|
||||||
|
if IsFrameRetransmittable(f) {
|
||||||
|
res = append(res, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFrameRetransmittable returns true if the frame should be retransmitted.
|
||||||
|
func IsFrameRetransmittable(f frames.Frame) bool {
|
||||||
|
switch f.(type) {
|
||||||
|
case *frames.StopWaitingFrame:
|
||||||
|
return false
|
||||||
|
case *frames.AckFrame:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasRetransmittableFrames returns true if at least one frame is retransmittable.
|
||||||
|
func HasRetransmittableFrames(fs []frames.Frame) bool {
|
||||||
|
for _, f := range fs {
|
||||||
|
if IsFrameRetransmittable(f) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
403
vendor/github.com/lucas-clemente/quic-go/ackhandler/sent_packet_handler.go
generated
vendored
Normal file
403
vendor/github.com/lucas-clemente/quic-go/ackhandler/sent_packet_handler.go
generated
vendored
Normal file
|
@ -0,0 +1,403 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/congestion"
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Maximum reordering in time space before time based loss detection considers a packet lost.
|
||||||
|
// In fraction of an RTT.
|
||||||
|
timeReorderingFraction = 1.0 / 8
|
||||||
|
// defaultRTOTimeout is the RTO time on new connections
|
||||||
|
defaultRTOTimeout = 500 * time.Millisecond
|
||||||
|
// Minimum time in the future an RTO alarm may be set for.
|
||||||
|
minRTOTimeout = 200 * time.Millisecond
|
||||||
|
// maxRTOTimeout is the maximum RTO time
|
||||||
|
maxRTOTimeout = 60 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrDuplicateOrOutOfOrderAck occurs when a duplicate or an out-of-order ACK is received
|
||||||
|
ErrDuplicateOrOutOfOrderAck = errors.New("SentPacketHandler: Duplicate or out-of-order ACK")
|
||||||
|
// ErrTooManyTrackedSentPackets occurs when the sentPacketHandler has to keep track of too many packets
|
||||||
|
ErrTooManyTrackedSentPackets = errors.New("Too many outstanding non-acked and non-retransmitted packets")
|
||||||
|
// ErrAckForSkippedPacket occurs when the client sent an ACK for a packet number that we intentionally skipped
|
||||||
|
ErrAckForSkippedPacket = qerr.Error(qerr.InvalidAckData, "Received an ACK for a skipped packet number")
|
||||||
|
errAckForUnsentPacket = qerr.Error(qerr.InvalidAckData, "Received ACK for an unsent package")
|
||||||
|
)
|
||||||
|
|
||||||
|
var errPacketNumberNotIncreasing = errors.New("Already sent a packet with a higher packet number")
|
||||||
|
|
||||||
|
type sentPacketHandler struct {
|
||||||
|
lastSentPacketNumber protocol.PacketNumber
|
||||||
|
skippedPackets []protocol.PacketNumber
|
||||||
|
|
||||||
|
LargestAcked protocol.PacketNumber
|
||||||
|
|
||||||
|
largestReceivedPacketWithAck protocol.PacketNumber
|
||||||
|
|
||||||
|
packetHistory *PacketList
|
||||||
|
stopWaitingManager stopWaitingManager
|
||||||
|
|
||||||
|
retransmissionQueue []*Packet
|
||||||
|
|
||||||
|
bytesInFlight protocol.ByteCount
|
||||||
|
|
||||||
|
congestion congestion.SendAlgorithm
|
||||||
|
rttStats *congestion.RTTStats
|
||||||
|
|
||||||
|
// The number of times an RTO has been sent without receiving an ack.
|
||||||
|
rtoCount uint32
|
||||||
|
|
||||||
|
// The time at which the next packet will be considered lost based on early transmit or exceeding the reordering window in time.
|
||||||
|
lossTime time.Time
|
||||||
|
|
||||||
|
// The alarm timeout
|
||||||
|
alarm time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSentPacketHandler creates a new sentPacketHandler
|
||||||
|
func NewSentPacketHandler(rttStats *congestion.RTTStats) SentPacketHandler {
|
||||||
|
congestion := congestion.NewCubicSender(
|
||||||
|
congestion.DefaultClock{},
|
||||||
|
rttStats,
|
||||||
|
false, /* don't use reno since chromium doesn't (why?) */
|
||||||
|
protocol.InitialCongestionWindow,
|
||||||
|
protocol.DefaultMaxCongestionWindow,
|
||||||
|
)
|
||||||
|
|
||||||
|
return &sentPacketHandler{
|
||||||
|
packetHistory: NewPacketList(),
|
||||||
|
stopWaitingManager: stopWaitingManager{},
|
||||||
|
rttStats: rttStats,
|
||||||
|
congestion: congestion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) largestInOrderAcked() protocol.PacketNumber {
|
||||||
|
if f := h.packetHistory.Front(); f != nil {
|
||||||
|
return f.Value.PacketNumber - 1
|
||||||
|
}
|
||||||
|
return h.LargestAcked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) SentPacket(packet *Packet) error {
|
||||||
|
if packet.PacketNumber <= h.lastSentPacketNumber {
|
||||||
|
return errPacketNumberNotIncreasing
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol.PacketNumber(len(h.retransmissionQueue)+h.packetHistory.Len()+1) > protocol.MaxTrackedSentPackets {
|
||||||
|
return ErrTooManyTrackedSentPackets
|
||||||
|
}
|
||||||
|
|
||||||
|
for p := h.lastSentPacketNumber + 1; p < packet.PacketNumber; p++ {
|
||||||
|
h.skippedPackets = append(h.skippedPackets, p)
|
||||||
|
|
||||||
|
if len(h.skippedPackets) > protocol.MaxTrackedSkippedPackets {
|
||||||
|
h.skippedPackets = h.skippedPackets[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.lastSentPacketNumber = packet.PacketNumber
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
packet.Frames = stripNonRetransmittableFrames(packet.Frames)
|
||||||
|
isRetransmittable := len(packet.Frames) != 0
|
||||||
|
|
||||||
|
if isRetransmittable {
|
||||||
|
packet.SendTime = now
|
||||||
|
h.bytesInFlight += packet.Length
|
||||||
|
h.packetHistory.PushBack(*packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.congestion.OnPacketSent(
|
||||||
|
now,
|
||||||
|
h.bytesInFlight,
|
||||||
|
packet.PacketNumber,
|
||||||
|
packet.Length,
|
||||||
|
isRetransmittable,
|
||||||
|
)
|
||||||
|
|
||||||
|
h.updateLossDetectionAlarm()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) ReceivedAck(ackFrame *frames.AckFrame, withPacketNumber protocol.PacketNumber, rcvTime time.Time) error {
|
||||||
|
if ackFrame.LargestAcked > h.lastSentPacketNumber {
|
||||||
|
return errAckForUnsentPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
// duplicate or out-of-order ACK
|
||||||
|
if withPacketNumber <= h.largestReceivedPacketWithAck {
|
||||||
|
return ErrDuplicateOrOutOfOrderAck
|
||||||
|
}
|
||||||
|
h.largestReceivedPacketWithAck = withPacketNumber
|
||||||
|
|
||||||
|
// ignore repeated ACK (ACKs that don't have a higher LargestAcked than the last ACK)
|
||||||
|
if ackFrame.LargestAcked <= h.largestInOrderAcked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
h.LargestAcked = ackFrame.LargestAcked
|
||||||
|
|
||||||
|
if h.skippedPacketsAcked(ackFrame) {
|
||||||
|
return ErrAckForSkippedPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
rttUpdated := h.maybeUpdateRTT(ackFrame.LargestAcked, ackFrame.DelayTime, rcvTime)
|
||||||
|
|
||||||
|
if rttUpdated {
|
||||||
|
h.congestion.MaybeExitSlowStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
ackedPackets, err := h.determineNewlyAckedPackets(ackFrame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ackedPackets) > 0 {
|
||||||
|
for _, p := range ackedPackets {
|
||||||
|
h.onPacketAcked(p)
|
||||||
|
h.congestion.OnPacketAcked(p.Value.PacketNumber, p.Value.Length, h.bytesInFlight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.detectLostPackets()
|
||||||
|
h.updateLossDetectionAlarm()
|
||||||
|
|
||||||
|
h.garbageCollectSkippedPackets()
|
||||||
|
h.stopWaitingManager.ReceivedAck(ackFrame)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) determineNewlyAckedPackets(ackFrame *frames.AckFrame) ([]*PacketElement, error) {
|
||||||
|
var ackedPackets []*PacketElement
|
||||||
|
ackRangeIndex := 0
|
||||||
|
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||||
|
packet := el.Value
|
||||||
|
packetNumber := packet.PacketNumber
|
||||||
|
|
||||||
|
// Ignore packets below the LowestAcked
|
||||||
|
if packetNumber < ackFrame.LowestAcked {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Break after LargestAcked is reached
|
||||||
|
if packetNumber > ackFrame.LargestAcked {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ackFrame.HasMissingRanges() {
|
||||||
|
ackRange := ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||||
|
|
||||||
|
for packetNumber > ackRange.LastPacketNumber && ackRangeIndex < len(ackFrame.AckRanges)-1 {
|
||||||
|
ackRangeIndex++
|
||||||
|
ackRange = ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetNumber >= ackRange.FirstPacketNumber { // packet i contained in ACK range
|
||||||
|
if packetNumber > ackRange.LastPacketNumber {
|
||||||
|
return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet 0x%x, while evaluating range 0x%x -> 0x%x", packetNumber, ackRange.FirstPacketNumber, ackRange.LastPacketNumber)
|
||||||
|
}
|
||||||
|
ackedPackets = append(ackedPackets, el)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ackedPackets = append(ackedPackets, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ackedPackets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) maybeUpdateRTT(largestAcked protocol.PacketNumber, ackDelay time.Duration, rcvTime time.Time) bool {
|
||||||
|
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||||
|
packet := el.Value
|
||||||
|
if packet.PacketNumber == largestAcked {
|
||||||
|
h.rttStats.UpdateRTT(rcvTime.Sub(packet.SendTime), ackDelay, time.Now())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Packets are sorted by number, so we can stop searching
|
||||||
|
if packet.PacketNumber > largestAcked {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) updateLossDetectionAlarm() {
|
||||||
|
// Cancel the alarm if no packets are outstanding
|
||||||
|
if h.packetHistory.Len() == 0 {
|
||||||
|
h.alarm = time.Time{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#496): Handle handshake packets separately
|
||||||
|
// TODO(#497): TLP
|
||||||
|
if !h.lossTime.IsZero() {
|
||||||
|
// Early retransmit timer or time loss detection.
|
||||||
|
h.alarm = h.lossTime
|
||||||
|
} else {
|
||||||
|
// RTO
|
||||||
|
h.alarm = time.Now().Add(h.computeRTOTimeout())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) detectLostPackets() {
|
||||||
|
h.lossTime = time.Time{}
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
maxRTT := float64(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT()))
|
||||||
|
delayUntilLost := time.Duration((1.0 + timeReorderingFraction) * maxRTT)
|
||||||
|
|
||||||
|
var lostPackets []*PacketElement
|
||||||
|
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||||
|
packet := el.Value
|
||||||
|
|
||||||
|
if packet.PacketNumber > h.LargestAcked {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
timeSinceSent := now.Sub(packet.SendTime)
|
||||||
|
if timeSinceSent > delayUntilLost {
|
||||||
|
lostPackets = append(lostPackets, el)
|
||||||
|
} else if h.lossTime.IsZero() {
|
||||||
|
// Note: This conditional is only entered once per call
|
||||||
|
h.lossTime = now.Add(delayUntilLost - timeSinceSent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lostPackets) > 0 {
|
||||||
|
for _, p := range lostPackets {
|
||||||
|
h.queuePacketForRetransmission(p)
|
||||||
|
h.congestion.OnPacketLost(p.Value.PacketNumber, p.Value.Length, h.bytesInFlight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) OnAlarm() {
|
||||||
|
// TODO(#496): Handle handshake packets separately
|
||||||
|
// TODO(#497): TLP
|
||||||
|
if !h.lossTime.IsZero() {
|
||||||
|
// Early retransmit or time loss detection
|
||||||
|
h.detectLostPackets()
|
||||||
|
} else {
|
||||||
|
// RTO
|
||||||
|
h.retransmitOldestTwoPackets()
|
||||||
|
h.rtoCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
h.updateLossDetectionAlarm()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) GetAlarmTimeout() time.Time {
|
||||||
|
return h.alarm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) onPacketAcked(packetElement *PacketElement) {
|
||||||
|
h.bytesInFlight -= packetElement.Value.Length
|
||||||
|
h.rtoCount = 0
|
||||||
|
// TODO(#497): h.tlpCount = 0
|
||||||
|
h.packetHistory.Remove(packetElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet {
|
||||||
|
if len(h.retransmissionQueue) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
packet := h.retransmissionQueue[0]
|
||||||
|
// Shift the slice and don't retain anything that isn't needed.
|
||||||
|
copy(h.retransmissionQueue, h.retransmissionQueue[1:])
|
||||||
|
h.retransmissionQueue[len(h.retransmissionQueue)-1] = nil
|
||||||
|
h.retransmissionQueue = h.retransmissionQueue[:len(h.retransmissionQueue)-1]
|
||||||
|
return packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) GetLeastUnacked() protocol.PacketNumber {
|
||||||
|
return h.largestInOrderAcked() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) GetStopWaitingFrame(force bool) *frames.StopWaitingFrame {
|
||||||
|
return h.stopWaitingManager.GetStopWaitingFrame(force)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) SendingAllowed() bool {
|
||||||
|
congestionLimited := h.bytesInFlight > h.congestion.GetCongestionWindow()
|
||||||
|
maxTrackedLimited := protocol.PacketNumber(len(h.retransmissionQueue)+h.packetHistory.Len()) >= protocol.MaxTrackedSentPackets
|
||||||
|
if congestionLimited {
|
||||||
|
utils.Debugf("Congestion limited: bytes in flight %d, window %d",
|
||||||
|
h.bytesInFlight,
|
||||||
|
h.congestion.GetCongestionWindow())
|
||||||
|
}
|
||||||
|
// Workaround for #555:
|
||||||
|
// Always allow sending of retransmissions. This should probably be limited
|
||||||
|
// to RTOs, but we currently don't have a nice way of distinguishing them.
|
||||||
|
haveRetransmissions := len(h.retransmissionQueue) > 0
|
||||||
|
return !maxTrackedLimited && (!congestionLimited || haveRetransmissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) retransmitOldestTwoPackets() {
|
||||||
|
if p := h.packetHistory.Front(); p != nil {
|
||||||
|
h.queueRTO(p)
|
||||||
|
}
|
||||||
|
if p := h.packetHistory.Front(); p != nil {
|
||||||
|
h.queueRTO(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) queueRTO(el *PacketElement) {
|
||||||
|
packet := &el.Value
|
||||||
|
utils.Debugf(
|
||||||
|
"\tQueueing packet 0x%x for retransmission (RTO), %d outstanding",
|
||||||
|
packet.PacketNumber,
|
||||||
|
h.packetHistory.Len(),
|
||||||
|
)
|
||||||
|
h.queuePacketForRetransmission(el)
|
||||||
|
h.congestion.OnPacketLost(packet.PacketNumber, packet.Length, h.bytesInFlight)
|
||||||
|
h.congestion.OnRetransmissionTimeout(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) queuePacketForRetransmission(packetElement *PacketElement) {
|
||||||
|
packet := &packetElement.Value
|
||||||
|
h.bytesInFlight -= packet.Length
|
||||||
|
h.retransmissionQueue = append(h.retransmissionQueue, packet)
|
||||||
|
h.packetHistory.Remove(packetElement)
|
||||||
|
h.stopWaitingManager.QueuedRetransmissionForPacketNumber(packet.PacketNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) computeRTOTimeout() time.Duration {
|
||||||
|
rto := h.congestion.RetransmissionDelay()
|
||||||
|
if rto == 0 {
|
||||||
|
rto = defaultRTOTimeout
|
||||||
|
}
|
||||||
|
rto = utils.MaxDuration(rto, minRTOTimeout)
|
||||||
|
// Exponential backoff
|
||||||
|
rto = rto << h.rtoCount
|
||||||
|
return utils.MinDuration(rto, maxRTOTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) skippedPacketsAcked(ackFrame *frames.AckFrame) bool {
|
||||||
|
for _, p := range h.skippedPackets {
|
||||||
|
if ackFrame.AcksPacket(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHandler) garbageCollectSkippedPackets() {
|
||||||
|
lioa := h.largestInOrderAcked()
|
||||||
|
deleteIndex := 0
|
||||||
|
for i, p := range h.skippedPackets {
|
||||||
|
if p <= lioa {
|
||||||
|
deleteIndex = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.skippedPackets = h.skippedPackets[deleteIndex:]
|
||||||
|
}
|
42
vendor/github.com/lucas-clemente/quic-go/ackhandler/stop_waiting_manager.go
generated
vendored
Normal file
42
vendor/github.com/lucas-clemente/quic-go/ackhandler/stop_waiting_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package ackhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lucas-clemente/quic-go/frames"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This stopWaitingManager is not supposed to satisfy the StopWaitingManager interface, which is a remnant of the legacy AckHandler, and should be remove once we drop support for QUIC 33
|
||||||
|
type stopWaitingManager struct {
|
||||||
|
largestLeastUnackedSent protocol.PacketNumber
|
||||||
|
nextLeastUnacked protocol.PacketNumber
|
||||||
|
|
||||||
|
lastStopWaitingFrame *frames.StopWaitingFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stopWaitingManager) GetStopWaitingFrame(force bool) *frames.StopWaitingFrame {
|
||||||
|
if s.nextLeastUnacked <= s.largestLeastUnackedSent {
|
||||||
|
if force {
|
||||||
|
return s.lastStopWaitingFrame
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.largestLeastUnackedSent = s.nextLeastUnacked
|
||||||
|
swf := &frames.StopWaitingFrame{
|
||||||
|
LeastUnacked: s.nextLeastUnacked,
|
||||||
|
}
|
||||||
|
s.lastStopWaitingFrame = swf
|
||||||
|
return swf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stopWaitingManager) ReceivedAck(ack *frames.AckFrame) {
|
||||||
|
if ack.LargestAcked >= s.nextLeastUnacked {
|
||||||
|
s.nextLeastUnacked = ack.LargestAcked + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stopWaitingManager) QueuedRetransmissionForPacketNumber(p protocol.PacketNumber) {
|
||||||
|
if p >= s.nextLeastUnacked {
|
||||||
|
s.nextLeastUnacked = p + 1
|
||||||
|
}
|
||||||
|
}
|
35
vendor/github.com/lucas-clemente/quic-go/appveyor.yml
generated
vendored
Normal file
35
vendor/github.com/lucas-clemente/quic-go/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
version: "{build}"
|
||||||
|
|
||||||
|
os: Windows Server 2012 R2
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GOPATH: c:\gopath
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
TIMESCALE_FACTOR: 20
|
||||||
|
matrix:
|
||||||
|
- GOARCH: 386
|
||||||
|
- GOARCH: amd64
|
||||||
|
|
||||||
|
clone_folder: c:\gopath\src\github.com\lucas-clemente\quic-go
|
||||||
|
|
||||||
|
install:
|
||||||
|
- rmdir c:\go /s /q
|
||||||
|
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.3.windows-amd64.zip
|
||||||
|
- 7z x go1.8.3.windows-amd64.zip -y -oC:\ > NUL
|
||||||
|
- set PATH=%PATH%;%GOPATH%\bin\windows_%GOARCH%;%GOPATH%\bin
|
||||||
|
- echo %PATH%
|
||||||
|
- echo %GOPATH%
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
- go get github.com/onsi/ginkgo/ginkgo
|
||||||
|
- go get github.com/onsi/gomega
|
||||||
|
- go version
|
||||||
|
- go env
|
||||||
|
- go get -v -t ./...
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- rm -r integrationtests
|
||||||
|
- ginkgo -r --randomizeAllSpecs --randomizeSuites --trace --progress
|
||||||
|
|
||||||
|
test: off
|
||||||
|
|
||||||
|
deploy: off
|
26
vendor/github.com/lucas-clemente/quic-go/buffer_pool.go
generated
vendored
Normal file
26
vendor/github.com/lucas-clemente/quic-go/buffer_pool.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufferPool sync.Pool
|
||||||
|
|
||||||
|
func getPacketBuffer() []byte {
|
||||||
|
return bufferPool.Get().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func putPacketBuffer(buf []byte) {
|
||||||
|
if cap(buf) != int(protocol.MaxReceivePacketSize) {
|
||||||
|
panic("putPacketBuffer called with packet of wrong size!")
|
||||||
|
}
|
||||||
|
bufferPool.Put(buf[:0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bufferPool.New = func() interface{} {
|
||||||
|
return make([]byte, 0, protocol.MaxReceivePacketSize)
|
||||||
|
}
|
||||||
|
}
|
335
vendor/github.com/lucas-clemente/quic-go/client.go
generated
vendored
Normal file
335
vendor/github.com/lucas-clemente/quic-go/client.go
generated
vendored
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
listenErr error
|
||||||
|
|
||||||
|
conn connection
|
||||||
|
hostname string
|
||||||
|
|
||||||
|
errorChan chan struct{}
|
||||||
|
handshakeChan <-chan handshakeEvent
|
||||||
|
|
||||||
|
tlsConf *tls.Config
|
||||||
|
config *Config
|
||||||
|
versionNegotiated bool // has version negotiation completed yet
|
||||||
|
|
||||||
|
connectionID protocol.ConnectionID
|
||||||
|
version protocol.VersionNumber
|
||||||
|
|
||||||
|
session packetHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errCloseSessionForNewVersion = errors.New("closing session in order to recreate it with a new version")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialAddr establishes a new QUIC connection to a server.
|
||||||
|
// The hostname for SNI is taken from the given address.
|
||||||
|
func DialAddr(addr string, tlsConf *tls.Config, config *Config) (Session, error) {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Dial(udpConn, udpAddr, addr, tlsConf, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialAddrNonFWSecure establishes a new QUIC connection to a server.
|
||||||
|
// The hostname for SNI is taken from the given address.
|
||||||
|
func DialAddrNonFWSecure(
|
||||||
|
addr string,
|
||||||
|
tlsConf *tls.Config,
|
||||||
|
config *Config,
|
||||||
|
) (NonFWSession, error) {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return DialNonFWSecure(udpConn, udpAddr, addr, tlsConf, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialNonFWSecure establishes a new non-forward-secure QUIC connection to a server using a net.PacketConn.
|
||||||
|
// The host parameter is used for SNI.
|
||||||
|
func DialNonFWSecure(
|
||||||
|
pconn net.PacketConn,
|
||||||
|
remoteAddr net.Addr,
|
||||||
|
host string,
|
||||||
|
tlsConf *tls.Config,
|
||||||
|
config *Config,
|
||||||
|
) (NonFWSession, error) {
|
||||||
|
connID, err := utils.GenerateConnectionID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
if tlsConf != nil {
|
||||||
|
hostname = tlsConf.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostname == "" {
|
||||||
|
hostname, _, err = net.SplitHostPort(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig := populateClientConfig(config)
|
||||||
|
c := &client{
|
||||||
|
conn: &conn{pconn: pconn, currentAddr: remoteAddr},
|
||||||
|
connectionID: connID,
|
||||||
|
hostname: hostname,
|
||||||
|
tlsConf: tlsConf,
|
||||||
|
config: clientConfig,
|
||||||
|
version: clientConfig.Versions[0],
|
||||||
|
errorChan: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.createNewSession(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Infof("Starting new connection to %s (%s -> %s), connectionID %x, version %d", hostname, c.conn.LocalAddr().String(), c.conn.RemoteAddr().String(), c.connectionID, c.version)
|
||||||
|
|
||||||
|
return c.session.(NonFWSession), c.establishSecureConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial establishes a new QUIC connection to a server using a net.PacketConn.
|
||||||
|
// The host parameter is used for SNI.
|
||||||
|
func Dial(
|
||||||
|
pconn net.PacketConn,
|
||||||
|
remoteAddr net.Addr,
|
||||||
|
host string,
|
||||||
|
tlsConf *tls.Config,
|
||||||
|
config *Config,
|
||||||
|
) (Session, error) {
|
||||||
|
sess, err := DialNonFWSecure(pconn, remoteAddr, host, tlsConf, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = sess.WaitUntilHandshakeComplete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sess, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// populateClientConfig populates fields in the quic.Config with their default values, if none are set
|
||||||
|
// it may be called with nil
|
||||||
|
func populateClientConfig(config *Config) *Config {
|
||||||
|
if config == nil {
|
||||||
|
config = &Config{}
|
||||||
|
}
|
||||||
|
versions := config.Versions
|
||||||
|
if len(versions) == 0 {
|
||||||
|
versions = protocol.SupportedVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
handshakeTimeout := protocol.DefaultHandshakeTimeout
|
||||||
|
if config.HandshakeTimeout != 0 {
|
||||||
|
handshakeTimeout = config.HandshakeTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
|
||||||
|
if maxReceiveStreamFlowControlWindow == 0 {
|
||||||
|
maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowClient
|
||||||
|
}
|
||||||
|
maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow
|
||||||
|
if maxReceiveConnectionFlowControlWindow == 0 {
|
||||||
|
maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowClient
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Config{
|
||||||
|
Versions: versions,
|
||||||
|
HandshakeTimeout: handshakeTimeout,
|
||||||
|
RequestConnectionIDTruncation: config.RequestConnectionIDTruncation,
|
||||||
|
MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
|
||||||
|
MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
|
||||||
|
KeepAlive: config.KeepAlive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// establishSecureConnection returns as soon as the connection is secure (as opposed to forward-secure)
|
||||||
|
func (c *client) establishSecureConnection() error {
|
||||||
|
go c.listen()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-c.errorChan:
|
||||||
|
return c.listenErr
|
||||||
|
case ev := <-c.handshakeChan:
|
||||||
|
if ev.err != nil {
|
||||||
|
return ev.err
|
||||||
|
}
|
||||||
|
if ev.encLevel != protocol.EncryptionSecure {
|
||||||
|
return fmt.Errorf("Client BUG: Expected encryption level to be secure, was %s", ev.encLevel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen listens
|
||||||
|
func (c *client) listen() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for {
|
||||||
|
var n int
|
||||||
|
var addr net.Addr
|
||||||
|
data := getPacketBuffer()
|
||||||
|
data = data[:protocol.MaxReceivePacketSize]
|
||||||
|
// The packet size should not exceed protocol.MaxReceivePacketSize bytes
|
||||||
|
// If it does, we only read a truncated packet, which will then end up undecryptable
|
||||||
|
n, addr, err = c.conn.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
if !strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||||
|
c.session.Close(err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data = data[:n]
|
||||||
|
|
||||||
|
c.handlePacket(addr, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) {
|
||||||
|
rcvTime := time.Now()
|
||||||
|
|
||||||
|
r := bytes.NewReader(packet)
|
||||||
|
hdr, err := ParsePublicHeader(r, protocol.PerspectiveServer)
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("error parsing packet from %s: %s", remoteAddr.String(), err.Error())
|
||||||
|
// drop this packet if we can't parse the Public Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hdr.Raw = packet[:len(packet)-r.Len()]
|
||||||
|
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if hdr.ResetFlag {
|
||||||
|
cr := c.conn.RemoteAddr()
|
||||||
|
// check if the remote address and the connection ID match
|
||||||
|
// otherwise this might be an attacker trying to inject a PUBLIC_RESET to kill the connection
|
||||||
|
if cr.Network() != remoteAddr.Network() || cr.String() != remoteAddr.String() || hdr.ConnectionID != c.connectionID {
|
||||||
|
utils.Infof("Received a spoofed Public Reset. Ignoring.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pr, err := parsePublicReset(r)
|
||||||
|
if err != nil {
|
||||||
|
utils.Infof("Received a Public Reset for connection %x. An error occurred parsing the packet.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.Infof("Received Public Reset, rejected packet number: %#x.", pr.rejectedPacketNumber)
|
||||||
|
c.session.closeRemote(qerr.Error(qerr.PublicReset, fmt.Sprintf("Received a Public Reset for packet number %#x", pr.rejectedPacketNumber)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore delayed / duplicated version negotiation packets
|
||||||
|
if c.versionNegotiated && hdr.VersionFlag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first packet after the client sent a packet with the VersionFlag set
|
||||||
|
// if the server doesn't send a version negotiation packet, it supports the suggested version
|
||||||
|
if !hdr.VersionFlag && !c.versionNegotiated {
|
||||||
|
c.versionNegotiated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.VersionFlag {
|
||||||
|
// version negotiation packets have no payload
|
||||||
|
if err := c.handlePacketWithVersionFlag(hdr); err != nil {
|
||||||
|
c.session.Close(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.session.handlePacket(&receivedPacket{
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
publicHeader: hdr,
|
||||||
|
data: packet[len(packet)-r.Len():],
|
||||||
|
rcvTime: rcvTime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) handlePacketWithVersionFlag(hdr *PublicHeader) error {
|
||||||
|
for _, v := range hdr.SupportedVersions {
|
||||||
|
if v == c.version {
|
||||||
|
// the version negotiation packet contains the version that we offered
|
||||||
|
// this might be a packet sent by an attacker (or by a terribly broken server implementation)
|
||||||
|
// ignore it
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newVersion := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
|
||||||
|
if newVersion == protocol.VersionUnsupported {
|
||||||
|
return qerr.InvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch to negotiated version
|
||||||
|
c.version = newVersion
|
||||||
|
c.versionNegotiated = true
|
||||||
|
var err error
|
||||||
|
c.connectionID, err = utils.GenerateConnectionID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
utils.Infof("Switching to QUIC version %d. New connection ID: %x", newVersion, c.connectionID)
|
||||||
|
|
||||||
|
c.session.Close(errCloseSessionForNewVersion)
|
||||||
|
return c.createNewSession(hdr.SupportedVersions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) createNewSession(negotiatedVersions []protocol.VersionNumber) error {
|
||||||
|
var err error
|
||||||
|
c.session, c.handshakeChan, err = newClientSession(
|
||||||
|
c.conn,
|
||||||
|
c.hostname,
|
||||||
|
c.version,
|
||||||
|
c.connectionID,
|
||||||
|
c.tlsConf,
|
||||||
|
c.config,
|
||||||
|
negotiatedVersions,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// session.run() returns as soon as the session is closed
|
||||||
|
err := c.session.run()
|
||||||
|
if err == errCloseSessionForNewVersion {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.listenErr = err
|
||||||
|
close(c.errorChan)
|
||||||
|
|
||||||
|
utils.Infof("Connection %x closed.", c.connectionID)
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
13
vendor/github.com/lucas-clemente/quic-go/codecov.yml
generated
vendored
Normal file
13
vendor/github.com/lucas-clemente/quic-go/codecov.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
coverage:
|
||||||
|
round: nearest
|
||||||
|
ignore:
|
||||||
|
- ackhandler/packet_linkedlist.go
|
||||||
|
- h2quic/gzipreader.go
|
||||||
|
- h2quic/response.go
|
||||||
|
- internal/utils/byteinterval_linkedlist.go
|
||||||
|
- internal/utils/packetinterval_linkedlist.go
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 0.5
|
||||||
|
patch: false
|
22
vendor/github.com/lucas-clemente/quic-go/congestion/bandwidth.go
generated
vendored
Normal file
22
vendor/github.com/lucas-clemente/quic-go/congestion/bandwidth.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bandwidth of a connection
|
||||||
|
type Bandwidth uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BitsPerSecond is 1 bit per second
|
||||||
|
BitsPerSecond Bandwidth = 1
|
||||||
|
// BytesPerSecond is 1 byte per second
|
||||||
|
BytesPerSecond = 8 * BitsPerSecond
|
||||||
|
)
|
||||||
|
|
||||||
|
// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta
|
||||||
|
func BandwidthFromDelta(bytes protocol.ByteCount, delta time.Duration) Bandwidth {
|
||||||
|
return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond
|
||||||
|
}
|
18
vendor/github.com/lucas-clemente/quic-go/congestion/clock.go
generated
vendored
Normal file
18
vendor/github.com/lucas-clemente/quic-go/congestion/clock.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// A Clock returns the current time
|
||||||
|
type Clock interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClock implements the Clock interface using the Go stdlib clock.
|
||||||
|
type DefaultClock struct{}
|
||||||
|
|
||||||
|
var _ Clock = DefaultClock{}
|
||||||
|
|
||||||
|
// Now gets the current time
|
||||||
|
func (DefaultClock) Now() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
228
vendor/github.com/lucas-clemente/quic-go/congestion/cubic.go
generated
vendored
Normal file
228
vendor/github.com/lucas-clemente/quic-go/congestion/cubic.go
generated
vendored
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This cubic implementation is based on the one found in Chromiums's QUIC
|
||||||
|
// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}.
|
||||||
|
|
||||||
|
// Constants based on TCP defaults.
|
||||||
|
// The following constants are in 2^10 fractions of a second instead of ms to
|
||||||
|
// allow a 10 shift right to divide.
|
||||||
|
|
||||||
|
// 1024*1024^3 (first 1024 is from 0.100^3)
|
||||||
|
// where 0.100 is 100 ms which is the scaling
|
||||||
|
// round trip time.
|
||||||
|
const cubeScale = 40
|
||||||
|
const cubeCongestionWindowScale = 410
|
||||||
|
const cubeFactor protocol.PacketNumber = 1 << cubeScale / cubeCongestionWindowScale
|
||||||
|
|
||||||
|
const defaultNumConnections = 2
|
||||||
|
|
||||||
|
// Default Cubic backoff factor
|
||||||
|
const beta float32 = 0.7
|
||||||
|
|
||||||
|
// Additional backoff factor when loss occurs in the concave part of the Cubic
|
||||||
|
// curve. This additional backoff factor is expected to give up bandwidth to
|
||||||
|
// new concurrent flows and speed up convergence.
|
||||||
|
const betaLastMax float32 = 0.85
|
||||||
|
|
||||||
|
// If true, Cubic's epoch is shifted when the sender is application-limited.
|
||||||
|
const shiftQuicCubicEpochWhenAppLimited = true
|
||||||
|
|
||||||
|
const maxCubicTimeInterval = 30 * time.Millisecond
|
||||||
|
|
||||||
|
// Cubic implements the cubic algorithm from TCP
|
||||||
|
type Cubic struct {
|
||||||
|
clock Clock
|
||||||
|
// Number of connections to simulate.
|
||||||
|
numConnections int
|
||||||
|
// Time when this cycle started, after last loss event.
|
||||||
|
epoch time.Time
|
||||||
|
// Time when sender went into application-limited period. Zero if not in
|
||||||
|
// application-limited period.
|
||||||
|
appLimitedStartTime time.Time
|
||||||
|
// Time when we updated last_congestion_window.
|
||||||
|
lastUpdateTime time.Time
|
||||||
|
// Last congestion window (in packets) used.
|
||||||
|
lastCongestionWindow protocol.PacketNumber
|
||||||
|
// Max congestion window (in packets) used just before last loss event.
|
||||||
|
// Note: to improve fairness to other streams an additional back off is
|
||||||
|
// applied to this value if the new value is below our latest value.
|
||||||
|
lastMaxCongestionWindow protocol.PacketNumber
|
||||||
|
// Number of acked packets since the cycle started (epoch).
|
||||||
|
ackedPacketsCount protocol.PacketNumber
|
||||||
|
// TCP Reno equivalent congestion window in packets.
|
||||||
|
estimatedTCPcongestionWindow protocol.PacketNumber
|
||||||
|
// Origin point of cubic function.
|
||||||
|
originPointCongestionWindow protocol.PacketNumber
|
||||||
|
// Time to origin point of cubic function in 2^10 fractions of a second.
|
||||||
|
timeToOriginPoint uint32
|
||||||
|
// Last congestion window in packets computed by cubic function.
|
||||||
|
lastTargetCongestionWindow protocol.PacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCubic returns a new Cubic instance
|
||||||
|
func NewCubic(clock Clock) *Cubic {
|
||||||
|
c := &Cubic{
|
||||||
|
clock: clock,
|
||||||
|
numConnections: defaultNumConnections,
|
||||||
|
}
|
||||||
|
c.Reset()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset is called after a timeout to reset the cubic state
|
||||||
|
func (c *Cubic) Reset() {
|
||||||
|
c.epoch = time.Time{}
|
||||||
|
c.appLimitedStartTime = time.Time{}
|
||||||
|
c.lastUpdateTime = time.Time{}
|
||||||
|
c.lastCongestionWindow = 0
|
||||||
|
c.lastMaxCongestionWindow = 0
|
||||||
|
c.ackedPacketsCount = 0
|
||||||
|
c.estimatedTCPcongestionWindow = 0
|
||||||
|
c.originPointCongestionWindow = 0
|
||||||
|
c.timeToOriginPoint = 0
|
||||||
|
c.lastTargetCongestionWindow = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cubic) alpha() float32 {
|
||||||
|
// TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
|
||||||
|
// beta here is a cwnd multiplier, and is equal to 1-beta from the paper.
|
||||||
|
// We derive the equivalent alpha for an N-connection emulation as:
|
||||||
|
b := c.beta()
|
||||||
|
return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cubic) beta() float32 {
|
||||||
|
// kNConnectionBeta is the backoff factor after loss for our N-connection
|
||||||
|
// emulation, which emulates the effective backoff of an ensemble of N
|
||||||
|
// TCP-Reno connections on a single loss event. The effective multiplier is
|
||||||
|
// computed as:
|
||||||
|
return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnApplicationLimited is called on ack arrival when sender is unable to use
|
||||||
|
// the available congestion window. Resets Cubic state during quiescence.
|
||||||
|
func (c *Cubic) OnApplicationLimited() {
|
||||||
|
if shiftQuicCubicEpochWhenAppLimited {
|
||||||
|
// When sender is not using the available congestion window, Cubic's epoch
|
||||||
|
// should not continue growing. Record the time when sender goes into an
|
||||||
|
// app-limited period here, to compensate later when cwnd growth happens.
|
||||||
|
if c.appLimitedStartTime.IsZero() {
|
||||||
|
c.appLimitedStartTime = c.clock.Now()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When sender is not using the available congestion window, Cubic's epoch
|
||||||
|
// should not continue growing. Reset the epoch when in such a period.
|
||||||
|
c.epoch = time.Time{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CongestionWindowAfterPacketLoss computes a new congestion window to use after
|
||||||
|
// a loss event. Returns the new congestion window in packets. The new
|
||||||
|
// congestion window is a multiplicative decrease of our current window.
|
||||||
|
func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol.PacketNumber) protocol.PacketNumber {
|
||||||
|
if currentCongestionWindow < c.lastMaxCongestionWindow {
|
||||||
|
// We never reached the old max, so assume we are competing with another
|
||||||
|
// flow. Use our extra back off factor to allow the other flow to go up.
|
||||||
|
c.lastMaxCongestionWindow = protocol.PacketNumber(betaLastMax * float32(currentCongestionWindow))
|
||||||
|
} else {
|
||||||
|
c.lastMaxCongestionWindow = currentCongestionWindow
|
||||||
|
}
|
||||||
|
c.epoch = time.Time{} // Reset time.
|
||||||
|
return protocol.PacketNumber(float32(currentCongestionWindow) * c.beta())
|
||||||
|
}
|
||||||
|
|
||||||
|
// CongestionWindowAfterAck computes a new congestion window to use after a received ACK.
|
||||||
|
// Returns the new congestion window in packets. The new congestion window
|
||||||
|
// follows a cubic function that depends on the time passed since last
|
||||||
|
// packet loss.
|
||||||
|
func (c *Cubic) CongestionWindowAfterAck(currentCongestionWindow protocol.PacketNumber, delayMin time.Duration) protocol.PacketNumber {
|
||||||
|
c.ackedPacketsCount++ // Packets acked.
|
||||||
|
currentTime := c.clock.Now()
|
||||||
|
|
||||||
|
// Cubic is "independent" of RTT, the update is limited by the time elapsed.
|
||||||
|
if c.lastCongestionWindow == currentCongestionWindow && (currentTime.Sub(c.lastUpdateTime) <= maxCubicTimeInterval) {
|
||||||
|
return utils.MaxPacketNumber(c.lastTargetCongestionWindow, c.estimatedTCPcongestionWindow)
|
||||||
|
}
|
||||||
|
c.lastCongestionWindow = currentCongestionWindow
|
||||||
|
c.lastUpdateTime = currentTime
|
||||||
|
|
||||||
|
if c.epoch.IsZero() {
|
||||||
|
// First ACK after a loss event.
|
||||||
|
c.epoch = currentTime // Start of epoch.
|
||||||
|
c.ackedPacketsCount = 1 // Reset count.
|
||||||
|
// Reset estimated_tcp_congestion_window_ to be in sync with cubic.
|
||||||
|
c.estimatedTCPcongestionWindow = currentCongestionWindow
|
||||||
|
if c.lastMaxCongestionWindow <= currentCongestionWindow {
|
||||||
|
c.timeToOriginPoint = 0
|
||||||
|
c.originPointCongestionWindow = currentCongestionWindow
|
||||||
|
} else {
|
||||||
|
c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow))))
|
||||||
|
c.originPointCongestionWindow = c.lastMaxCongestionWindow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If sender was app-limited, then freeze congestion window growth during
|
||||||
|
// app-limited period. Continue growth now by shifting the epoch-start
|
||||||
|
// through the app-limited period.
|
||||||
|
if shiftQuicCubicEpochWhenAppLimited && !c.appLimitedStartTime.IsZero() {
|
||||||
|
shift := currentTime.Sub(c.appLimitedStartTime)
|
||||||
|
c.epoch = c.epoch.Add(shift)
|
||||||
|
c.appLimitedStartTime = time.Time{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the time unit from microseconds to 2^10 fractions per second. Take
|
||||||
|
// the round trip time in account. This is done to allow us to use shift as a
|
||||||
|
// divide operator.
|
||||||
|
elapsedTime := int64((currentTime.Add(delayMin).Sub(c.epoch)/time.Microsecond)<<10) / 1000000
|
||||||
|
|
||||||
|
offset := int64(c.timeToOriginPoint) - elapsedTime
|
||||||
|
// Right-shifts of negative, signed numbers have
|
||||||
|
// implementation-dependent behavior. Force the offset to be
|
||||||
|
// positive, similar to the kernel implementation.
|
||||||
|
if offset < 0 {
|
||||||
|
offset = -offset
|
||||||
|
}
|
||||||
|
deltaCongestionWindow := protocol.PacketNumber((cubeCongestionWindowScale * offset * offset * offset) >> cubeScale)
|
||||||
|
var targetCongestionWindow protocol.PacketNumber
|
||||||
|
if elapsedTime > int64(c.timeToOriginPoint) {
|
||||||
|
targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow
|
||||||
|
} else {
|
||||||
|
targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow
|
||||||
|
}
|
||||||
|
// With dynamic beta/alpha based on number of active streams, it is possible
|
||||||
|
// for the required_ack_count to become much lower than acked_packets_count_
|
||||||
|
// suddenly, leading to more than one iteration through the following loop.
|
||||||
|
for {
|
||||||
|
// Update estimated TCP congestion_window.
|
||||||
|
requiredAckCount := protocol.PacketNumber(float32(c.estimatedTCPcongestionWindow) / c.alpha())
|
||||||
|
if c.ackedPacketsCount < requiredAckCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.ackedPacketsCount -= requiredAckCount
|
||||||
|
c.estimatedTCPcongestionWindow++
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a new cubic congestion window.
|
||||||
|
c.lastTargetCongestionWindow = targetCongestionWindow
|
||||||
|
|
||||||
|
// Compute target congestion_window based on cubic target and estimated TCP
|
||||||
|
// congestion_window, use highest (fastest).
|
||||||
|
if targetCongestionWindow < c.estimatedTCPcongestionWindow {
|
||||||
|
targetCongestionWindow = c.estimatedTCPcongestionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetCongestionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNumConnections sets the number of emulated connections
|
||||||
|
func (c *Cubic) SetNumConnections(n int) {
|
||||||
|
c.numConnections = n
|
||||||
|
}
|
298
vendor/github.com/lucas-clemente/quic-go/congestion/cubic_sender.go
generated
vendored
Normal file
298
vendor/github.com/lucas-clemente/quic-go/congestion/cubic_sender.go
generated
vendored
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxBurstBytes = 3 * protocol.DefaultTCPMSS
|
||||||
|
defaultMinimumCongestionWindow protocol.PacketNumber = 2
|
||||||
|
renoBeta float32 = 0.7 // Reno backoff factor.
|
||||||
|
)
|
||||||
|
|
||||||
|
type cubicSender struct {
|
||||||
|
hybridSlowStart HybridSlowStart
|
||||||
|
prr PrrSender
|
||||||
|
rttStats *RTTStats
|
||||||
|
stats connectionStats
|
||||||
|
cubic *Cubic
|
||||||
|
|
||||||
|
reno bool
|
||||||
|
|
||||||
|
// Track the largest packet that has been sent.
|
||||||
|
largestSentPacketNumber protocol.PacketNumber
|
||||||
|
|
||||||
|
// Track the largest packet that has been acked.
|
||||||
|
largestAckedPacketNumber protocol.PacketNumber
|
||||||
|
|
||||||
|
// Track the largest packet number outstanding when a CWND cutback occurs.
|
||||||
|
largestSentAtLastCutback protocol.PacketNumber
|
||||||
|
|
||||||
|
// Congestion window in packets.
|
||||||
|
congestionWindow protocol.PacketNumber
|
||||||
|
|
||||||
|
// Slow start congestion window in packets, aka ssthresh.
|
||||||
|
slowstartThreshold protocol.PacketNumber
|
||||||
|
|
||||||
|
// Whether the last loss event caused us to exit slowstart.
|
||||||
|
// Used for stats collection of slowstartPacketsLost
|
||||||
|
lastCutbackExitedSlowstart bool
|
||||||
|
|
||||||
|
// When true, exit slow start with large cutback of congestion window.
|
||||||
|
slowStartLargeReduction bool
|
||||||
|
|
||||||
|
// Minimum congestion window in packets.
|
||||||
|
minCongestionWindow protocol.PacketNumber
|
||||||
|
|
||||||
|
// Maximum number of outstanding packets for tcp.
|
||||||
|
maxTCPCongestionWindow protocol.PacketNumber
|
||||||
|
|
||||||
|
// Number of connections to simulate.
|
||||||
|
numConnections int
|
||||||
|
|
||||||
|
// ACK counter for the Reno implementation.
|
||||||
|
congestionWindowCount protocol.ByteCount
|
||||||
|
|
||||||
|
initialCongestionWindow protocol.PacketNumber
|
||||||
|
initialMaxCongestionWindow protocol.PacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCubicSender makes a new cubic sender
|
||||||
|
func NewCubicSender(clock Clock, rttStats *RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.PacketNumber) SendAlgorithmWithDebugInfo {
|
||||||
|
return &cubicSender{
|
||||||
|
rttStats: rttStats,
|
||||||
|
initialCongestionWindow: initialCongestionWindow,
|
||||||
|
initialMaxCongestionWindow: initialMaxCongestionWindow,
|
||||||
|
congestionWindow: initialCongestionWindow,
|
||||||
|
minCongestionWindow: defaultMinimumCongestionWindow,
|
||||||
|
slowstartThreshold: initialMaxCongestionWindow,
|
||||||
|
maxTCPCongestionWindow: initialMaxCongestionWindow,
|
||||||
|
numConnections: defaultNumConnections,
|
||||||
|
cubic: NewCubic(clock),
|
||||||
|
reno: reno,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) TimeUntilSend(now time.Time, bytesInFlight protocol.ByteCount) time.Duration {
|
||||||
|
if c.InRecovery() {
|
||||||
|
// PRR is used when in recovery.
|
||||||
|
return c.prr.TimeUntilSend(c.GetCongestionWindow(), bytesInFlight, c.GetSlowStartThreshold())
|
||||||
|
}
|
||||||
|
if c.GetCongestionWindow() > bytesInFlight {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return utils.InfDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) bool {
|
||||||
|
// Only update bytesInFlight for data packets.
|
||||||
|
if !isRetransmittable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if c.InRecovery() {
|
||||||
|
// PRR is used when in recovery.
|
||||||
|
c.prr.OnPacketSent(bytes)
|
||||||
|
}
|
||||||
|
c.largestSentPacketNumber = packetNumber
|
||||||
|
c.hybridSlowStart.OnPacketSent(packetNumber)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) InRecovery() bool {
|
||||||
|
return c.largestAckedPacketNumber <= c.largestSentAtLastCutback && c.largestAckedPacketNumber != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) InSlowStart() bool {
|
||||||
|
return c.GetCongestionWindow() < c.GetSlowStartThreshold()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) GetCongestionWindow() protocol.ByteCount {
|
||||||
|
return protocol.ByteCount(c.congestionWindow) * protocol.DefaultTCPMSS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) GetSlowStartThreshold() protocol.ByteCount {
|
||||||
|
return protocol.ByteCount(c.slowstartThreshold) * protocol.DefaultTCPMSS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) ExitSlowstart() {
|
||||||
|
c.slowstartThreshold = c.congestionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) SlowstartThreshold() protocol.PacketNumber {
|
||||||
|
return c.slowstartThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) MaybeExitSlowStart() {
|
||||||
|
if c.InSlowStart() && c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/protocol.DefaultTCPMSS) {
|
||||||
|
c.ExitSlowstart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) OnPacketAcked(ackedPacketNumber protocol.PacketNumber, ackedBytes protocol.ByteCount, bytesInFlight protocol.ByteCount) {
|
||||||
|
c.largestAckedPacketNumber = utils.MaxPacketNumber(ackedPacketNumber, c.largestAckedPacketNumber)
|
||||||
|
if c.InRecovery() {
|
||||||
|
// PRR is used when in recovery.
|
||||||
|
c.prr.OnPacketAcked(ackedBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, bytesInFlight)
|
||||||
|
if c.InSlowStart() {
|
||||||
|
c.hybridSlowStart.OnPacketAcked(ackedPacketNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) OnPacketLost(packetNumber protocol.PacketNumber, lostBytes protocol.ByteCount, bytesInFlight protocol.ByteCount) {
|
||||||
|
// TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
|
||||||
|
// already sent should be treated as a single loss event, since it's expected.
|
||||||
|
if packetNumber <= c.largestSentAtLastCutback {
|
||||||
|
if c.lastCutbackExitedSlowstart {
|
||||||
|
c.stats.slowstartPacketsLost++
|
||||||
|
c.stats.slowstartBytesLost += lostBytes
|
||||||
|
if c.slowStartLargeReduction {
|
||||||
|
if c.stats.slowstartPacketsLost == 1 || (c.stats.slowstartBytesLost/protocol.DefaultTCPMSS) > (c.stats.slowstartBytesLost-lostBytes)/protocol.DefaultTCPMSS {
|
||||||
|
// Reduce congestion window by 1 for every mss of bytes lost.
|
||||||
|
c.congestionWindow = utils.MaxPacketNumber(c.congestionWindow-1, c.minCongestionWindow)
|
||||||
|
}
|
||||||
|
c.slowstartThreshold = c.congestionWindow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.lastCutbackExitedSlowstart = c.InSlowStart()
|
||||||
|
if c.InSlowStart() {
|
||||||
|
c.stats.slowstartPacketsLost++
|
||||||
|
}
|
||||||
|
|
||||||
|
c.prr.OnPacketLost(bytesInFlight)
|
||||||
|
|
||||||
|
// TODO(chromium): Separate out all of slow start into a separate class.
|
||||||
|
if c.slowStartLargeReduction && c.InSlowStart() {
|
||||||
|
c.congestionWindow = c.congestionWindow - 1
|
||||||
|
} else if c.reno {
|
||||||
|
c.congestionWindow = protocol.PacketNumber(float32(c.congestionWindow) * c.RenoBeta())
|
||||||
|
} else {
|
||||||
|
c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow)
|
||||||
|
}
|
||||||
|
// Enforce a minimum congestion window.
|
||||||
|
if c.congestionWindow < c.minCongestionWindow {
|
||||||
|
c.congestionWindow = c.minCongestionWindow
|
||||||
|
}
|
||||||
|
c.slowstartThreshold = c.congestionWindow
|
||||||
|
c.largestSentAtLastCutback = c.largestSentPacketNumber
|
||||||
|
// reset packet count from congestion avoidance mode. We start
|
||||||
|
// counting again when we're out of recovery.
|
||||||
|
c.congestionWindowCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) RenoBeta() float32 {
|
||||||
|
// kNConnectionBeta is the backoff factor after loss for our N-connection
|
||||||
|
// emulation, which emulates the effective backoff of an ensemble of N
|
||||||
|
// TCP-Reno connections on a single loss event. The effective multiplier is
|
||||||
|
// computed as:
|
||||||
|
return (float32(c.numConnections) - 1. + renoBeta) / float32(c.numConnections)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we receive an ack. Normal TCP tracks how many packets one ack
|
||||||
|
// represents, but quic has a separate ack for each packet.
|
||||||
|
func (c *cubicSender) maybeIncreaseCwnd(ackedPacketNumber protocol.PacketNumber, ackedBytes protocol.ByteCount, bytesInFlight protocol.ByteCount) {
|
||||||
|
// Do not increase the congestion window unless the sender is close to using
|
||||||
|
// the current window.
|
||||||
|
if !c.isCwndLimited(bytesInFlight) {
|
||||||
|
c.cubic.OnApplicationLimited()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.congestionWindow >= c.maxTCPCongestionWindow {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.InSlowStart() {
|
||||||
|
// TCP slow start, exponential growth, increase by one for each ACK.
|
||||||
|
c.congestionWindow++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.reno {
|
||||||
|
// Classic Reno congestion avoidance.
|
||||||
|
c.congestionWindowCount++
|
||||||
|
// Divide by num_connections to smoothly increase the CWND at a faster
|
||||||
|
// rate than conventional Reno.
|
||||||
|
if protocol.PacketNumber(c.congestionWindowCount*protocol.ByteCount(c.numConnections)) >= c.congestionWindow {
|
||||||
|
c.congestionWindow++
|
||||||
|
c.congestionWindowCount = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.congestionWindow = utils.MinPacketNumber(c.maxTCPCongestionWindow, c.cubic.CongestionWindowAfterAck(c.congestionWindow, c.rttStats.MinRTT()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool {
|
||||||
|
congestionWindow := c.GetCongestionWindow()
|
||||||
|
if bytesInFlight >= congestionWindow {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
availableBytes := congestionWindow - bytesInFlight
|
||||||
|
slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2
|
||||||
|
return slowStartLimited || availableBytes <= maxBurstBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// BandwidthEstimate returns the current bandwidth estimate
|
||||||
|
func (c *cubicSender) BandwidthEstimate() Bandwidth {
|
||||||
|
srtt := c.rttStats.SmoothedRTT()
|
||||||
|
if srtt == 0 {
|
||||||
|
// If we haven't measured an rtt, the bandwidth estimate is unknown.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return BandwidthFromDelta(c.GetCongestionWindow(), srtt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HybridSlowStart returns the hybrid slow start instance for testing
|
||||||
|
func (c *cubicSender) HybridSlowStart() *HybridSlowStart {
|
||||||
|
return &c.hybridSlowStart
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNumEmulatedConnections sets the number of emulated connections
|
||||||
|
func (c *cubicSender) SetNumEmulatedConnections(n int) {
|
||||||
|
c.numConnections = utils.Max(n, 1)
|
||||||
|
c.cubic.SetNumConnections(c.numConnections)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRetransmissionTimeout is called on an retransmission timeout
|
||||||
|
func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) {
|
||||||
|
c.largestSentAtLastCutback = 0
|
||||||
|
if !packetsRetransmitted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.hybridSlowStart.Restart()
|
||||||
|
c.cubic.Reset()
|
||||||
|
c.slowstartThreshold = c.congestionWindow / 2
|
||||||
|
c.congestionWindow = c.minCongestionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnConnectionMigration is called when the connection is migrated (?)
|
||||||
|
func (c *cubicSender) OnConnectionMigration() {
|
||||||
|
c.hybridSlowStart.Restart()
|
||||||
|
c.prr = PrrSender{}
|
||||||
|
c.largestSentPacketNumber = 0
|
||||||
|
c.largestAckedPacketNumber = 0
|
||||||
|
c.largestSentAtLastCutback = 0
|
||||||
|
c.lastCutbackExitedSlowstart = false
|
||||||
|
c.cubic.Reset()
|
||||||
|
c.congestionWindowCount = 0
|
||||||
|
c.congestionWindow = c.initialCongestionWindow
|
||||||
|
c.slowstartThreshold = c.initialMaxCongestionWindow
|
||||||
|
c.maxTCPCongestionWindow = c.initialMaxCongestionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSlowStartLargeReduction allows enabling the SSLR experiment
|
||||||
|
func (c *cubicSender) SetSlowStartLargeReduction(enabled bool) {
|
||||||
|
c.slowStartLargeReduction = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetransmissionDelay gives the time to retransmission
|
||||||
|
func (c *cubicSender) RetransmissionDelay() time.Duration {
|
||||||
|
if c.rttStats.SmoothedRTT() == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return c.rttStats.SmoothedRTT() + c.rttStats.MeanDeviation()*4
|
||||||
|
}
|
111
vendor/github.com/lucas-clemente/quic-go/congestion/hybrid_slow_start.go
generated
vendored
Normal file
111
vendor/github.com/lucas-clemente/quic-go/congestion/hybrid_slow_start.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Note(pwestin): the magic clamping numbers come from the original code in
|
||||||
|
// tcp_cubic.c.
|
||||||
|
const hybridStartLowWindow = protocol.ByteCount(16)
|
||||||
|
|
||||||
|
// Number of delay samples for detecting the increase of delay.
|
||||||
|
const hybridStartMinSamples = uint32(8)
|
||||||
|
|
||||||
|
// Exit slow start if the min rtt has increased by more than 1/8th.
|
||||||
|
const hybridStartDelayFactorExp = 3 // 2^3 = 8
|
||||||
|
// The original paper specifies 2 and 8ms, but those have changed over time.
|
||||||
|
const hybridStartDelayMinThresholdUs = int64(4000)
|
||||||
|
const hybridStartDelayMaxThresholdUs = int64(16000)
|
||||||
|
|
||||||
|
// HybridSlowStart implements the TCP hybrid slow start algorithm
|
||||||
|
type HybridSlowStart struct {
|
||||||
|
endPacketNumber protocol.PacketNumber
|
||||||
|
lastSentPacketNumber protocol.PacketNumber
|
||||||
|
started bool
|
||||||
|
currentMinRTT time.Duration
|
||||||
|
rttSampleCount uint32
|
||||||
|
hystartFound bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase.
|
||||||
|
func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) {
|
||||||
|
s.endPacketNumber = lastSent
|
||||||
|
s.currentMinRTT = 0
|
||||||
|
s.rttSampleCount = 0
|
||||||
|
s.started = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEndOfRound returns true if this ack is the last packet number of our current slow start round.
|
||||||
|
func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool {
|
||||||
|
return s.endPacketNumber < ack
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldExitSlowStart should be called on every new ack frame, since a new
|
||||||
|
// RTT measurement can be made then.
|
||||||
|
// rtt: the RTT for this ack packet.
|
||||||
|
// minRTT: is the lowest delay (RTT) we have seen during the session.
|
||||||
|
// congestionWindow: the congestion window in packets.
|
||||||
|
func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool {
|
||||||
|
if !s.started {
|
||||||
|
// Time to start the hybrid slow start.
|
||||||
|
s.StartReceiveRound(s.lastSentPacketNumber)
|
||||||
|
}
|
||||||
|
if s.hystartFound {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Second detection parameter - delay increase detection.
|
||||||
|
// Compare the minimum delay (s.currentMinRTT) of the current
|
||||||
|
// burst of packets relative to the minimum delay during the session.
|
||||||
|
// Note: we only look at the first few(8) packets in each burst, since we
|
||||||
|
// only want to compare the lowest RTT of the burst relative to previous
|
||||||
|
// bursts.
|
||||||
|
s.rttSampleCount++
|
||||||
|
if s.rttSampleCount <= hybridStartMinSamples {
|
||||||
|
if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT {
|
||||||
|
s.currentMinRTT = latestRTT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We only need to check this once per round.
|
||||||
|
if s.rttSampleCount == hybridStartMinSamples {
|
||||||
|
// Divide minRTT by 8 to get a rtt increase threshold for exiting.
|
||||||
|
minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp)
|
||||||
|
// Ensure the rtt threshold is never less than 2ms or more than 16ms.
|
||||||
|
minRTTincreaseThresholdUs = utils.MinInt64(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs)
|
||||||
|
minRTTincreaseThreshold := time.Duration(utils.MaxInt64(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond
|
||||||
|
|
||||||
|
if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) {
|
||||||
|
s.hystartFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Exit from slow start if the cwnd is greater than 16 and
|
||||||
|
// increasing delay is found.
|
||||||
|
return congestionWindow >= hybridStartLowWindow && s.hystartFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPacketSent is called when a packet was sent
|
||||||
|
func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) {
|
||||||
|
s.lastSentPacketNumber = packetNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
|
||||||
|
// the round when the final packet of the burst is received and start it on
|
||||||
|
// the next incoming ack.
|
||||||
|
func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) {
|
||||||
|
if s.IsEndOfRound(ackedPacketNumber) {
|
||||||
|
s.started = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Started returns true if started
|
||||||
|
func (s *HybridSlowStart) Started() bool {
|
||||||
|
return s.started
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart the slow start phase
|
||||||
|
func (s *HybridSlowStart) Restart() {
|
||||||
|
s.started = false
|
||||||
|
s.hystartFound = false
|
||||||
|
}
|
37
vendor/github.com/lucas-clemente/quic-go/congestion/interface.go
generated
vendored
Normal file
37
vendor/github.com/lucas-clemente/quic-go/congestion/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SendAlgorithm performs congestion control and calculates the congestion window
|
||||||
|
type SendAlgorithm interface {
|
||||||
|
TimeUntilSend(now time.Time, bytesInFlight protocol.ByteCount) time.Duration
|
||||||
|
OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) bool
|
||||||
|
GetCongestionWindow() protocol.ByteCount
|
||||||
|
MaybeExitSlowStart()
|
||||||
|
OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, bytesInFlight protocol.ByteCount)
|
||||||
|
OnPacketLost(number protocol.PacketNumber, lostBytes protocol.ByteCount, bytesInFlight protocol.ByteCount)
|
||||||
|
SetNumEmulatedConnections(n int)
|
||||||
|
OnRetransmissionTimeout(packetsRetransmitted bool)
|
||||||
|
OnConnectionMigration()
|
||||||
|
RetransmissionDelay() time.Duration
|
||||||
|
|
||||||
|
// Experiments
|
||||||
|
SetSlowStartLargeReduction(enabled bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendAlgorithmWithDebugInfo adds some debug functions to SendAlgorithm
|
||||||
|
type SendAlgorithmWithDebugInfo interface {
|
||||||
|
SendAlgorithm
|
||||||
|
BandwidthEstimate() Bandwidth
|
||||||
|
|
||||||
|
// Stuff only used in testing
|
||||||
|
|
||||||
|
HybridSlowStart() *HybridSlowStart
|
||||||
|
SlowstartThreshold() protocol.PacketNumber
|
||||||
|
RenoBeta() float32
|
||||||
|
InRecovery() bool
|
||||||
|
}
|
63
vendor/github.com/lucas-clemente/quic-go/congestion/prr_sender.go
generated
vendored
Normal file
63
vendor/github.com/lucas-clemente/quic-go/congestion/prr_sender.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrrSender implements the Proportional Rate Reduction (PRR) per RFC 6937
|
||||||
|
type PrrSender struct {
|
||||||
|
bytesSentSinceLoss protocol.ByteCount
|
||||||
|
bytesDeliveredSinceLoss protocol.ByteCount
|
||||||
|
ackCountSinceLoss protocol.ByteCount
|
||||||
|
bytesInFlightBeforeLoss protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPacketSent should be called after a packet was sent
|
||||||
|
func (p *PrrSender) OnPacketSent(sentBytes protocol.ByteCount) {
|
||||||
|
p.bytesSentSinceLoss += sentBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPacketLost should be called on the first loss that triggers a recovery
|
||||||
|
// period and all other methods in this class should only be called when in
|
||||||
|
// recovery.
|
||||||
|
func (p *PrrSender) OnPacketLost(bytesInFlight protocol.ByteCount) {
|
||||||
|
p.bytesSentSinceLoss = 0
|
||||||
|
p.bytesInFlightBeforeLoss = bytesInFlight
|
||||||
|
p.bytesDeliveredSinceLoss = 0
|
||||||
|
p.ackCountSinceLoss = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPacketAcked should be called after a packet was acked
|
||||||
|
func (p *PrrSender) OnPacketAcked(ackedBytes protocol.ByteCount) {
|
||||||
|
p.bytesDeliveredSinceLoss += ackedBytes
|
||||||
|
p.ackCountSinceLoss++
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeUntilSend calculates the time until a packet can be sent
|
||||||
|
func (p *PrrSender) TimeUntilSend(congestionWindow, bytesInFlight, slowstartThreshold protocol.ByteCount) time.Duration {
|
||||||
|
// Return QuicTime::Zero In order to ensure limited transmit always works.
|
||||||
|
if p.bytesSentSinceLoss == 0 || bytesInFlight < protocol.DefaultTCPMSS {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if congestionWindow > bytesInFlight {
|
||||||
|
// During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead
|
||||||
|
// of sending the entire available window. This prevents burst retransmits
|
||||||
|
// when more packets are lost than the CWND reduction.
|
||||||
|
// limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS
|
||||||
|
if p.bytesDeliveredSinceLoss+p.ackCountSinceLoss*protocol.DefaultTCPMSS <= p.bytesSentSinceLoss {
|
||||||
|
return utils.InfDuration
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// Implement Proportional Rate Reduction (RFC6937).
|
||||||
|
// Checks a simplified version of the PRR formula that doesn't use division:
|
||||||
|
// AvailableSendWindow =
|
||||||
|
// CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent
|
||||||
|
if p.bytesDeliveredSinceLoss*slowstartThreshold > p.bytesSentSinceLoss*p.bytesInFlightBeforeLoss {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return utils.InfDuration
|
||||||
|
}
|
182
vendor/github.com/lucas-clemente/quic-go/congestion/rtt_stats.go
generated
vendored
Normal file
182
vendor/github.com/lucas-clemente/quic-go/congestion/rtt_stats.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
initialRTTus = 100 * 1000
|
||||||
|
rttAlpha float32 = 0.125
|
||||||
|
oneMinusAlpha float32 = (1 - rttAlpha)
|
||||||
|
rttBeta float32 = 0.25
|
||||||
|
oneMinusBeta float32 = (1 - rttBeta)
|
||||||
|
halfWindow float32 = 0.5
|
||||||
|
quarterWindow float32 = 0.25
|
||||||
|
)
|
||||||
|
|
||||||
|
type rttSample struct {
|
||||||
|
rtt time.Duration
|
||||||
|
time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTTStats provides round-trip statistics
|
||||||
|
type RTTStats struct {
|
||||||
|
initialRTTus int64
|
||||||
|
|
||||||
|
recentMinRTTwindow time.Duration
|
||||||
|
minRTT time.Duration
|
||||||
|
latestRTT time.Duration
|
||||||
|
smoothedRTT time.Duration
|
||||||
|
meanDeviation time.Duration
|
||||||
|
|
||||||
|
numMinRTTsamplesRemaining uint32
|
||||||
|
|
||||||
|
newMinRTT rttSample
|
||||||
|
recentMinRTT rttSample
|
||||||
|
halfWindowRTT rttSample
|
||||||
|
quarterWindowRTT rttSample
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRTTStats makes a properly initialized RTTStats object
|
||||||
|
func NewRTTStats() *RTTStats {
|
||||||
|
return &RTTStats{
|
||||||
|
initialRTTus: initialRTTus,
|
||||||
|
recentMinRTTwindow: utils.InfDuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitialRTTus is the initial RTT in us
|
||||||
|
func (r *RTTStats) InitialRTTus() int64 { return r.initialRTTus }
|
||||||
|
|
||||||
|
// MinRTT Returns the minRTT for the entire connection.
|
||||||
|
// May return Zero if no valid updates have occurred.
|
||||||
|
func (r *RTTStats) MinRTT() time.Duration { return r.minRTT }
|
||||||
|
|
||||||
|
// LatestRTT returns the most recent rtt measurement.
|
||||||
|
// May return Zero if no valid updates have occurred.
|
||||||
|
func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT }
|
||||||
|
|
||||||
|
// RecentMinRTT the minRTT since SampleNewRecentMinRtt has been called, or the
|
||||||
|
// minRTT for the entire connection if SampleNewMinRtt was never called.
|
||||||
|
func (r *RTTStats) RecentMinRTT() time.Duration { return r.recentMinRTT.rtt }
|
||||||
|
|
||||||
|
// SmoothedRTT returns the EWMA smoothed RTT for the connection.
|
||||||
|
// May return Zero if no valid updates have occurred.
|
||||||
|
func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT }
|
||||||
|
|
||||||
|
// GetQuarterWindowRTT gets the quarter window RTT
|
||||||
|
func (r *RTTStats) GetQuarterWindowRTT() time.Duration { return r.quarterWindowRTT.rtt }
|
||||||
|
|
||||||
|
// GetHalfWindowRTT gets the half window RTT
|
||||||
|
func (r *RTTStats) GetHalfWindowRTT() time.Duration { return r.halfWindowRTT.rtt }
|
||||||
|
|
||||||
|
// MeanDeviation gets the mean deviation
|
||||||
|
func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation }
|
||||||
|
|
||||||
|
// SetRecentMinRTTwindow sets how old a recent min rtt sample can be.
|
||||||
|
func (r *RTTStats) SetRecentMinRTTwindow(recentMinRTTwindow time.Duration) {
|
||||||
|
r.recentMinRTTwindow = recentMinRTTwindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRTT updates the RTT based on a new sample.
|
||||||
|
func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) {
|
||||||
|
if sendDelta == utils.InfDuration || sendDelta <= 0 {
|
||||||
|
utils.Debugf("Ignoring measured sendDelta, because it's is either infinite, zero, or negative: %d", sendDelta/time.Microsecond)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update r.minRTT first. r.minRTT does not use an rttSample corrected for
|
||||||
|
// ackDelay but the raw observed sendDelta, since poor clock granularity at
|
||||||
|
// the client may cause a high ackDelay to result in underestimation of the
|
||||||
|
// r.minRTT.
|
||||||
|
if r.minRTT == 0 || r.minRTT > sendDelta {
|
||||||
|
r.minRTT = sendDelta
|
||||||
|
}
|
||||||
|
r.updateRecentMinRTT(sendDelta, now)
|
||||||
|
|
||||||
|
// Correct for ackDelay if information received from the peer results in a
|
||||||
|
// positive RTT sample. Otherwise, we use the sendDelta as a reasonable
|
||||||
|
// measure for smoothedRTT.
|
||||||
|
sample := sendDelta
|
||||||
|
if sample > ackDelay {
|
||||||
|
sample -= ackDelay
|
||||||
|
}
|
||||||
|
r.latestRTT = sample
|
||||||
|
// First time call.
|
||||||
|
if r.smoothedRTT == 0 {
|
||||||
|
r.smoothedRTT = sample
|
||||||
|
r.meanDeviation = sample / 2
|
||||||
|
} else {
|
||||||
|
r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32(utils.AbsDuration(r.smoothedRTT-sample)/time.Microsecond)) * time.Microsecond
|
||||||
|
r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RTTStats) updateRecentMinRTT(sample time.Duration, now time.Time) { // Recent minRTT update.
|
||||||
|
if r.numMinRTTsamplesRemaining > 0 {
|
||||||
|
r.numMinRTTsamplesRemaining--
|
||||||
|
if r.newMinRTT.rtt == 0 || sample <= r.newMinRTT.rtt {
|
||||||
|
r.newMinRTT = rttSample{rtt: sample, time: now}
|
||||||
|
}
|
||||||
|
if r.numMinRTTsamplesRemaining == 0 {
|
||||||
|
r.recentMinRTT = r.newMinRTT
|
||||||
|
r.halfWindowRTT = r.newMinRTT
|
||||||
|
r.quarterWindowRTT = r.newMinRTT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the three recent rtt samples.
|
||||||
|
if r.recentMinRTT.rtt == 0 || sample <= r.recentMinRTT.rtt {
|
||||||
|
r.recentMinRTT = rttSample{rtt: sample, time: now}
|
||||||
|
r.halfWindowRTT = r.recentMinRTT
|
||||||
|
r.quarterWindowRTT = r.recentMinRTT
|
||||||
|
} else if sample <= r.halfWindowRTT.rtt {
|
||||||
|
r.halfWindowRTT = rttSample{rtt: sample, time: now}
|
||||||
|
r.quarterWindowRTT = r.halfWindowRTT
|
||||||
|
} else if sample <= r.quarterWindowRTT.rtt {
|
||||||
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire old min rtt samples.
|
||||||
|
if r.recentMinRTT.time.Before(now.Add(-r.recentMinRTTwindow)) {
|
||||||
|
r.recentMinRTT = r.halfWindowRTT
|
||||||
|
r.halfWindowRTT = r.quarterWindowRTT
|
||||||
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
||||||
|
} else if r.halfWindowRTT.time.Before(now.Add(-time.Duration(float32(r.recentMinRTTwindow/time.Microsecond)*halfWindow) * time.Microsecond)) {
|
||||||
|
r.halfWindowRTT = r.quarterWindowRTT
|
||||||
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
||||||
|
} else if r.quarterWindowRTT.time.Before(now.Add(-time.Duration(float32(r.recentMinRTTwindow/time.Microsecond)*quarterWindow) * time.Microsecond)) {
|
||||||
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleNewRecentMinRTT forces RttStats to sample a new recent min rtt within the next
|
||||||
|
// |numSamples| UpdateRTT calls.
|
||||||
|
func (r *RTTStats) SampleNewRecentMinRTT(numSamples uint32) {
|
||||||
|
r.numMinRTTsamplesRemaining = numSamples
|
||||||
|
r.newMinRTT = rttSample{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset.
|
||||||
|
func (r *RTTStats) OnConnectionMigration() {
|
||||||
|
r.latestRTT = 0
|
||||||
|
r.minRTT = 0
|
||||||
|
r.smoothedRTT = 0
|
||||||
|
r.meanDeviation = 0
|
||||||
|
r.initialRTTus = initialRTTus
|
||||||
|
r.numMinRTTsamplesRemaining = 0
|
||||||
|
r.recentMinRTTwindow = utils.InfDuration
|
||||||
|
r.recentMinRTT = rttSample{}
|
||||||
|
r.halfWindowRTT = rttSample{}
|
||||||
|
r.quarterWindowRTT = rttSample{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
|
||||||
|
// is larger. The mean deviation is increased to the most recent deviation if
|
||||||
|
// it's larger.
|
||||||
|
func (r *RTTStats) ExpireSmoothedMetrics() {
|
||||||
|
r.meanDeviation = utils.MaxDuration(r.meanDeviation, utils.AbsDuration(r.smoothedRTT-r.latestRTT))
|
||||||
|
r.smoothedRTT = utils.MaxDuration(r.smoothedRTT, r.latestRTT)
|
||||||
|
}
|
8
vendor/github.com/lucas-clemente/quic-go/congestion/stats.go
generated
vendored
Normal file
8
vendor/github.com/lucas-clemente/quic-go/congestion/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package congestion
|
||||||
|
|
||||||
|
import "github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
|
||||||
|
type connectionStats struct {
|
||||||
|
slowstartPacketsLost protocol.PacketNumber
|
||||||
|
slowstartBytesLost protocol.ByteCount
|
||||||
|
}
|
54
vendor/github.com/lucas-clemente/quic-go/conn.go
generated
vendored
Normal file
54
vendor/github.com/lucas-clemente/quic-go/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connection interface {
|
||||||
|
Write([]byte) error
|
||||||
|
Read([]byte) (int, net.Addr, error)
|
||||||
|
Close() error
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
RemoteAddr() net.Addr
|
||||||
|
SetCurrentRemoteAddr(net.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type conn struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
pconn net.PacketConn
|
||||||
|
currentAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ connection = &conn{}
|
||||||
|
|
||||||
|
func (c *conn) Write(p []byte) error {
|
||||||
|
_, err := c.pconn.WriteTo(p, c.currentAddr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Read(p []byte) (int, net.Addr, error) {
|
||||||
|
return c.pconn.ReadFrom(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) SetCurrentRemoteAddr(addr net.Addr) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.currentAddr = addr
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) LocalAddr() net.Addr {
|
||||||
|
return c.pconn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) RemoteAddr() net.Addr {
|
||||||
|
c.mutex.RLock()
|
||||||
|
addr := c.currentAddr
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Close() error {
|
||||||
|
return c.pconn.Close()
|
||||||
|
}
|
9
vendor/github.com/lucas-clemente/quic-go/crypto/AEAD.go
generated
vendored
Normal file
9
vendor/github.com/lucas-clemente/quic-go/crypto/AEAD.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import "github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
|
||||||
|
// An AEAD implements QUIC's authenticated encryption and associated data
|
||||||
|
type AEAD interface {
|
||||||
|
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
|
||||||
|
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
|
||||||
|
}
|
58
vendor/github.com/lucas-clemente/quic-go/crypto/aesgcm_aead.go
generated
vendored
Normal file
58
vendor/github.com/lucas-clemente/quic-go/crypto/aesgcm_aead.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/aes12"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aeadAESGCM struct {
|
||||||
|
otherIV []byte
|
||||||
|
myIV []byte
|
||||||
|
encrypter cipher.AEAD
|
||||||
|
decrypter cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAEADAESGCM creates a AEAD using AES-GCM with 12 bytes tag size
|
||||||
|
//
|
||||||
|
// AES-GCM support is a bit hacky, since the go stdlib does not support 12 byte
|
||||||
|
// tag size, and couples the cipher and aes packages closely.
|
||||||
|
// See https://github.com/lucas-clemente/aes12.
|
||||||
|
func NewAEADAESGCM(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
|
||||||
|
if len(myKey) != 16 || len(otherKey) != 16 || len(myIV) != 4 || len(otherIV) != 4 {
|
||||||
|
return nil, errors.New("AES-GCM: expected 16-byte keys and 4-byte IVs")
|
||||||
|
}
|
||||||
|
encrypterCipher, err := aes12.NewCipher(myKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encrypter, err := aes12.NewGCM(encrypterCipher)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decrypterCipher, err := aes12.NewCipher(otherKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decrypter, err := aes12.NewGCM(decrypterCipher)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &aeadAESGCM{
|
||||||
|
otherIV: otherIV,
|
||||||
|
myIV: myIV,
|
||||||
|
encrypter: encrypter,
|
||||||
|
decrypter: decrypter,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead *aeadAESGCM) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||||
|
return aead.decrypter.Open(dst, makeNonce(aead.otherIV, packetNumber), src, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead *aeadAESGCM) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return aead.encrypter.Seal(dst, makeNonce(aead.myIV, packetNumber), src, associatedData)
|
||||||
|
}
|
48
vendor/github.com/lucas-clemente/quic-go/crypto/cert_cache.go
generated
vendored
Normal file
48
vendor/github.com/lucas-clemente/quic-go/crypto/cert_cache.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
compressedCertsCache *lru.Cache
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCompressedCert(chain [][]byte, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
|
||||||
|
// Hash all inputs
|
||||||
|
hasher := fnv.New64a()
|
||||||
|
for _, v := range chain {
|
||||||
|
hasher.Write(v)
|
||||||
|
}
|
||||||
|
hasher.Write(pCommonSetHashes)
|
||||||
|
hasher.Write(pCachedHashes)
|
||||||
|
hash := hasher.Sum64()
|
||||||
|
|
||||||
|
var result []byte
|
||||||
|
|
||||||
|
resultI, isCached := compressedCertsCache.Get(hash)
|
||||||
|
if isCached {
|
||||||
|
result = resultI.([]byte)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
result, err = compressChain(chain, pCommonSetHashes, pCachedHashes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
compressedCertsCache.Add(hash, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
compressedCertsCache, err = lru.New(protocol.NumCachedCertificates)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("fatal error in quic-go: could not create lru cache: %s", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
113
vendor/github.com/lucas-clemente/quic-go/crypto/cert_chain.go
generated
vendored
Normal file
113
vendor/github.com/lucas-clemente/quic-go/crypto/cert_chain.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A CertChain holds a certificate and a private key
|
||||||
|
type CertChain interface {
|
||||||
|
SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error)
|
||||||
|
GetCertsCompressed(sni string, commonSetHashes, cachedHashes []byte) ([]byte, error)
|
||||||
|
GetLeafCert(sni string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// proofSource stores a key and a certificate for the server proof
|
||||||
|
type certChain struct {
|
||||||
|
config *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CertChain = &certChain{}
|
||||||
|
|
||||||
|
var errNoMatchingCertificate = errors.New("no matching certificate found")
|
||||||
|
|
||||||
|
// NewCertChain loads the key and cert from files
|
||||||
|
func NewCertChain(tlsConfig *tls.Config) CertChain {
|
||||||
|
return &certChain{config: tlsConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignServerProof signs CHLO and server config for use in the server proof
|
||||||
|
func (c *certChain) SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error) {
|
||||||
|
cert, err := c.getCertForSNI(sni)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signServerProof(cert, chlo, serverConfigData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCertsCompressed gets the certificate in the format described by the QUIC crypto doc
|
||||||
|
func (c *certChain) GetCertsCompressed(sni string, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
|
||||||
|
cert, err := c.getCertForSNI(sni)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getCompressedCert(cert.Certificate, pCommonSetHashes, pCachedHashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLeafCert gets the leaf certificate
|
||||||
|
func (c *certChain) GetLeafCert(sni string) ([]byte, error) {
|
||||||
|
cert, err := c.getCertForSNI(sni)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cert.Certificate[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *certChain) getCertForSNI(sni string) (*tls.Certificate, error) {
|
||||||
|
c := cc.config
|
||||||
|
c, err := maybeGetConfigForClient(c, sni)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// The rest of this function is mostly copied from crypto/tls.getCertificate
|
||||||
|
|
||||||
|
if c.GetCertificate != nil {
|
||||||
|
cert, err := c.GetCertificate(&tls.ClientHelloInfo{ServerName: sni})
|
||||||
|
if cert != nil || err != nil {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Certificates) == 0 {
|
||||||
|
return nil, errNoMatchingCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Certificates) == 1 || c.NameToCertificate == nil {
|
||||||
|
// There's only one choice, so no point doing any work.
|
||||||
|
return &c.Certificates[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
name := strings.ToLower(sni)
|
||||||
|
for len(name) > 0 && name[len(name)-1] == '.' {
|
||||||
|
name = name[:len(name)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert, ok := c.NameToCertificate[name]; ok {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// try replacing labels in the name with wildcards until we get a
|
||||||
|
// match.
|
||||||
|
labels := strings.Split(name, ".")
|
||||||
|
for i := range labels {
|
||||||
|
labels[i] = "*"
|
||||||
|
candidate := strings.Join(labels, ".")
|
||||||
|
if cert, ok := c.NameToCertificate[candidate]; ok {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing matches, return the first certificate.
|
||||||
|
return &c.Certificates[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func maybeGetConfigForClient(c *tls.Config, sni string) (*tls.Config, error) {
|
||||||
|
if c.GetConfigForClient == nil {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return c.GetConfigForClient(&tls.ClientHelloInfo{
|
||||||
|
ServerName: sni,
|
||||||
|
})
|
||||||
|
}
|
272
vendor/github.com/lucas-clemente/quic-go/crypto/cert_compression.go
generated
vendored
Normal file
272
vendor/github.com/lucas-clemente/quic-go/crypto/cert_compression.go
generated
vendored
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/flate"
|
||||||
|
"compress/zlib"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type entryType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
entryCompressed entryType = 1
|
||||||
|
entryCached entryType = 2
|
||||||
|
entryCommon entryType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
t entryType
|
||||||
|
h uint64 // set hash
|
||||||
|
i uint32 // index
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressChain(chain [][]byte, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
|
||||||
|
res := &bytes.Buffer{}
|
||||||
|
|
||||||
|
cachedHashes, err := splitHashes(pCachedHashes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
setHashes, err := splitHashes(pCommonSetHashes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chainHashes := make([]uint64, len(chain))
|
||||||
|
for i := range chain {
|
||||||
|
chainHashes[i] = HashCert(chain[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := buildEntries(chain, chainHashes, cachedHashes, setHashes)
|
||||||
|
|
||||||
|
totalUncompressedLen := 0
|
||||||
|
for i, e := range entries {
|
||||||
|
res.WriteByte(uint8(e.t))
|
||||||
|
switch e.t {
|
||||||
|
case entryCached:
|
||||||
|
utils.WriteUint64(res, e.h)
|
||||||
|
case entryCommon:
|
||||||
|
utils.WriteUint64(res, e.h)
|
||||||
|
utils.WriteUint32(res, e.i)
|
||||||
|
case entryCompressed:
|
||||||
|
totalUncompressedLen += 4 + len(chain[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.WriteByte(0) // end of list
|
||||||
|
|
||||||
|
if totalUncompressedLen > 0 {
|
||||||
|
gz, err := zlib.NewWriterLevelDict(res, flate.BestCompression, buildZlibDictForEntries(entries, chain))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cert compression failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.WriteUint32(res, uint32(totalUncompressedLen))
|
||||||
|
|
||||||
|
for i, e := range entries {
|
||||||
|
if e.t != entryCompressed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lenCert := len(chain[i])
|
||||||
|
gz.Write([]byte{
|
||||||
|
byte(lenCert & 0xff),
|
||||||
|
byte((lenCert >> 8) & 0xff),
|
||||||
|
byte((lenCert >> 16) & 0xff),
|
||||||
|
byte((lenCert >> 24) & 0xff),
|
||||||
|
})
|
||||||
|
gz.Write(chain[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
gz.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decompressChain(data []byte) ([][]byte, error) {
|
||||||
|
var chain [][]byte
|
||||||
|
var entries []entry
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
|
||||||
|
var numCerts int
|
||||||
|
var hasCompressedCerts bool
|
||||||
|
for {
|
||||||
|
entryTypeByte, err := r.ReadByte()
|
||||||
|
if entryTypeByte == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
et := entryType(entryTypeByte)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
numCerts++
|
||||||
|
|
||||||
|
switch et {
|
||||||
|
case entryCached:
|
||||||
|
// we're not sending any certificate hashes in the CHLO, so there shouldn't be any cached certificates in the chain
|
||||||
|
return nil, errors.New("unexpected cached certificate")
|
||||||
|
case entryCommon:
|
||||||
|
e := entry{t: entryCommon}
|
||||||
|
e.h, err = utils.ReadUint64(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
e.i, err = utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certSet, ok := certSets[e.h]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unknown certSet")
|
||||||
|
}
|
||||||
|
if e.i >= uint32(len(certSet)) {
|
||||||
|
return nil, errors.New("certificate not found in certSet")
|
||||||
|
}
|
||||||
|
entries = append(entries, e)
|
||||||
|
chain = append(chain, certSet[e.i])
|
||||||
|
case entryCompressed:
|
||||||
|
hasCompressedCerts = true
|
||||||
|
entries = append(entries, entry{t: entryCompressed})
|
||||||
|
chain = append(chain, nil)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown entryType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numCerts == 0 {
|
||||||
|
return make([][]byte, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasCompressedCerts {
|
||||||
|
uncompressedLength, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(4)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
zlibDict := buildZlibDictForEntries(entries, chain)
|
||||||
|
gz, err := zlib.NewReaderDict(r, zlibDict)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer gz.Close()
|
||||||
|
|
||||||
|
var totalLength uint32
|
||||||
|
var certIndex int
|
||||||
|
for totalLength < uncompressedLength {
|
||||||
|
lenBytes := make([]byte, 4)
|
||||||
|
_, err := gz.Read(lenBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certLen := binary.LittleEndian.Uint32(lenBytes)
|
||||||
|
|
||||||
|
cert := make([]byte, certLen)
|
||||||
|
n, err := gz.Read(cert)
|
||||||
|
if uint32(n) != certLen && err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if certIndex >= len(entries) {
|
||||||
|
return nil, errors.New("CertCompression BUG: no element to save uncompressed certificate")
|
||||||
|
}
|
||||||
|
if entries[certIndex].t == entryCompressed {
|
||||||
|
chain[certIndex] = cert
|
||||||
|
certIndex++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
certIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
totalLength += 4 + certLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildEntries(chain [][]byte, chainHashes, cachedHashes, setHashes []uint64) []entry {
|
||||||
|
res := make([]entry, len(chain))
|
||||||
|
chainLoop:
|
||||||
|
for i := range chain {
|
||||||
|
// Check if hash is in cachedHashes
|
||||||
|
for j := range cachedHashes {
|
||||||
|
if chainHashes[i] == cachedHashes[j] {
|
||||||
|
res[i] = entry{t: entryCached, h: chainHashes[i]}
|
||||||
|
continue chainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through common sets and check if it's in there
|
||||||
|
for _, setHash := range setHashes {
|
||||||
|
set, ok := certSets[setHash]
|
||||||
|
if !ok {
|
||||||
|
// We don't have this set
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// We have this set, check if chain[i] is in the set
|
||||||
|
pos := set.findCertInSet(chain[i])
|
||||||
|
if pos >= 0 {
|
||||||
|
// Found
|
||||||
|
res[i] = entry{t: entryCommon, h: setHash, i: uint32(pos)}
|
||||||
|
continue chainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res[i] = entry{t: entryCompressed}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildZlibDictForEntries(entries []entry, chain [][]byte) []byte {
|
||||||
|
var dict bytes.Buffer
|
||||||
|
|
||||||
|
// First the cached and common in reverse order
|
||||||
|
for i := len(entries) - 1; i >= 0; i-- {
|
||||||
|
if entries[i].t == entryCompressed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dict.Write(chain[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
dict.Write(certDictZlib)
|
||||||
|
return dict.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitHashes(hashes []byte) ([]uint64, error) {
|
||||||
|
if len(hashes)%8 != 0 {
|
||||||
|
return nil, errors.New("expected a multiple of 8 bytes for CCS / CCRT hashes")
|
||||||
|
}
|
||||||
|
n := len(hashes) / 8
|
||||||
|
res := make([]uint64, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
res[i] = binary.LittleEndian.Uint64(hashes[i*8 : (i+1)*8])
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCommonCertificateHashes() []byte {
|
||||||
|
ccs := make([]byte, 8*len(certSets))
|
||||||
|
i := 0
|
||||||
|
for certSetHash := range certSets {
|
||||||
|
binary.LittleEndian.PutUint64(ccs[i*8:(i+1)*8], certSetHash)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return ccs
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashCert calculates the FNV1a hash of a certificate
|
||||||
|
func HashCert(cert []byte) uint64 {
|
||||||
|
h := fnv.New64a()
|
||||||
|
h.Write(cert)
|
||||||
|
return h.Sum64()
|
||||||
|
}
|
128
vendor/github.com/lucas-clemente/quic-go/crypto/cert_dict.go
generated
vendored
Normal file
128
vendor/github.com/lucas-clemente/quic-go/crypto/cert_dict.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
var certDictZlib = []byte{
|
||||||
|
0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
|
||||||
|
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
|
||||||
|
0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
|
||||||
|
0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
|
||||||
|
0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
|
||||||
|
0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
|
||||||
|
0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
|
0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
|
||||||
|
0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
|
||||||
|
0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
|
||||||
|
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
|
||||||
|
0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
|
||||||
|
0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
|
||||||
|
0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
|
||||||
|
0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
|
||||||
|
0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
|
||||||
|
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
|
||||||
|
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
|
||||||
|
0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
|
||||||
|
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
|
||||||
|
0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
|
||||||
|
0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
|
||||||
|
0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
|
||||||
|
0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
|
||||||
|
0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
|
||||||
|
0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
|
||||||
|
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
|
||||||
|
0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
|
||||||
|
0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
|
||||||
|
0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
|
||||||
|
0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
|
||||||
|
0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
|
||||||
|
0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
|
||||||
|
0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
|
||||||
|
0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||||
|
0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||||
|
0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
|
||||||
|
0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
|
||||||
|
0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||||
|
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
|
||||||
|
0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
|
||||||
|
0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
|
||||||
|
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
|
||||||
|
0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
|
||||||
|
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
|
||||||
|
0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
|
||||||
|
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
|
||||||
|
0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
|
||||||
|
0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
|
||||||
|
0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
|
||||||
|
0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
|
||||||
|
0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
|
||||||
|
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
|
||||||
|
0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||||
|
0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
|
||||||
|
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
|
||||||
|
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
|
||||||
|
0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||||
|
0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
|
||||||
|
0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
||||||
|
0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
|
||||||
|
0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
||||||
|
0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
|
||||||
|
0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
|
||||||
|
0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
|
||||||
|
0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
|
||||||
|
0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
|
||||||
|
0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
|
||||||
|
0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
|
||||||
|
0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
|
||||||
|
0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
|
||||||
|
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
|
||||||
|
0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
|
||||||
|
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
|
||||||
|
0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||||
|
0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
|
||||||
|
0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
|
||||||
|
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
|
||||||
|
0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
|
||||||
|
0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
|
||||||
|
0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
|
||||||
|
0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
|
||||||
|
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
|
||||||
|
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
|
||||||
|
0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
|
||||||
|
0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
|
||||||
|
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
|
||||||
|
0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
|
||||||
|
0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
|
||||||
|
0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
|
||||||
|
0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
|
||||||
|
0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
|
||||||
|
0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
|
||||||
|
0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
|
||||||
|
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
|
||||||
|
0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
|
||||||
|
0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
|
||||||
|
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
|
||||||
|
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
|
||||||
|
0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
|
||||||
|
0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
|
||||||
|
0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
|
||||||
|
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
|
||||||
|
0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
|
||||||
|
0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
|
||||||
|
0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
|
||||||
|
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||||
|
0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
|
||||||
|
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
|
||||||
|
0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
|
||||||
|
0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
|
||||||
|
0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
|
||||||
|
0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
|
||||||
|
0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
|
||||||
|
0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
|
||||||
|
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
|
||||||
|
}
|
130
vendor/github.com/lucas-clemente/quic-go/crypto/cert_manager.go
generated
vendored
Normal file
130
vendor/github.com/lucas-clemente/quic-go/crypto/cert_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"hash/fnv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CertManager manages the certificates sent by the server
|
||||||
|
type CertManager interface {
|
||||||
|
SetData([]byte) error
|
||||||
|
GetCommonCertificateHashes() []byte
|
||||||
|
GetLeafCert() []byte
|
||||||
|
GetLeafCertHash() (uint64, error)
|
||||||
|
VerifyServerProof(proof, chlo, serverConfigData []byte) bool
|
||||||
|
Verify(hostname string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type certManager struct {
|
||||||
|
chain []*x509.Certificate
|
||||||
|
config *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CertManager = &certManager{}
|
||||||
|
|
||||||
|
var errNoCertificateChain = errors.New("CertManager BUG: No certicifate chain loaded")
|
||||||
|
|
||||||
|
// NewCertManager creates a new CertManager
|
||||||
|
func NewCertManager(tlsConfig *tls.Config) CertManager {
|
||||||
|
return &certManager{config: tlsConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetData takes the byte-slice sent in the SHLO and decompresses it into the certificate chain
|
||||||
|
func (c *certManager) SetData(data []byte) error {
|
||||||
|
byteChain, err := decompressChain(data)
|
||||||
|
if err != nil {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := make([]*x509.Certificate, len(byteChain))
|
||||||
|
for i, data := range byteChain {
|
||||||
|
cert, err := x509.ParseCertificate(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
chain[i] = cert
|
||||||
|
}
|
||||||
|
|
||||||
|
c.chain = chain
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *certManager) GetCommonCertificateHashes() []byte {
|
||||||
|
return getCommonCertificateHashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLeafCert returns the leaf certificate of the certificate chain
|
||||||
|
// it returns nil if the certificate chain has not yet been set
|
||||||
|
func (c *certManager) GetLeafCert() []byte {
|
||||||
|
if len(c.chain) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.chain[0].Raw
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLeafCertHash calculates the FNV1a_64 hash of the leaf certificate
|
||||||
|
func (c *certManager) GetLeafCertHash() (uint64, error) {
|
||||||
|
leafCert := c.GetLeafCert()
|
||||||
|
if leafCert == nil {
|
||||||
|
return 0, errNoCertificateChain
|
||||||
|
}
|
||||||
|
|
||||||
|
h := fnv.New64a()
|
||||||
|
_, err := h.Write(leafCert)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return h.Sum64(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyServerProof verifies the signature of the server config
|
||||||
|
// it should only be called after the certificate chain has been set, otherwise it returns false
|
||||||
|
func (c *certManager) VerifyServerProof(proof, chlo, serverConfigData []byte) bool {
|
||||||
|
if len(c.chain) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return verifyServerProof(proof, c.chain[0], chlo, serverConfigData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify verifies the certificate chain
|
||||||
|
func (c *certManager) Verify(hostname string) error {
|
||||||
|
if len(c.chain) == 0 {
|
||||||
|
return errNoCertificateChain
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.config != nil && c.config.InsecureSkipVerify {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
leafCert := c.chain[0]
|
||||||
|
|
||||||
|
var opts x509.VerifyOptions
|
||||||
|
if c.config != nil {
|
||||||
|
opts.Roots = c.config.RootCAs
|
||||||
|
if c.config.Time == nil {
|
||||||
|
opts.CurrentTime = time.Now()
|
||||||
|
} else {
|
||||||
|
opts.CurrentTime = c.config.Time()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we don't need to care about the tls.Config.ServerName here, since hostname has already been set to that value in the session setup
|
||||||
|
opts.DNSName = hostname
|
||||||
|
|
||||||
|
// the first certificate is the leaf certificate, all others are intermediates
|
||||||
|
if len(c.chain) > 1 {
|
||||||
|
intermediates := x509.NewCertPool()
|
||||||
|
for i := 1; i < len(c.chain); i++ {
|
||||||
|
intermediates.AddCert(c.chain[i])
|
||||||
|
}
|
||||||
|
opts.Intermediates = intermediates
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := leafCert.Verify(opts)
|
||||||
|
return err
|
||||||
|
}
|
24
vendor/github.com/lucas-clemente/quic-go/crypto/cert_sets.go
generated
vendored
Normal file
24
vendor/github.com/lucas-clemente/quic-go/crypto/cert_sets.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go-certificates"
|
||||||
|
)
|
||||||
|
|
||||||
|
type certSet [][]byte
|
||||||
|
|
||||||
|
var certSets = map[uint64]certSet{
|
||||||
|
certsets.CertSet2Hash: certsets.CertSet2,
|
||||||
|
certsets.CertSet3Hash: certsets.CertSet3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// findCertInSet searches for the cert in the set. Negative return value means not found.
|
||||||
|
func (s *certSet) findCertInSet(cert []byte) int {
|
||||||
|
for i, c := range *s {
|
||||||
|
if bytes.Equal(c, cert) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
53
vendor/github.com/lucas-clemente/quic-go/crypto/chacha20poly1305_aead.go
generated
vendored
Normal file
53
vendor/github.com/lucas-clemente/quic-go/crypto/chacha20poly1305_aead.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/aead/chacha20"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aeadChacha20Poly1305 struct {
|
||||||
|
otherIV []byte
|
||||||
|
myIV []byte
|
||||||
|
encrypter cipher.AEAD
|
||||||
|
decrypter cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAEADChacha20Poly1305 creates a AEAD using chacha20poly1305
|
||||||
|
func NewAEADChacha20Poly1305(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
|
||||||
|
if len(myKey) != 32 || len(otherKey) != 32 || len(myIV) != 4 || len(otherIV) != 4 {
|
||||||
|
return nil, errors.New("chacha20poly1305: expected 32-byte keys and 4-byte IVs")
|
||||||
|
}
|
||||||
|
// copy because ChaCha20Poly1305 expects array pointers
|
||||||
|
var MyKey, OtherKey [32]byte
|
||||||
|
copy(MyKey[:], myKey)
|
||||||
|
copy(OtherKey[:], otherKey)
|
||||||
|
|
||||||
|
encrypter, err := chacha20.NewChaCha20Poly1305WithTagSize(&MyKey, 12)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decrypter, err := chacha20.NewChaCha20Poly1305WithTagSize(&OtherKey, 12)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &aeadChacha20Poly1305{
|
||||||
|
otherIV: otherIV,
|
||||||
|
myIV: myIV,
|
||||||
|
encrypter: encrypter,
|
||||||
|
decrypter: decrypter,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead *aeadChacha20Poly1305) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||||
|
return aead.decrypter.Open(dst, makeNonce(aead.otherIV, packetNumber), src, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead *aeadChacha20Poly1305) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return aead.encrypter.Seal(dst, makeNonce(aead.myIV, packetNumber), src, associatedData)
|
||||||
|
}
|
71
vendor/github.com/lucas-clemente/quic-go/crypto/chacha20poly1305_aead_test.go
generated
vendored
Normal file
71
vendor/github.com/lucas-clemente/quic-go/crypto/chacha20poly1305_aead_test.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Chacha20poly1305", func() {
|
||||||
|
var (
|
||||||
|
alice, bob AEAD
|
||||||
|
keyAlice, keyBob, ivAlice, ivBob []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
keyAlice = make([]byte, 32)
|
||||||
|
keyBob = make([]byte, 32)
|
||||||
|
ivAlice = make([]byte, 4)
|
||||||
|
ivBob = make([]byte, 4)
|
||||||
|
rand.Reader.Read(keyAlice)
|
||||||
|
rand.Reader.Read(keyBob)
|
||||||
|
rand.Reader.Read(ivAlice)
|
||||||
|
rand.Reader.Read(ivBob)
|
||||||
|
var err error
|
||||||
|
alice, err = NewAEADChacha20Poly1305(keyBob, keyAlice, ivBob, ivAlice)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
bob, err = NewAEADChacha20Poly1305(keyAlice, keyBob, ivAlice, ivBob)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("seals and opens", func() {
|
||||||
|
b := alice.Seal(nil, []byte("foobar"), 42, []byte("aad"))
|
||||||
|
text, err := bob.Open(nil, b, 42, []byte("aad"))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(text).To(Equal([]byte("foobar")))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("seals and opens reverse", func() {
|
||||||
|
b := bob.Seal(nil, []byte("foobar"), 42, []byte("aad"))
|
||||||
|
text, err := alice.Open(nil, b, 42, []byte("aad"))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(text).To(Equal([]byte("foobar")))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("has the proper length", func() {
|
||||||
|
b := bob.Seal(nil, []byte("foobar"), 42, []byte("aad"))
|
||||||
|
Expect(b).To(HaveLen(6 + 12))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("fails with wrong aad", func() {
|
||||||
|
b := alice.Seal(nil, []byte("foobar"), 42, []byte("aad"))
|
||||||
|
_, err := bob.Open(nil, b, 42, []byte("aad2"))
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("rejects wrong key and iv sizes", func() {
|
||||||
|
var err error
|
||||||
|
e := "chacha20poly1305: expected 32-byte keys and 4-byte IVs"
|
||||||
|
_, err = NewAEADChacha20Poly1305(keyBob[1:], keyAlice, ivBob, ivAlice)
|
||||||
|
Expect(err).To(MatchError(e))
|
||||||
|
_, err = NewAEADChacha20Poly1305(keyBob, keyAlice[1:], ivBob, ivAlice)
|
||||||
|
Expect(err).To(MatchError(e))
|
||||||
|
_, err = NewAEADChacha20Poly1305(keyBob, keyAlice, ivBob[1:], ivAlice)
|
||||||
|
Expect(err).To(MatchError(e))
|
||||||
|
_, err = NewAEADChacha20Poly1305(keyBob, keyAlice, ivBob, ivAlice[1:])
|
||||||
|
Expect(err).To(MatchError(e))
|
||||||
|
})
|
||||||
|
})
|
45
vendor/github.com/lucas-clemente/quic-go/crypto/curve_25519.go
generated
vendored
Normal file
45
vendor/github.com/lucas-clemente/quic-go/crypto/curve_25519.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyExchange manages the exchange of keys
|
||||||
|
type curve25519KEX struct {
|
||||||
|
secret [32]byte
|
||||||
|
public [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ KeyExchange = &curve25519KEX{}
|
||||||
|
|
||||||
|
// NewCurve25519KEX creates a new KeyExchange using Curve25519, see https://cr.yp.to/ecdh.html
|
||||||
|
func NewCurve25519KEX() (KeyExchange, error) {
|
||||||
|
c := &curve25519KEX{}
|
||||||
|
if _, err := rand.Read(c.secret[:]); err != nil {
|
||||||
|
return nil, errors.New("Curve25519: could not create private key")
|
||||||
|
}
|
||||||
|
// See https://cr.yp.to/ecdh.html
|
||||||
|
c.secret[0] &= 248
|
||||||
|
c.secret[31] &= 127
|
||||||
|
c.secret[31] |= 64
|
||||||
|
curve25519.ScalarBaseMult(&c.public, &c.secret)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curve25519KEX) PublicKey() []byte {
|
||||||
|
return c.public[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curve25519KEX) CalculateSharedKey(otherPublic []byte) ([]byte, error) {
|
||||||
|
if len(otherPublic) != 32 {
|
||||||
|
return nil, errors.New("Curve25519: expected public key of 32 byte")
|
||||||
|
}
|
||||||
|
var res [32]byte
|
||||||
|
var otherPublicArray [32]byte
|
||||||
|
copy(otherPublicArray[:], otherPublic)
|
||||||
|
curve25519.ScalarMult(&res, &c.secret, &otherPublicArray)
|
||||||
|
return res[:], nil
|
||||||
|
}
|
101
vendor/github.com/lucas-clemente/quic-go/crypto/key_derivation.go
generated
vendored
Normal file
101
vendor/github.com/lucas-clemente/quic-go/crypto/key_derivation.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeriveKeysChacha20 derives the client and server keys and creates a matching chacha20poly1305 AEAD instance
|
||||||
|
// func DeriveKeysChacha20(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) {
|
||||||
|
// otherKey, myKey, otherIV, myIV, err := deriveKeys(version, forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 32)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return NewAEADChacha20Poly1305(otherKey, myKey, otherIV, myIV)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// DeriveKeysAESGCM derives the client and server keys and creates a matching AES-GCM AEAD instance
|
||||||
|
func DeriveKeysAESGCM(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (AEAD, error) {
|
||||||
|
var swap bool
|
||||||
|
if pers == protocol.PerspectiveClient {
|
||||||
|
swap = true
|
||||||
|
}
|
||||||
|
otherKey, myKey, otherIV, myIV, err := deriveKeys(forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 16, swap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewAEADAESGCM(otherKey, myKey, otherIV, myIV)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deriveKeys derives the keys and the IVs
|
||||||
|
// swap should be set true if generating the values for the client, and false for the server
|
||||||
|
func deriveKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo, scfg, cert, divNonce []byte, keyLen int, swap bool) ([]byte, []byte, []byte, []byte, error) {
|
||||||
|
var info bytes.Buffer
|
||||||
|
if forwardSecure {
|
||||||
|
info.Write([]byte("QUIC forward secure key expansion\x00"))
|
||||||
|
} else {
|
||||||
|
info.Write([]byte("QUIC key expansion\x00"))
|
||||||
|
}
|
||||||
|
utils.WriteUint64(&info, uint64(connID))
|
||||||
|
info.Write(chlo)
|
||||||
|
info.Write(scfg)
|
||||||
|
info.Write(cert)
|
||||||
|
|
||||||
|
r := hkdf.New(sha256.New, sharedSecret, nonces, info.Bytes())
|
||||||
|
|
||||||
|
s := make([]byte, 2*keyLen+2*4)
|
||||||
|
if _, err := io.ReadFull(r, s); err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key1 := s[:keyLen]
|
||||||
|
key2 := s[keyLen : 2*keyLen]
|
||||||
|
iv1 := s[2*keyLen : 2*keyLen+4]
|
||||||
|
iv2 := s[2*keyLen+4:]
|
||||||
|
|
||||||
|
var otherKey, myKey []byte
|
||||||
|
var otherIV, myIV []byte
|
||||||
|
|
||||||
|
if !forwardSecure {
|
||||||
|
if err := diversify(key2, iv2, divNonce); err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if swap {
|
||||||
|
otherKey = key2
|
||||||
|
myKey = key1
|
||||||
|
otherIV = iv2
|
||||||
|
myIV = iv1
|
||||||
|
} else {
|
||||||
|
otherKey = key1
|
||||||
|
myKey = key2
|
||||||
|
otherIV = iv1
|
||||||
|
myIV = iv2
|
||||||
|
}
|
||||||
|
|
||||||
|
return otherKey, myKey, otherIV, myIV, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diversify(key, iv, divNonce []byte) error {
|
||||||
|
secret := make([]byte, len(key)+len(iv))
|
||||||
|
copy(secret, key)
|
||||||
|
copy(secret[len(key):], iv)
|
||||||
|
|
||||||
|
r := hkdf.New(sha256.New, secret, divNonce, []byte("QUIC key diversification"))
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(r, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(r, iv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
7
vendor/github.com/lucas-clemente/quic-go/crypto/key_exchange.go
generated
vendored
Normal file
7
vendor/github.com/lucas-clemente/quic-go/crypto/key_exchange.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
// KeyExchange manages the exchange of keys
|
||||||
|
type KeyExchange interface {
|
||||||
|
PublicKey() []byte
|
||||||
|
CalculateSharedKey(otherPublic []byte) ([]byte, error)
|
||||||
|
}
|
14
vendor/github.com/lucas-clemente/quic-go/crypto/nonce.go
generated
vendored
Normal file
14
vendor/github.com/lucas-clemente/quic-go/crypto/nonce.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeNonce(iv []byte, packetNumber protocol.PacketNumber) []byte {
|
||||||
|
res := make([]byte, 12)
|
||||||
|
copy(res[0:4], iv)
|
||||||
|
binary.LittleEndian.PutUint64(res[4:12], uint64(packetNumber))
|
||||||
|
return res
|
||||||
|
}
|
80
vendor/github.com/lucas-clemente/quic-go/crypto/null_aead.go
generated
vendored
Normal file
80
vendor/github.com/lucas-clemente/quic-go/crypto/null_aead.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/fnv128a"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nullAEAD handles not-yet encrypted packets
|
||||||
|
type nullAEAD struct {
|
||||||
|
perspective protocol.Perspective
|
||||||
|
version protocol.VersionNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AEAD = &nullAEAD{}
|
||||||
|
|
||||||
|
// NewNullAEAD creates a NullAEAD
|
||||||
|
func NewNullAEAD(p protocol.Perspective, v protocol.VersionNumber) AEAD {
|
||||||
|
return &nullAEAD{
|
||||||
|
perspective: p,
|
||||||
|
version: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open and verify the ciphertext
|
||||||
|
func (n *nullAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||||
|
if len(src) < 12 {
|
||||||
|
return nil, errors.New("NullAEAD: ciphertext cannot be less than 12 bytes long")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := fnv128a.New()
|
||||||
|
hash.Write(associatedData)
|
||||||
|
hash.Write(src[12:])
|
||||||
|
if n.version >= protocol.Version37 {
|
||||||
|
if n.perspective == protocol.PerspectiveServer {
|
||||||
|
hash.Write([]byte("Client"))
|
||||||
|
} else {
|
||||||
|
hash.Write([]byte("Server"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testHigh, testLow := hash.Sum128()
|
||||||
|
|
||||||
|
low := binary.LittleEndian.Uint64(src)
|
||||||
|
high := binary.LittleEndian.Uint32(src[8:])
|
||||||
|
|
||||||
|
if uint32(testHigh&0xffffffff) != high || testLow != low {
|
||||||
|
return nil, errors.New("NullAEAD: failed to authenticate received data")
|
||||||
|
}
|
||||||
|
return src[12:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal writes hash and ciphertext to the buffer
|
||||||
|
func (n *nullAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
if cap(dst) < 12+len(src) {
|
||||||
|
dst = make([]byte, 12+len(src))
|
||||||
|
} else {
|
||||||
|
dst = dst[:12+len(src)]
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := fnv128a.New()
|
||||||
|
hash.Write(associatedData)
|
||||||
|
hash.Write(src)
|
||||||
|
|
||||||
|
if n.version >= protocol.Version37 {
|
||||||
|
if n.perspective == protocol.PerspectiveServer {
|
||||||
|
hash.Write([]byte("Server"))
|
||||||
|
} else {
|
||||||
|
hash.Write([]byte("Client"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
high, low := hash.Sum128()
|
||||||
|
|
||||||
|
copy(dst[12:], src)
|
||||||
|
binary.LittleEndian.PutUint64(dst, low)
|
||||||
|
binary.LittleEndian.PutUint32(dst[8:], uint32(high))
|
||||||
|
return dst
|
||||||
|
}
|
66
vendor/github.com/lucas-clemente/quic-go/crypto/server_proof.go
generated
vendored
Normal file
66
vendor/github.com/lucas-clemente/quic-go/crypto/server_proof.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ecdsaSignature struct {
|
||||||
|
R, S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// signServerProof signs CHLO and server config for use in the server proof
|
||||||
|
func signServerProof(cert *tls.Certificate, chlo []byte, serverConfigData []byte) ([]byte, error) {
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
|
||||||
|
chloHash := sha256.Sum256(chlo)
|
||||||
|
hash.Write([]byte{32, 0, 0, 0})
|
||||||
|
hash.Write(chloHash[:])
|
||||||
|
hash.Write(serverConfigData)
|
||||||
|
|
||||||
|
key, ok := cert.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("expected PrivateKey to implement crypto.Signer")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := crypto.SignerOpts(crypto.SHA256)
|
||||||
|
|
||||||
|
if _, ok = key.(*rsa.PrivateKey); ok {
|
||||||
|
opts = &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.Sign(rand.Reader, hash.Sum(nil), opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyServerProof verifies the server proof signature
|
||||||
|
func verifyServerProof(proof []byte, cert *x509.Certificate, chlo []byte, serverConfigData []byte) bool {
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
|
||||||
|
chloHash := sha256.Sum256(chlo)
|
||||||
|
hash.Write([]byte{32, 0, 0, 0})
|
||||||
|
hash.Write(chloHash[:])
|
||||||
|
hash.Write(serverConfigData)
|
||||||
|
|
||||||
|
// RSA
|
||||||
|
if cert.PublicKeyAlgorithm == x509.RSA {
|
||||||
|
opts := &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
|
||||||
|
err := rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), crypto.SHA256, hash.Sum(nil), proof, opts)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSA
|
||||||
|
signature := &ecdsaSignature{}
|
||||||
|
rest, err := asn1.Unmarshal(proof, signature)
|
||||||
|
if err != nil || len(rest) != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), hash.Sum(nil), signature.R, signature.S)
|
||||||
|
}
|
76
vendor/github.com/lucas-clemente/quic-go/crypto/source_address_token.go
generated
vendored
Normal file
76
vendor/github.com/lucas-clemente/quic-go/crypto/source_address_token.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StkSource is used to create and verify source address tokens
|
||||||
|
type StkSource interface {
|
||||||
|
// NewToken creates a new token
|
||||||
|
NewToken([]byte) ([]byte, error)
|
||||||
|
// DecodeToken decodes a token
|
||||||
|
DecodeToken([]byte) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stkSource struct {
|
||||||
|
aead cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
const stkKeySize = 16
|
||||||
|
|
||||||
|
// Chrome currently sets this to 12, but discusses changing it to 16. We start
|
||||||
|
// at 16 :)
|
||||||
|
const stkNonceSize = 16
|
||||||
|
|
||||||
|
// NewStkSource creates a source for source address tokens
|
||||||
|
func NewStkSource() (StkSource, error) {
|
||||||
|
secret := make([]byte, 32)
|
||||||
|
if _, err := rand.Read(secret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, err := deriveKey(secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aead, err := cipher.NewGCMWithNonceSize(c, stkNonceSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &stkSource{aead: aead}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stkSource) NewToken(data []byte) ([]byte, error) {
|
||||||
|
nonce := make([]byte, stkNonceSize)
|
||||||
|
if _, err := rand.Read(nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s.aead.Seal(nonce, nonce, data, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stkSource) DecodeToken(p []byte) ([]byte, error) {
|
||||||
|
if len(p) < stkNonceSize {
|
||||||
|
return nil, fmt.Errorf("STK too short: %d", len(p))
|
||||||
|
}
|
||||||
|
nonce := p[:stkNonceSize]
|
||||||
|
return s.aead.Open(nil, nonce, p[stkNonceSize:], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deriveKey(secret []byte) ([]byte, error) {
|
||||||
|
r := hkdf.New(sha256.New, secret, nil, []byte("QUIC source address token key"))
|
||||||
|
key := make([]byte, stkKeySize)
|
||||||
|
if _, err := io.ReadFull(r, key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
240
vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_control_manager.go
generated
vendored
Normal file
240
vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_control_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
package flowcontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/congestion"
|
||||||
|
"github.com/lucas-clemente/quic-go/handshake"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flowControlManager struct {
|
||||||
|
connectionParameters handshake.ConnectionParametersManager
|
||||||
|
rttStats *congestion.RTTStats
|
||||||
|
|
||||||
|
streamFlowController map[protocol.StreamID]*flowController
|
||||||
|
connFlowController *flowController
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ FlowControlManager = &flowControlManager{}
|
||||||
|
|
||||||
|
var errMapAccess = errors.New("Error accessing the flowController map.")
|
||||||
|
|
||||||
|
// NewFlowControlManager creates a new flow control manager
|
||||||
|
func NewFlowControlManager(connectionParameters handshake.ConnectionParametersManager, rttStats *congestion.RTTStats) FlowControlManager {
|
||||||
|
return &flowControlManager{
|
||||||
|
connectionParameters: connectionParameters,
|
||||||
|
rttStats: rttStats,
|
||||||
|
streamFlowController: make(map[protocol.StreamID]*flowController),
|
||||||
|
connFlowController: newFlowController(0, false, connectionParameters, rttStats),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStream creates new flow controllers for a stream
|
||||||
|
// it does nothing if the stream already exists
|
||||||
|
func (f *flowControlManager) NewStream(streamID protocol.StreamID, contributesToConnection bool) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
if _, ok := f.streamFlowController[streamID]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.streamFlowController[streamID] = newFlowController(streamID, contributesToConnection, f.connectionParameters, f.rttStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveStream removes a closed stream from flow control
|
||||||
|
func (f *flowControlManager) RemoveStream(streamID protocol.StreamID) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
delete(f.streamFlowController, streamID)
|
||||||
|
f.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetStream should be called when receiving a RstStreamFrame
|
||||||
|
// it updates the byte offset to the value in the RstStreamFrame
|
||||||
|
// streamID must not be 0 here
|
||||||
|
func (f *flowControlManager) ResetStream(streamID protocol.StreamID, byteOffset protocol.ByteCount) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
streamFlowController, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
increment, err := streamFlowController.UpdateHighestReceived(byteOffset)
|
||||||
|
if err != nil {
|
||||||
|
return qerr.StreamDataAfterTermination
|
||||||
|
}
|
||||||
|
|
||||||
|
if streamFlowController.CheckFlowControlViolation() {
|
||||||
|
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", byteOffset, streamID, streamFlowController.receiveWindow))
|
||||||
|
}
|
||||||
|
|
||||||
|
if streamFlowController.ContributesToConnection() {
|
||||||
|
f.connFlowController.IncrementHighestReceived(increment)
|
||||||
|
if f.connFlowController.CheckFlowControlViolation() {
|
||||||
|
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", f.connFlowController.highestReceived, f.connFlowController.receiveWindow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateHighestReceived updates the highest received byte offset for a stream
|
||||||
|
// it adds the number of additional bytes to connection level flow control
|
||||||
|
// streamID must not be 0 here
|
||||||
|
func (f *flowControlManager) UpdateHighestReceived(streamID protocol.StreamID, byteOffset protocol.ByteCount) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
streamFlowController, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// UpdateHighestReceived returns an ErrReceivedSmallerByteOffset when StreamFrames got reordered
|
||||||
|
// this error can be ignored here
|
||||||
|
increment, _ := streamFlowController.UpdateHighestReceived(byteOffset)
|
||||||
|
|
||||||
|
if streamFlowController.CheckFlowControlViolation() {
|
||||||
|
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", byteOffset, streamID, streamFlowController.receiveWindow))
|
||||||
|
}
|
||||||
|
|
||||||
|
if streamFlowController.ContributesToConnection() {
|
||||||
|
f.connFlowController.IncrementHighestReceived(increment)
|
||||||
|
if f.connFlowController.CheckFlowControlViolation() {
|
||||||
|
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", f.connFlowController.highestReceived, f.connFlowController.receiveWindow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// streamID must not be 0 here
|
||||||
|
func (f *flowControlManager) AddBytesRead(streamID protocol.StreamID, n protocol.ByteCount) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
fc, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fc.AddBytesRead(n)
|
||||||
|
if fc.ContributesToConnection() {
|
||||||
|
f.connFlowController.AddBytesRead(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flowControlManager) GetWindowUpdates() (res []WindowUpdate) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
// get WindowUpdates for streams
|
||||||
|
for id, fc := range f.streamFlowController {
|
||||||
|
if necessary, newIncrement, offset := fc.MaybeUpdateWindow(); necessary {
|
||||||
|
res = append(res, WindowUpdate{StreamID: id, Offset: offset})
|
||||||
|
if fc.ContributesToConnection() && newIncrement != 0 {
|
||||||
|
f.connFlowController.EnsureMinimumWindowIncrement(protocol.ByteCount(float64(newIncrement) * protocol.ConnectionFlowControlMultiplier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// get a WindowUpdate for the connection
|
||||||
|
if necessary, _, offset := f.connFlowController.MaybeUpdateWindow(); necessary {
|
||||||
|
res = append(res, WindowUpdate{StreamID: 0, Offset: offset})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flowControlManager) GetReceiveWindow(streamID protocol.StreamID) (protocol.ByteCount, error) {
|
||||||
|
f.mutex.RLock()
|
||||||
|
defer f.mutex.RUnlock()
|
||||||
|
|
||||||
|
// StreamID can be 0 when retransmitting
|
||||||
|
if streamID == 0 {
|
||||||
|
return f.connFlowController.receiveWindow, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
flowController, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return flowController.receiveWindow, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// streamID must not be 0 here
|
||||||
|
func (f *flowControlManager) AddBytesSent(streamID protocol.StreamID, n protocol.ByteCount) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
fc, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fc.AddBytesSent(n)
|
||||||
|
if fc.ContributesToConnection() {
|
||||||
|
f.connFlowController.AddBytesSent(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// must not be called with StreamID 0
|
||||||
|
func (f *flowControlManager) SendWindowSize(streamID protocol.StreamID) (protocol.ByteCount, error) {
|
||||||
|
f.mutex.RLock()
|
||||||
|
defer f.mutex.RUnlock()
|
||||||
|
|
||||||
|
fc, err := f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
res := fc.SendWindowSize()
|
||||||
|
|
||||||
|
if fc.ContributesToConnection() {
|
||||||
|
res = utils.MinByteCount(res, f.connFlowController.SendWindowSize())
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flowControlManager) RemainingConnectionWindowSize() protocol.ByteCount {
|
||||||
|
f.mutex.RLock()
|
||||||
|
defer f.mutex.RUnlock()
|
||||||
|
|
||||||
|
return f.connFlowController.SendWindowSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// streamID may be 0 here
|
||||||
|
func (f *flowControlManager) UpdateWindow(streamID protocol.StreamID, offset protocol.ByteCount) (bool, error) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
var fc *flowController
|
||||||
|
if streamID == 0 {
|
||||||
|
fc = f.connFlowController
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
fc, err = f.getFlowController(streamID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fc.UpdateSendWindow(offset), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flowControlManager) getFlowController(streamID protocol.StreamID) (*flowController, error) {
|
||||||
|
streamFlowController, ok := f.streamFlowController[streamID]
|
||||||
|
if !ok {
|
||||||
|
return nil, errMapAccess
|
||||||
|
}
|
||||||
|
return streamFlowController, nil
|
||||||
|
}
|
198
vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_controller.go
generated
vendored
Normal file
198
vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_controller.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
package flowcontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/congestion"
|
||||||
|
"github.com/lucas-clemente/quic-go/handshake"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flowController struct {
|
||||||
|
streamID protocol.StreamID
|
||||||
|
contributesToConnection bool // does the stream contribute to connection level flow control
|
||||||
|
|
||||||
|
connectionParameters handshake.ConnectionParametersManager
|
||||||
|
rttStats *congestion.RTTStats
|
||||||
|
|
||||||
|
bytesSent protocol.ByteCount
|
||||||
|
sendWindow protocol.ByteCount
|
||||||
|
|
||||||
|
lastWindowUpdateTime time.Time
|
||||||
|
|
||||||
|
bytesRead protocol.ByteCount
|
||||||
|
highestReceived protocol.ByteCount
|
||||||
|
receiveWindow protocol.ByteCount
|
||||||
|
receiveWindowIncrement protocol.ByteCount
|
||||||
|
maxReceiveWindowIncrement protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrReceivedSmallerByteOffset occurs if the ByteOffset received is smaller than a ByteOffset that was set previously
|
||||||
|
var ErrReceivedSmallerByteOffset = errors.New("Received a smaller byte offset")
|
||||||
|
|
||||||
|
// newFlowController gets a new flow controller
|
||||||
|
func newFlowController(streamID protocol.StreamID, contributesToConnection bool, connectionParameters handshake.ConnectionParametersManager, rttStats *congestion.RTTStats) *flowController {
|
||||||
|
fc := flowController{
|
||||||
|
streamID: streamID,
|
||||||
|
contributesToConnection: contributesToConnection,
|
||||||
|
connectionParameters: connectionParameters,
|
||||||
|
rttStats: rttStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
if streamID == 0 {
|
||||||
|
fc.receiveWindow = connectionParameters.GetReceiveConnectionFlowControlWindow()
|
||||||
|
fc.receiveWindowIncrement = fc.receiveWindow
|
||||||
|
fc.maxReceiveWindowIncrement = connectionParameters.GetMaxReceiveConnectionFlowControlWindow()
|
||||||
|
} else {
|
||||||
|
fc.receiveWindow = connectionParameters.GetReceiveStreamFlowControlWindow()
|
||||||
|
fc.receiveWindowIncrement = fc.receiveWindow
|
||||||
|
fc.maxReceiveWindowIncrement = connectionParameters.GetMaxReceiveStreamFlowControlWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) ContributesToConnection() bool {
|
||||||
|
return c.contributesToConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) getSendWindow() protocol.ByteCount {
|
||||||
|
if c.sendWindow == 0 {
|
||||||
|
if c.streamID == 0 {
|
||||||
|
return c.connectionParameters.GetSendConnectionFlowControlWindow()
|
||||||
|
}
|
||||||
|
return c.connectionParameters.GetSendStreamFlowControlWindow()
|
||||||
|
}
|
||||||
|
return c.sendWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) AddBytesSent(n protocol.ByteCount) {
|
||||||
|
c.bytesSent += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSendWindow should be called after receiving a WindowUpdateFrame
|
||||||
|
// it returns true if the window was actually updated
|
||||||
|
func (c *flowController) UpdateSendWindow(newOffset protocol.ByteCount) bool {
|
||||||
|
if newOffset > c.sendWindow {
|
||||||
|
c.sendWindow = newOffset
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) SendWindowSize() protocol.ByteCount {
|
||||||
|
sendWindow := c.getSendWindow()
|
||||||
|
|
||||||
|
if c.bytesSent > sendWindow { // should never happen, but make sure we don't do an underflow here
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return sendWindow - c.bytesSent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) SendWindowOffset() protocol.ByteCount {
|
||||||
|
return c.getSendWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateHighestReceived updates the highestReceived value, if the byteOffset is higher
|
||||||
|
// Should **only** be used for the stream-level FlowController
|
||||||
|
// it returns an ErrReceivedSmallerByteOffset if the received byteOffset is smaller than any byteOffset received before
|
||||||
|
// This error occurs every time StreamFrames get reordered and has to be ignored in that case
|
||||||
|
// It should only be treated as an error when resetting a stream
|
||||||
|
func (c *flowController) UpdateHighestReceived(byteOffset protocol.ByteCount) (protocol.ByteCount, error) {
|
||||||
|
if byteOffset == c.highestReceived {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if byteOffset > c.highestReceived {
|
||||||
|
increment := byteOffset - c.highestReceived
|
||||||
|
c.highestReceived = byteOffset
|
||||||
|
return increment, nil
|
||||||
|
}
|
||||||
|
return 0, ErrReceivedSmallerByteOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementHighestReceived adds an increment to the highestReceived value
|
||||||
|
// Should **only** be used for the connection-level FlowController
|
||||||
|
func (c *flowController) IncrementHighestReceived(increment protocol.ByteCount) {
|
||||||
|
c.highestReceived += increment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) AddBytesRead(n protocol.ByteCount) {
|
||||||
|
// pretend we sent a WindowUpdate when reading the first byte
|
||||||
|
// this way auto-tuning of the window increment already works for the first WindowUpdate
|
||||||
|
if c.bytesRead == 0 {
|
||||||
|
c.lastWindowUpdateTime = time.Now()
|
||||||
|
}
|
||||||
|
c.bytesRead += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeUpdateWindow updates the receive window, if necessary
|
||||||
|
// if the receive window increment is changed, the new value is returned, otherwise a 0
|
||||||
|
// the last return value is the new offset of the receive window
|
||||||
|
func (c *flowController) MaybeUpdateWindow() (bool, protocol.ByteCount /* new increment */, protocol.ByteCount /* new offset */) {
|
||||||
|
diff := c.receiveWindow - c.bytesRead
|
||||||
|
|
||||||
|
// Chromium implements the same threshold
|
||||||
|
if diff < (c.receiveWindowIncrement / 2) {
|
||||||
|
var newWindowIncrement protocol.ByteCount
|
||||||
|
oldWindowIncrement := c.receiveWindowIncrement
|
||||||
|
|
||||||
|
c.maybeAdjustWindowIncrement()
|
||||||
|
if c.receiveWindowIncrement != oldWindowIncrement {
|
||||||
|
newWindowIncrement = c.receiveWindowIncrement
|
||||||
|
}
|
||||||
|
|
||||||
|
c.lastWindowUpdateTime = time.Now()
|
||||||
|
c.receiveWindow = c.bytesRead + c.receiveWindowIncrement
|
||||||
|
return true, newWindowIncrement, c.receiveWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeAdjustWindowIncrement increases the receiveWindowIncrement if we're sending WindowUpdates too often
|
||||||
|
func (c *flowController) maybeAdjustWindowIncrement() {
|
||||||
|
if c.lastWindowUpdateTime.IsZero() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rtt := c.rttStats.SmoothedRTT()
|
||||||
|
if rtt == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeSinceLastWindowUpdate := time.Since(c.lastWindowUpdateTime)
|
||||||
|
|
||||||
|
// interval between the window updates is sufficiently large, no need to increase the increment
|
||||||
|
if timeSinceLastWindowUpdate >= 2*rtt {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oldWindowSize := c.receiveWindowIncrement
|
||||||
|
c.receiveWindowIncrement = utils.MinByteCount(2*c.receiveWindowIncrement, c.maxReceiveWindowIncrement)
|
||||||
|
|
||||||
|
// debug log, if the window size was actually increased
|
||||||
|
if oldWindowSize < c.receiveWindowIncrement {
|
||||||
|
newWindowSize := c.receiveWindowIncrement / (1 << 10)
|
||||||
|
if c.streamID == 0 {
|
||||||
|
utils.Debugf("Increasing receive flow control window for the connection to %d kB", newWindowSize)
|
||||||
|
} else {
|
||||||
|
utils.Debugf("Increasing receive flow control window increment for stream %d to %d kB", c.streamID, newWindowSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureMinimumWindowIncrement sets a minimum window increment
|
||||||
|
// it is intended be used for the connection-level flow controller
|
||||||
|
// it should make sure that the connection-level window is increased when a stream-level window grows
|
||||||
|
func (c *flowController) EnsureMinimumWindowIncrement(inc protocol.ByteCount) {
|
||||||
|
if inc > c.receiveWindowIncrement {
|
||||||
|
c.receiveWindowIncrement = utils.MinByteCount(inc, c.maxReceiveWindowIncrement)
|
||||||
|
c.lastWindowUpdateTime = time.Time{} // disables autotuning for the next window update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *flowController) CheckFlowControlViolation() bool {
|
||||||
|
return c.highestReceived > c.receiveWindow
|
||||||
|
}
|
26
vendor/github.com/lucas-clemente/quic-go/flowcontrol/interface.go
generated
vendored
Normal file
26
vendor/github.com/lucas-clemente/quic-go/flowcontrol/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package flowcontrol
|
||||||
|
|
||||||
|
import "github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
|
||||||
|
// WindowUpdate provides the data for WindowUpdateFrames.
|
||||||
|
type WindowUpdate struct {
|
||||||
|
StreamID protocol.StreamID
|
||||||
|
Offset protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FlowControlManager manages the flow control
|
||||||
|
type FlowControlManager interface {
|
||||||
|
NewStream(streamID protocol.StreamID, contributesToConnectionFlow bool)
|
||||||
|
RemoveStream(streamID protocol.StreamID)
|
||||||
|
// methods needed for receiving data
|
||||||
|
ResetStream(streamID protocol.StreamID, byteOffset protocol.ByteCount) error
|
||||||
|
UpdateHighestReceived(streamID protocol.StreamID, byteOffset protocol.ByteCount) error
|
||||||
|
AddBytesRead(streamID protocol.StreamID, n protocol.ByteCount) error
|
||||||
|
GetWindowUpdates() []WindowUpdate
|
||||||
|
GetReceiveWindow(streamID protocol.StreamID) (protocol.ByteCount, error)
|
||||||
|
// methods needed for sending data
|
||||||
|
AddBytesSent(streamID protocol.StreamID, n protocol.ByteCount) error
|
||||||
|
SendWindowSize(streamID protocol.StreamID) (protocol.ByteCount, error)
|
||||||
|
RemainingConnectionWindowSize() protocol.ByteCount
|
||||||
|
UpdateWindow(streamID protocol.StreamID, offset protocol.ByteCount) (bool, error)
|
||||||
|
}
|
468
vendor/github.com/lucas-clemente/quic-go/frames/ack_frame.go
generated
vendored
Normal file
468
vendor/github.com/lucas-clemente/quic-go/frames/ack_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,468 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidAckRanges occurs when a client sends inconsistent ACK ranges
|
||||||
|
ErrInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges")
|
||||||
|
// ErrInvalidFirstAckRange occurs when the first ACK range contains no packets
|
||||||
|
ErrInvalidFirstAckRange = errors.New("AckFrame: ACK frame has invalid first ACK range")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInconsistentAckLargestAcked = errors.New("internal inconsistency: LargestAcked does not match ACK ranges")
|
||||||
|
errInconsistentAckLowestAcked = errors.New("internal inconsistency: LowestAcked does not match ACK ranges")
|
||||||
|
)
|
||||||
|
|
||||||
|
// An AckFrame is an ACK frame in QUIC
|
||||||
|
type AckFrame struct {
|
||||||
|
LargestAcked protocol.PacketNumber
|
||||||
|
LowestAcked protocol.PacketNumber
|
||||||
|
AckRanges []AckRange // has to be ordered. The ACK range with the highest FirstPacketNumber goes first, the ACK range with the lowest FirstPacketNumber goes last
|
||||||
|
|
||||||
|
// time when the LargestAcked was receiveid
|
||||||
|
// this field Will not be set for received ACKs frames
|
||||||
|
PacketReceivedTime time.Time
|
||||||
|
DelayTime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAckFrame reads an ACK frame
|
||||||
|
func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
|
||||||
|
frame := &AckFrame{}
|
||||||
|
|
||||||
|
typeByte, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMissingRanges := false
|
||||||
|
if typeByte&0x20 == 0x20 {
|
||||||
|
hasMissingRanges = true
|
||||||
|
}
|
||||||
|
|
||||||
|
largestAckedLen := 2 * ((typeByte & 0x0C) >> 2)
|
||||||
|
if largestAckedLen == 0 {
|
||||||
|
largestAckedLen = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
missingSequenceNumberDeltaLen := 2 * (typeByte & 0x03)
|
||||||
|
if missingSequenceNumberDeltaLen == 0 {
|
||||||
|
missingSequenceNumberDeltaLen = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
largestAcked, err := utils.ReadUintN(r, largestAckedLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.LargestAcked = protocol.PacketNumber(largestAcked)
|
||||||
|
|
||||||
|
delay, err := utils.ReadUfloat16(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.DelayTime = time.Duration(delay) * time.Microsecond
|
||||||
|
|
||||||
|
var numAckBlocks uint8
|
||||||
|
if hasMissingRanges {
|
||||||
|
numAckBlocks, err = r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMissingRanges && numAckBlocks == 0 {
|
||||||
|
return nil, ErrInvalidAckRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
ackBlockLength, err := utils.ReadUintN(r, missingSequenceNumberDeltaLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if frame.LargestAcked > 0 && ackBlockLength < 1 {
|
||||||
|
return nil, ErrInvalidFirstAckRange
|
||||||
|
}
|
||||||
|
|
||||||
|
if ackBlockLength > largestAcked {
|
||||||
|
return nil, ErrInvalidAckRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMissingRanges {
|
||||||
|
ackRange := AckRange{
|
||||||
|
FirstPacketNumber: protocol.PacketNumber(largestAcked-ackBlockLength) + 1,
|
||||||
|
LastPacketNumber: frame.LargestAcked,
|
||||||
|
}
|
||||||
|
frame.AckRanges = append(frame.AckRanges, ackRange)
|
||||||
|
|
||||||
|
var inLongBlock bool
|
||||||
|
var lastRangeComplete bool
|
||||||
|
for i := uint8(0); i < numAckBlocks; i++ {
|
||||||
|
var gap uint8
|
||||||
|
gap, err = r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ackBlockLength, err = utils.ReadUintN(r, missingSequenceNumberDeltaLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
length := protocol.PacketNumber(ackBlockLength)
|
||||||
|
|
||||||
|
if inLongBlock {
|
||||||
|
frame.AckRanges[len(frame.AckRanges)-1].FirstPacketNumber -= protocol.PacketNumber(gap) + length
|
||||||
|
frame.AckRanges[len(frame.AckRanges)-1].LastPacketNumber -= protocol.PacketNumber(gap)
|
||||||
|
} else {
|
||||||
|
lastRangeComplete = false
|
||||||
|
ackRange := AckRange{
|
||||||
|
LastPacketNumber: frame.AckRanges[len(frame.AckRanges)-1].FirstPacketNumber - protocol.PacketNumber(gap) - 1,
|
||||||
|
}
|
||||||
|
ackRange.FirstPacketNumber = ackRange.LastPacketNumber - length + 1
|
||||||
|
frame.AckRanges = append(frame.AckRanges, ackRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
lastRangeComplete = true
|
||||||
|
}
|
||||||
|
|
||||||
|
inLongBlock = (ackBlockLength == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the last range was not complete, FirstPacketNumber and LastPacketNumber make no sense
|
||||||
|
// remove the range from frame.AckRanges
|
||||||
|
if !lastRangeComplete {
|
||||||
|
frame.AckRanges = frame.AckRanges[:len(frame.AckRanges)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.LowestAcked = frame.AckRanges[len(frame.AckRanges)-1].FirstPacketNumber
|
||||||
|
} else {
|
||||||
|
if frame.LargestAcked == 0 {
|
||||||
|
frame.LowestAcked = 0
|
||||||
|
} else {
|
||||||
|
frame.LowestAcked = protocol.PacketNumber(largestAcked + 1 - ackBlockLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !frame.validateAckRanges() {
|
||||||
|
return nil, ErrInvalidAckRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
var numTimestamp byte
|
||||||
|
numTimestamp, err = r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if numTimestamp > 0 {
|
||||||
|
// Delta Largest acked
|
||||||
|
_, err = r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// First Timestamp
|
||||||
|
_, err = utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < int(numTimestamp)-1; i++ {
|
||||||
|
// Delta Largest acked
|
||||||
|
_, err = r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time Since Previous Timestamp
|
||||||
|
_, err = utils.ReadUint16(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes an ACK frame.
|
||||||
|
func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
largestAckedLen := protocol.GetPacketNumberLength(f.LargestAcked)
|
||||||
|
|
||||||
|
typeByte := uint8(0x40)
|
||||||
|
|
||||||
|
if largestAckedLen != protocol.PacketNumberLen1 {
|
||||||
|
typeByte ^= (uint8(largestAckedLen / 2)) << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
missingSequenceNumberDeltaLen := f.getMissingSequenceNumberDeltaLen()
|
||||||
|
if missingSequenceNumberDeltaLen != protocol.PacketNumberLen1 {
|
||||||
|
typeByte ^= (uint8(missingSequenceNumberDeltaLen / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.HasMissingRanges() {
|
||||||
|
typeByte |= 0x20
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
switch largestAckedLen {
|
||||||
|
case protocol.PacketNumberLen1:
|
||||||
|
b.WriteByte(uint8(f.LargestAcked))
|
||||||
|
case protocol.PacketNumberLen2:
|
||||||
|
utils.WriteUint16(b, uint16(f.LargestAcked))
|
||||||
|
case protocol.PacketNumberLen4:
|
||||||
|
utils.WriteUint32(b, uint32(f.LargestAcked))
|
||||||
|
case protocol.PacketNumberLen6:
|
||||||
|
utils.WriteUint48(b, uint64(f.LargestAcked))
|
||||||
|
}
|
||||||
|
|
||||||
|
f.DelayTime = time.Since(f.PacketReceivedTime)
|
||||||
|
utils.WriteUfloat16(b, uint64(f.DelayTime/time.Microsecond))
|
||||||
|
|
||||||
|
var numRanges uint64
|
||||||
|
var numRangesWritten uint64
|
||||||
|
if f.HasMissingRanges() {
|
||||||
|
numRanges = f.numWritableNackRanges()
|
||||||
|
if numRanges > 0xFF {
|
||||||
|
panic("AckFrame: Too many ACK ranges")
|
||||||
|
}
|
||||||
|
b.WriteByte(uint8(numRanges - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstAckBlockLength protocol.PacketNumber
|
||||||
|
if !f.HasMissingRanges() {
|
||||||
|
firstAckBlockLength = f.LargestAcked - f.LowestAcked + 1
|
||||||
|
} else {
|
||||||
|
if f.LargestAcked != f.AckRanges[0].LastPacketNumber {
|
||||||
|
return errInconsistentAckLargestAcked
|
||||||
|
}
|
||||||
|
if f.LowestAcked != f.AckRanges[len(f.AckRanges)-1].FirstPacketNumber {
|
||||||
|
return errInconsistentAckLowestAcked
|
||||||
|
}
|
||||||
|
firstAckBlockLength = f.LargestAcked - f.AckRanges[0].FirstPacketNumber + 1
|
||||||
|
numRangesWritten++
|
||||||
|
}
|
||||||
|
|
||||||
|
switch missingSequenceNumberDeltaLen {
|
||||||
|
case protocol.PacketNumberLen1:
|
||||||
|
b.WriteByte(uint8(firstAckBlockLength))
|
||||||
|
case protocol.PacketNumberLen2:
|
||||||
|
utils.WriteUint16(b, uint16(firstAckBlockLength))
|
||||||
|
case protocol.PacketNumberLen4:
|
||||||
|
utils.WriteUint32(b, uint32(firstAckBlockLength))
|
||||||
|
case protocol.PacketNumberLen6:
|
||||||
|
utils.WriteUint48(b, uint64(firstAckBlockLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, ackRange := range f.AckRanges {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
length := ackRange.LastPacketNumber - ackRange.FirstPacketNumber + 1
|
||||||
|
gap := f.AckRanges[i-1].FirstPacketNumber - ackRange.LastPacketNumber - 1
|
||||||
|
|
||||||
|
num := gap/0xFF + 1
|
||||||
|
if gap%0xFF == 0 {
|
||||||
|
num--
|
||||||
|
}
|
||||||
|
|
||||||
|
if num == 1 {
|
||||||
|
b.WriteByte(uint8(gap))
|
||||||
|
switch missingSequenceNumberDeltaLen {
|
||||||
|
case protocol.PacketNumberLen1:
|
||||||
|
b.WriteByte(uint8(length))
|
||||||
|
case protocol.PacketNumberLen2:
|
||||||
|
utils.WriteUint16(b, uint16(length))
|
||||||
|
case protocol.PacketNumberLen4:
|
||||||
|
utils.WriteUint32(b, uint32(length))
|
||||||
|
case protocol.PacketNumberLen6:
|
||||||
|
utils.WriteUint48(b, uint64(length))
|
||||||
|
}
|
||||||
|
numRangesWritten++
|
||||||
|
} else {
|
||||||
|
for i := 0; i < int(num); i++ {
|
||||||
|
var lengthWritten uint64
|
||||||
|
var gapWritten uint8
|
||||||
|
|
||||||
|
if i == int(num)-1 { // last block
|
||||||
|
lengthWritten = uint64(length)
|
||||||
|
gapWritten = uint8(1 + ((gap - 1) % 255))
|
||||||
|
} else {
|
||||||
|
lengthWritten = 0
|
||||||
|
gapWritten = 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte(gapWritten)
|
||||||
|
switch missingSequenceNumberDeltaLen {
|
||||||
|
case protocol.PacketNumberLen1:
|
||||||
|
b.WriteByte(uint8(lengthWritten))
|
||||||
|
case protocol.PacketNumberLen2:
|
||||||
|
utils.WriteUint16(b, uint16(lengthWritten))
|
||||||
|
case protocol.PacketNumberLen4:
|
||||||
|
utils.WriteUint32(b, uint32(lengthWritten))
|
||||||
|
case protocol.PacketNumberLen6:
|
||||||
|
utils.WriteUint48(b, lengthWritten)
|
||||||
|
}
|
||||||
|
|
||||||
|
numRangesWritten++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is needed if not all AckRanges can be written to the ACK frame (if there are more than 0xFF)
|
||||||
|
if numRangesWritten >= numRanges {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numRanges != numRangesWritten {
|
||||||
|
return errors.New("BUG: Inconsistent number of ACK ranges written")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte(0) // no timestamps
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *AckFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
length := protocol.ByteCount(1 + 2 + 1) // 1 TypeByte, 2 ACK delay time, 1 Num Timestamp
|
||||||
|
length += protocol.ByteCount(protocol.GetPacketNumberLength(f.LargestAcked))
|
||||||
|
|
||||||
|
missingSequenceNumberDeltaLen := protocol.ByteCount(f.getMissingSequenceNumberDeltaLen())
|
||||||
|
|
||||||
|
if f.HasMissingRanges() {
|
||||||
|
length += (1 + missingSequenceNumberDeltaLen) * protocol.ByteCount(f.numWritableNackRanges())
|
||||||
|
} else {
|
||||||
|
length += missingSequenceNumberDeltaLen
|
||||||
|
}
|
||||||
|
|
||||||
|
length += (1 + 2) * 0 /* TODO: num_timestamps */
|
||||||
|
|
||||||
|
return length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasMissingRanges returns if this frame reports any missing packets
|
||||||
|
func (f *AckFrame) HasMissingRanges() bool {
|
||||||
|
return len(f.AckRanges) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *AckFrame) validateAckRanges() bool {
|
||||||
|
if len(f.AckRanges) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are missing packets, there will always be at least 2 ACK ranges
|
||||||
|
if len(f.AckRanges) == 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.AckRanges[0].LastPacketNumber != f.LargestAcked {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the validity of every single ACK range
|
||||||
|
for _, ackRange := range f.AckRanges {
|
||||||
|
if ackRange.FirstPacketNumber > ackRange.LastPacketNumber {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the consistency for ACK with multiple NACK ranges
|
||||||
|
for i, ackRange := range f.AckRanges {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lastAckRange := f.AckRanges[i-1]
|
||||||
|
if lastAckRange.FirstPacketNumber <= ackRange.FirstPacketNumber {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lastAckRange.FirstPacketNumber <= ackRange.LastPacketNumber+1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// numWritableNackRanges calculates the number of ACK blocks that are about to be written
|
||||||
|
// this number is different from len(f.AckRanges) for the case of long gaps (> 255 packets)
|
||||||
|
func (f *AckFrame) numWritableNackRanges() uint64 {
|
||||||
|
if len(f.AckRanges) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var numRanges uint64
|
||||||
|
for i, ackRange := range f.AckRanges {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastAckRange := f.AckRanges[i-1]
|
||||||
|
gap := lastAckRange.FirstPacketNumber - ackRange.LastPacketNumber - 1
|
||||||
|
rangeLength := 1 + uint64(gap)/0xFF
|
||||||
|
if uint64(gap)%0xFF == 0 {
|
||||||
|
rangeLength--
|
||||||
|
}
|
||||||
|
|
||||||
|
if numRanges+rangeLength < 0xFF {
|
||||||
|
numRanges += rangeLength
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numRanges + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *AckFrame) getMissingSequenceNumberDeltaLen() protocol.PacketNumberLen {
|
||||||
|
var maxRangeLength protocol.PacketNumber
|
||||||
|
|
||||||
|
if f.HasMissingRanges() {
|
||||||
|
for _, ackRange := range f.AckRanges {
|
||||||
|
rangeLength := ackRange.LastPacketNumber - ackRange.FirstPacketNumber + 1
|
||||||
|
if rangeLength > maxRangeLength {
|
||||||
|
maxRangeLength = rangeLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxRangeLength = f.LargestAcked - f.LowestAcked + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxRangeLength <= 0xFF {
|
||||||
|
return protocol.PacketNumberLen1
|
||||||
|
}
|
||||||
|
if maxRangeLength <= 0xFFFF {
|
||||||
|
return protocol.PacketNumberLen2
|
||||||
|
}
|
||||||
|
if maxRangeLength <= 0xFFFFFFFF {
|
||||||
|
return protocol.PacketNumberLen4
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol.PacketNumberLen6
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcksPacket determines if this ACK frame acks a certain packet number
|
||||||
|
func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool {
|
||||||
|
if p < f.LowestAcked || p > f.LargestAcked { // this is just a performance optimization
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.HasMissingRanges() {
|
||||||
|
// TODO: this could be implemented as a binary search
|
||||||
|
for _, ackRange := range f.AckRanges {
|
||||||
|
if p >= ackRange.FirstPacketNumber && p <= ackRange.LastPacketNumber {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// if packet doesn't have missing ranges
|
||||||
|
return (p >= f.LowestAcked && p <= f.LargestAcked)
|
||||||
|
}
|
9
vendor/github.com/lucas-clemente/quic-go/frames/ack_range.go
generated
vendored
Normal file
9
vendor/github.com/lucas-clemente/quic-go/frames/ack_range.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import "github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
|
||||||
|
// AckRange is an ACK range
|
||||||
|
type AckRange struct {
|
||||||
|
FirstPacketNumber protocol.PacketNumber
|
||||||
|
LastPacketNumber protocol.PacketNumber
|
||||||
|
}
|
44
vendor/github.com/lucas-clemente/quic-go/frames/blocked_frame.go
generated
vendored
Normal file
44
vendor/github.com/lucas-clemente/quic-go/frames/blocked_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A BlockedFrame in QUIC
|
||||||
|
type BlockedFrame struct {
|
||||||
|
StreamID protocol.StreamID
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write writes a BlockedFrame frame
|
||||||
|
func (f *BlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
b.WriteByte(0x05)
|
||||||
|
utils.WriteUint32(b, uint32(f.StreamID))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *BlockedFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return 1 + 4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBlockedFrame parses a BLOCKED frame
|
||||||
|
func ParseBlockedFrame(r *bytes.Reader) (*BlockedFrame, error) {
|
||||||
|
frame := &BlockedFrame{}
|
||||||
|
|
||||||
|
// read the TypeByte
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sid, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.StreamID = protocol.StreamID(sid)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
73
vendor/github.com/lucas-clemente/quic-go/frames/connection_close_frame.go
generated
vendored
Normal file
73
vendor/github.com/lucas-clemente/quic-go/frames/connection_close_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ConnectionCloseFrame in QUIC
|
||||||
|
type ConnectionCloseFrame struct {
|
||||||
|
ErrorCode qerr.ErrorCode
|
||||||
|
ReasonPhrase string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseConnectionCloseFrame reads a CONNECTION_CLOSE frame
|
||||||
|
func ParseConnectionCloseFrame(r *bytes.Reader) (*ConnectionCloseFrame, error) {
|
||||||
|
frame := &ConnectionCloseFrame{}
|
||||||
|
|
||||||
|
// read the TypeByte
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCode, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ErrorCode = qerr.ErrorCode(errorCode)
|
||||||
|
|
||||||
|
reasonPhraseLen, err := utils.ReadUint16(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reasonPhraseLen > uint16(protocol.MaxPacketSize) {
|
||||||
|
return nil, qerr.Error(qerr.InvalidConnectionCloseData, "reason phrase too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
reasonPhrase := make([]byte, reasonPhraseLen)
|
||||||
|
if _, err := io.ReadFull(r, reasonPhrase); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ReasonPhrase = string(reasonPhrase)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *ConnectionCloseFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return 1 + 4 + 2 + protocol.ByteCount(len(f.ReasonPhrase)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes an CONNECTION_CLOSE frame.
|
||||||
|
func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
b.WriteByte(0x02)
|
||||||
|
utils.WriteUint32(b, uint32(f.ErrorCode))
|
||||||
|
|
||||||
|
if len(f.ReasonPhrase) > math.MaxUint16 {
|
||||||
|
return errors.New("ConnectionFrame: ReasonPhrase too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
reasonPhraseLen := uint16(len(f.ReasonPhrase))
|
||||||
|
utils.WriteUint16(b, reasonPhraseLen)
|
||||||
|
b.WriteString(f.ReasonPhrase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
13
vendor/github.com/lucas-clemente/quic-go/frames/frame.go
generated
vendored
Normal file
13
vendor/github.com/lucas-clemente/quic-go/frames/frame.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Frame in QUIC
|
||||||
|
type Frame interface {
|
||||||
|
Write(b *bytes.Buffer, version protocol.VersionNumber) error
|
||||||
|
MinLength(version protocol.VersionNumber) (protocol.ByteCount, error)
|
||||||
|
}
|
73
vendor/github.com/lucas-clemente/quic-go/frames/goaway_frame.go
generated
vendored
Normal file
73
vendor/github.com/lucas-clemente/quic-go/frames/goaway_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A GoawayFrame is a GOAWAY frame
|
||||||
|
type GoawayFrame struct {
|
||||||
|
ErrorCode qerr.ErrorCode
|
||||||
|
LastGoodStream protocol.StreamID
|
||||||
|
ReasonPhrase string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGoawayFrame parses a GOAWAY frame
|
||||||
|
func ParseGoawayFrame(r *bytes.Reader) (*GoawayFrame, error) {
|
||||||
|
frame := &GoawayFrame{}
|
||||||
|
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCode, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ErrorCode = qerr.ErrorCode(errorCode)
|
||||||
|
|
||||||
|
lastGoodStream, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.LastGoodStream = protocol.StreamID(lastGoodStream)
|
||||||
|
|
||||||
|
reasonPhraseLen, err := utils.ReadUint16(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reasonPhraseLen > uint16(protocol.MaxPacketSize) {
|
||||||
|
return nil, qerr.Error(qerr.InvalidGoawayData, "reason phrase too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
reasonPhrase := make([]byte, reasonPhraseLen)
|
||||||
|
if _, err := io.ReadFull(r, reasonPhrase); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ReasonPhrase = string(reasonPhrase)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *GoawayFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
typeByte := uint8(0x03)
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
utils.WriteUint32(b, uint32(f.ErrorCode))
|
||||||
|
utils.WriteUint32(b, uint32(f.LastGoodStream))
|
||||||
|
utils.WriteUint16(b, uint16(len(f.ReasonPhrase)))
|
||||||
|
b.WriteString(f.ReasonPhrase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *GoawayFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return protocol.ByteCount(1 + 4 + 4 + 2 + len(f.ReasonPhrase)), nil
|
||||||
|
}
|
28
vendor/github.com/lucas-clemente/quic-go/frames/log.go
generated
vendored
Normal file
28
vendor/github.com/lucas-clemente/quic-go/frames/log.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import "github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
|
||||||
|
// LogFrame logs a frame, either sent or received
|
||||||
|
func LogFrame(frame Frame, sent bool) {
|
||||||
|
if !utils.Debug() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dir := "<-"
|
||||||
|
if sent {
|
||||||
|
dir = "->"
|
||||||
|
}
|
||||||
|
switch f := frame.(type) {
|
||||||
|
case *StreamFrame:
|
||||||
|
utils.Debugf("\t%s &frames.StreamFrame{StreamID: %d, FinBit: %t, Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, f.StreamID, f.FinBit, f.Offset, f.DataLen(), f.Offset+f.DataLen())
|
||||||
|
case *StopWaitingFrame:
|
||||||
|
if sent {
|
||||||
|
utils.Debugf("\t%s &frames.StopWaitingFrame{LeastUnacked: 0x%x, PacketNumberLen: 0x%x}", dir, f.LeastUnacked, f.PacketNumberLen)
|
||||||
|
} else {
|
||||||
|
utils.Debugf("\t%s &frames.StopWaitingFrame{LeastUnacked: 0x%x}", dir, f.LeastUnacked)
|
||||||
|
}
|
||||||
|
case *AckFrame:
|
||||||
|
utils.Debugf("\t%s &frames.AckFrame{LargestAcked: 0x%x, LowestAcked: 0x%x, AckRanges: %#v, DelayTime: %s}", dir, f.LargestAcked, f.LowestAcked, f.AckRanges, f.DelayTime.String())
|
||||||
|
default:
|
||||||
|
utils.Debugf("\t%s %#v", dir, frame)
|
||||||
|
}
|
||||||
|
}
|
33
vendor/github.com/lucas-clemente/quic-go/frames/ping_frame.go
generated
vendored
Normal file
33
vendor/github.com/lucas-clemente/quic-go/frames/ping_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A PingFrame is a ping frame
|
||||||
|
type PingFrame struct{}
|
||||||
|
|
||||||
|
// ParsePingFrame parses a Ping frame
|
||||||
|
func ParsePingFrame(r *bytes.Reader) (*PingFrame, error) {
|
||||||
|
frame := &PingFrame{}
|
||||||
|
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PingFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
typeByte := uint8(0x07)
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *PingFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return 1, nil
|
||||||
|
}
|
59
vendor/github.com/lucas-clemente/quic-go/frames/rst_stream_frame.go
generated
vendored
Normal file
59
vendor/github.com/lucas-clemente/quic-go/frames/rst_stream_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A RstStreamFrame in QUIC
|
||||||
|
type RstStreamFrame struct {
|
||||||
|
StreamID protocol.StreamID
|
||||||
|
ErrorCode uint32
|
||||||
|
ByteOffset protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write writes a RST_STREAM frame
|
||||||
|
func (f *RstStreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
b.WriteByte(0x01)
|
||||||
|
utils.WriteUint32(b, uint32(f.StreamID))
|
||||||
|
utils.WriteUint64(b, uint64(f.ByteOffset))
|
||||||
|
utils.WriteUint32(b, f.ErrorCode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *RstStreamFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return 1 + 4 + 8 + 4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRstStreamFrame parses a RST_STREAM frame
|
||||||
|
func ParseRstStreamFrame(r *bytes.Reader) (*RstStreamFrame, error) {
|
||||||
|
frame := &RstStreamFrame{}
|
||||||
|
|
||||||
|
// read the TypeByte
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sid, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.StreamID = protocol.StreamID(sid)
|
||||||
|
|
||||||
|
byteOffset, err := utils.ReadUint64(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ByteOffset = protocol.ByteCount(byteOffset)
|
||||||
|
|
||||||
|
frame.ErrorCode, err = utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
91
vendor/github.com/lucas-clemente/quic-go/frames/stop_waiting_frame.go
generated
vendored
Normal file
91
vendor/github.com/lucas-clemente/quic-go/frames/stop_waiting_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A StopWaitingFrame in QUIC
|
||||||
|
type StopWaitingFrame struct {
|
||||||
|
LeastUnacked protocol.PacketNumber
|
||||||
|
PacketNumberLen protocol.PacketNumberLen
|
||||||
|
PacketNumber protocol.PacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errLeastUnackedHigherThanPacketNumber = errors.New("StopWaitingFrame: LeastUnacked can't be greater than the packet number")
|
||||||
|
errPacketNumberNotSet = errors.New("StopWaitingFrame: PacketNumber not set")
|
||||||
|
errPacketNumberLenNotSet = errors.New("StopWaitingFrame: PacketNumberLen not set")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *StopWaitingFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
// packetNumber is the packet number of the packet that this StopWaitingFrame will be sent with
|
||||||
|
typeByte := uint8(0x06)
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
// make sure the PacketNumber was set
|
||||||
|
if f.PacketNumber == protocol.PacketNumber(0) {
|
||||||
|
return errPacketNumberNotSet
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.LeastUnacked > f.PacketNumber {
|
||||||
|
return errLeastUnackedHigherThanPacketNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
leastUnackedDelta := uint64(f.PacketNumber - f.LeastUnacked)
|
||||||
|
|
||||||
|
switch f.PacketNumberLen {
|
||||||
|
case protocol.PacketNumberLen1:
|
||||||
|
b.WriteByte(uint8(leastUnackedDelta))
|
||||||
|
case protocol.PacketNumberLen2:
|
||||||
|
utils.WriteUint16(b, uint16(leastUnackedDelta))
|
||||||
|
case protocol.PacketNumberLen4:
|
||||||
|
utils.WriteUint32(b, uint32(leastUnackedDelta))
|
||||||
|
case protocol.PacketNumberLen6:
|
||||||
|
utils.WriteUint48(b, leastUnackedDelta)
|
||||||
|
default:
|
||||||
|
return errPacketNumberLenNotSet
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *StopWaitingFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
minLength := protocol.ByteCount(1) // typeByte
|
||||||
|
|
||||||
|
if f.PacketNumberLen == protocol.PacketNumberLenInvalid {
|
||||||
|
return 0, errPacketNumberLenNotSet
|
||||||
|
}
|
||||||
|
minLength += protocol.ByteCount(f.PacketNumberLen)
|
||||||
|
|
||||||
|
return minLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseStopWaitingFrame parses a StopWaiting frame
|
||||||
|
func ParseStopWaitingFrame(r *bytes.Reader, packetNumber protocol.PacketNumber, packetNumberLen protocol.PacketNumberLen, version protocol.VersionNumber) (*StopWaitingFrame, error) {
|
||||||
|
frame := &StopWaitingFrame{}
|
||||||
|
|
||||||
|
// read the TypeByte
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
leastUnackedDelta, err := utils.ReadUintN(r, uint8(packetNumberLen))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if leastUnackedDelta > uint64(packetNumber) {
|
||||||
|
return nil, qerr.Error(qerr.InvalidStopWaitingData, "invalid LeastUnackedDelta")
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.LeastUnacked = protocol.PacketNumber(uint64(packetNumber) - leastUnackedDelta)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
212
vendor/github.com/lucas-clemente/quic-go/frames/stream_frame.go
generated
vendored
Normal file
212
vendor/github.com/lucas-clemente/quic-go/frames/stream_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A StreamFrame of QUIC
|
||||||
|
type StreamFrame struct {
|
||||||
|
StreamID protocol.StreamID
|
||||||
|
FinBit bool
|
||||||
|
DataLenPresent bool
|
||||||
|
Offset protocol.ByteCount
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidStreamIDLen = errors.New("StreamFrame: Invalid StreamID length")
|
||||||
|
errInvalidOffsetLen = errors.New("StreamFrame: Invalid offset length")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseStreamFrame reads a stream frame. The type byte must not have been read yet.
|
||||||
|
func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
|
||||||
|
frame := &StreamFrame{}
|
||||||
|
|
||||||
|
typeByte, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.FinBit = typeByte&0x40 > 0
|
||||||
|
frame.DataLenPresent = typeByte&0x20 > 0
|
||||||
|
offsetLen := typeByte & 0x1C >> 2
|
||||||
|
if offsetLen != 0 {
|
||||||
|
offsetLen++
|
||||||
|
}
|
||||||
|
streamIDLen := typeByte&0x03 + 1
|
||||||
|
|
||||||
|
sid, err := utils.ReadUintN(r, streamIDLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.StreamID = protocol.StreamID(sid)
|
||||||
|
|
||||||
|
offset, err := utils.ReadUintN(r, offsetLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.Offset = protocol.ByteCount(offset)
|
||||||
|
|
||||||
|
var dataLen uint16
|
||||||
|
if frame.DataLenPresent {
|
||||||
|
dataLen, err = utils.ReadUint16(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataLen > uint16(protocol.MaxPacketSize) {
|
||||||
|
return nil, qerr.Error(qerr.InvalidStreamData, "data len too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !frame.DataLenPresent {
|
||||||
|
// The rest of the packet is data
|
||||||
|
dataLen = uint16(r.Len())
|
||||||
|
}
|
||||||
|
if dataLen != 0 {
|
||||||
|
frame.Data = make([]byte, dataLen)
|
||||||
|
n, err := r.Read(frame.Data)
|
||||||
|
if n != int(dataLen) {
|
||||||
|
return nil, errors.New("BUG: StreamFrame could not read dataLen bytes")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if frame.Offset+frame.DataLen() < frame.Offset {
|
||||||
|
return nil, qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !frame.FinBit && frame.DataLen() == 0 {
|
||||||
|
return nil, qerr.EmptyStreamFrameNoFin
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteStreamFrame writes a stream frame.
|
||||||
|
func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
if len(f.Data) == 0 && !f.FinBit {
|
||||||
|
return errors.New("StreamFrame: attempting to write empty frame without FIN")
|
||||||
|
}
|
||||||
|
|
||||||
|
typeByte := uint8(0x80) // sets the leftmost bit to 1
|
||||||
|
|
||||||
|
if f.FinBit {
|
||||||
|
typeByte ^= 0x40
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DataLenPresent {
|
||||||
|
typeByte ^= 0x20
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetLength := f.getOffsetLength()
|
||||||
|
|
||||||
|
if offsetLength > 0 {
|
||||||
|
typeByte ^= (uint8(offsetLength) - 1) << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
streamIDLen := f.calculateStreamIDLength()
|
||||||
|
typeByte ^= streamIDLen - 1
|
||||||
|
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
switch streamIDLen {
|
||||||
|
case 1:
|
||||||
|
b.WriteByte(uint8(f.StreamID))
|
||||||
|
case 2:
|
||||||
|
utils.WriteUint16(b, uint16(f.StreamID))
|
||||||
|
case 3:
|
||||||
|
utils.WriteUint24(b, uint32(f.StreamID))
|
||||||
|
case 4:
|
||||||
|
utils.WriteUint32(b, uint32(f.StreamID))
|
||||||
|
default:
|
||||||
|
return errInvalidStreamIDLen
|
||||||
|
}
|
||||||
|
|
||||||
|
switch offsetLength {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
utils.WriteUint16(b, uint16(f.Offset))
|
||||||
|
case 3:
|
||||||
|
utils.WriteUint24(b, uint32(f.Offset))
|
||||||
|
case 4:
|
||||||
|
utils.WriteUint32(b, uint32(f.Offset))
|
||||||
|
case 5:
|
||||||
|
utils.WriteUint40(b, uint64(f.Offset))
|
||||||
|
case 6:
|
||||||
|
utils.WriteUint48(b, uint64(f.Offset))
|
||||||
|
case 7:
|
||||||
|
utils.WriteUint56(b, uint64(f.Offset))
|
||||||
|
case 8:
|
||||||
|
utils.WriteUint64(b, uint64(f.Offset))
|
||||||
|
default:
|
||||||
|
return errInvalidOffsetLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DataLenPresent {
|
||||||
|
utils.WriteUint16(b, uint16(len(f.Data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(f.Data)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StreamFrame) calculateStreamIDLength() uint8 {
|
||||||
|
if f.StreamID < (1 << 8) {
|
||||||
|
return 1
|
||||||
|
} else if f.StreamID < (1 << 16) {
|
||||||
|
return 2
|
||||||
|
} else if f.StreamID < (1 << 24) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StreamFrame) getOffsetLength() protocol.ByteCount {
|
||||||
|
if f.Offset == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 16) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 24) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 32) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 40) {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 48) {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 56) {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength returns the length of the header of a StreamFrame
|
||||||
|
// the total length of the StreamFrame is frame.MinLength() + frame.DataLen()
|
||||||
|
func (f *StreamFrame) MinLength(protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
length := protocol.ByteCount(1) + protocol.ByteCount(f.calculateStreamIDLength()) + f.getOffsetLength()
|
||||||
|
if f.DataLenPresent {
|
||||||
|
length += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataLen gives the length of data in bytes
|
||||||
|
func (f *StreamFrame) DataLen() protocol.ByteCount {
|
||||||
|
return protocol.ByteCount(len(f.Data))
|
||||||
|
}
|
54
vendor/github.com/lucas-clemente/quic-go/frames/window_update_frame.go
generated
vendored
Normal file
54
vendor/github.com/lucas-clemente/quic-go/frames/window_update_frame.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package frames
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A WindowUpdateFrame in QUIC
|
||||||
|
type WindowUpdateFrame struct {
|
||||||
|
StreamID protocol.StreamID
|
||||||
|
ByteOffset protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write writes a RST_STREAM frame
|
||||||
|
func (f *WindowUpdateFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
typeByte := uint8(0x04)
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
utils.WriteUint32(b, uint32(f.StreamID))
|
||||||
|
utils.WriteUint64(b, uint64(f.ByteOffset))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLength of a written frame
|
||||||
|
func (f *WindowUpdateFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
return 1 + 4 + 8, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseWindowUpdateFrame parses a RST_STREAM frame
|
||||||
|
func ParseWindowUpdateFrame(r *bytes.Reader) (*WindowUpdateFrame, error) {
|
||||||
|
frame := &WindowUpdateFrame{}
|
||||||
|
|
||||||
|
// read the TypeByte
|
||||||
|
_, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sid, err := utils.ReadUint32(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.StreamID = protocol.StreamID(sid)
|
||||||
|
|
||||||
|
byteOffset, err := utils.ReadUint64(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.ByteOffset = protocol.ByteCount(byteOffset)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
296
vendor/github.com/lucas-clemente/quic-go/h2quic/client.go
generated
vendored
Normal file
296
vendor/github.com/lucas-clemente/quic-go/h2quic/client.go
generated
vendored
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type roundTripperOpts struct {
|
||||||
|
DisableCompression bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialAddr = quic.DialAddr
|
||||||
|
|
||||||
|
// client is a HTTP2 client doing QUIC requests
|
||||||
|
type client struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
tlsConf *tls.Config
|
||||||
|
config *quic.Config
|
||||||
|
opts *roundTripperOpts
|
||||||
|
|
||||||
|
hostname string
|
||||||
|
encryptionLevel protocol.EncryptionLevel
|
||||||
|
handshakeErr error
|
||||||
|
dialOnce sync.Once
|
||||||
|
|
||||||
|
session quic.Session
|
||||||
|
headerStream quic.Stream
|
||||||
|
headerErr *qerr.QuicError
|
||||||
|
headerErrored chan struct{} // this channel is closed if an error occurs on the header stream
|
||||||
|
requestWriter *requestWriter
|
||||||
|
|
||||||
|
responses map[protocol.StreamID]chan *http.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ http.RoundTripper = &client{}
|
||||||
|
|
||||||
|
var defaultQuicConfig = &quic.Config{
|
||||||
|
RequestConnectionIDTruncation: true,
|
||||||
|
KeepAlive: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// newClient creates a new client
|
||||||
|
func newClient(
|
||||||
|
hostname string,
|
||||||
|
tlsConfig *tls.Config,
|
||||||
|
opts *roundTripperOpts,
|
||||||
|
quicConfig *quic.Config,
|
||||||
|
) *client {
|
||||||
|
config := defaultQuicConfig
|
||||||
|
if quicConfig != nil {
|
||||||
|
config = quicConfig
|
||||||
|
}
|
||||||
|
return &client{
|
||||||
|
hostname: authorityAddr("https", hostname),
|
||||||
|
responses: make(map[protocol.StreamID]chan *http.Response),
|
||||||
|
encryptionLevel: protocol.EncryptionUnencrypted,
|
||||||
|
tlsConf: tlsConfig,
|
||||||
|
config: config,
|
||||||
|
opts: opts,
|
||||||
|
headerErrored: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dial dials the connection
|
||||||
|
func (c *client) dial() error {
|
||||||
|
var err error
|
||||||
|
c.session, err = dialAddr(c.hostname, c.tlsConf, c.config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// once the version has been negotiated, open the header stream
|
||||||
|
c.headerStream, err = c.session.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.headerStream.StreamID() != 3 {
|
||||||
|
return errors.New("h2quic Client BUG: StreamID of Header Stream is not 3")
|
||||||
|
}
|
||||||
|
c.requestWriter = newRequestWriter(c.headerStream)
|
||||||
|
go c.handleHeaderStream()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) handleHeaderStream() {
|
||||||
|
decoder := hpack.NewDecoder(4096, func(hf hpack.HeaderField) {})
|
||||||
|
h2framer := http2.NewFramer(nil, c.headerStream)
|
||||||
|
|
||||||
|
var lastStream protocol.StreamID
|
||||||
|
|
||||||
|
for {
|
||||||
|
frame, err := h2framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
c.headerErr = qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastStream = protocol.StreamID(frame.Header().StreamID)
|
||||||
|
hframe, ok := frame.(*http2.HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
c.headerErr = qerr.Error(qerr.InvalidHeadersStreamData, "not a headers frame")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
mhframe := &http2.MetaHeadersFrame{HeadersFrame: hframe}
|
||||||
|
mhframe.Fields, err = decoder.DecodeFull(hframe.HeaderBlockFragment())
|
||||||
|
if err != nil {
|
||||||
|
c.headerErr = qerr.Error(qerr.InvalidHeadersStreamData, "cannot read header fields")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mutex.RLock()
|
||||||
|
responseChan, ok := c.responses[protocol.StreamID(hframe.StreamID)]
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
c.headerErr = qerr.Error(qerr.InternalError, fmt.Sprintf("h2client BUG: response channel for stream %d not found", lastStream))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp, err := responseFromHeaders(mhframe)
|
||||||
|
if err != nil {
|
||||||
|
c.headerErr = qerr.Error(qerr.InternalError, err.Error())
|
||||||
|
}
|
||||||
|
responseChan <- rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop all running request
|
||||||
|
utils.Debugf("Error handling header stream %d: %s", lastStream, c.headerErr.Error())
|
||||||
|
close(c.headerErrored)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roundtrip executes a request and returns a response
|
||||||
|
func (c *client) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
// TODO: add port to address, if it doesn't have one
|
||||||
|
if req.URL.Scheme != "https" {
|
||||||
|
return nil, errors.New("quic http2: unsupported scheme")
|
||||||
|
}
|
||||||
|
if authorityAddr("https", hostnameFromRequest(req)) != c.hostname {
|
||||||
|
return nil, fmt.Errorf("h2quic Client BUG: RoundTrip called for the wrong client (expected %s, got %s)", c.hostname, req.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.dialOnce.Do(func() {
|
||||||
|
c.handshakeErr = c.dial()
|
||||||
|
})
|
||||||
|
|
||||||
|
if c.handshakeErr != nil {
|
||||||
|
return nil, c.handshakeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
hasBody := (req.Body != nil)
|
||||||
|
|
||||||
|
responseChan := make(chan *http.Response)
|
||||||
|
dataStream, err := c.session.OpenStreamSync()
|
||||||
|
if err != nil {
|
||||||
|
_ = c.CloseWithError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.responses[dataStream.StreamID()] = responseChan
|
||||||
|
c.mutex.Unlock()
|
||||||
|
|
||||||
|
var requestedGzip bool
|
||||||
|
if !c.opts.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" && req.Method != "HEAD" {
|
||||||
|
requestedGzip = true
|
||||||
|
}
|
||||||
|
// TODO: add support for trailers
|
||||||
|
endStream := !hasBody
|
||||||
|
err = c.requestWriter.WriteRequest(req, dataStream.StreamID(), endStream, requestedGzip)
|
||||||
|
if err != nil {
|
||||||
|
_ = c.CloseWithError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resc := make(chan error, 1)
|
||||||
|
if hasBody {
|
||||||
|
go func() {
|
||||||
|
resc <- c.writeRequestBody(dataStream, req.Body)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *http.Response
|
||||||
|
|
||||||
|
var receivedResponse bool
|
||||||
|
var bodySent bool
|
||||||
|
|
||||||
|
if !hasBody {
|
||||||
|
bodySent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for !(bodySent && receivedResponse) {
|
||||||
|
select {
|
||||||
|
case res = <-responseChan:
|
||||||
|
receivedResponse = true
|
||||||
|
c.mutex.Lock()
|
||||||
|
delete(c.responses, dataStream.StreamID())
|
||||||
|
c.mutex.Unlock()
|
||||||
|
case err := <-resc:
|
||||||
|
bodySent = true
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case <-c.headerErrored:
|
||||||
|
// an error occured on the header stream
|
||||||
|
_ = c.CloseWithError(c.headerErr)
|
||||||
|
return nil, c.headerErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: correctly set this variable
|
||||||
|
var streamEnded bool
|
||||||
|
isHead := (req.Method == "HEAD")
|
||||||
|
|
||||||
|
res = setLength(res, isHead, streamEnded)
|
||||||
|
|
||||||
|
if streamEnded || isHead {
|
||||||
|
res.Body = noBody
|
||||||
|
} else {
|
||||||
|
res.Body = dataStream
|
||||||
|
if requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
|
||||||
|
res.Header.Del("Content-Encoding")
|
||||||
|
res.Header.Del("Content-Length")
|
||||||
|
res.ContentLength = -1
|
||||||
|
res.Body = &gzipReader{body: res.Body}
|
||||||
|
res.Uncompressed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Request = req
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) writeRequestBody(dataStream quic.Stream, body io.ReadCloser) (err error) {
|
||||||
|
defer func() {
|
||||||
|
cerr := body.Close()
|
||||||
|
if err == nil {
|
||||||
|
// TODO: what to do with dataStream here? Maybe reset it?
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(dataStream, body)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: what to do with dataStream here? Maybe reset it?
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dataStream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the client
|
||||||
|
func (c *client) CloseWithError(e error) error {
|
||||||
|
if c.session == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.session.Close(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Close() error {
|
||||||
|
return c.CloseWithError(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from net/transport.go
|
||||||
|
|
||||||
|
// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
|
||||||
|
// and returns a host:port. The port 443 is added if needed.
|
||||||
|
func authorityAddr(scheme string, authority string) (addr string) {
|
||||||
|
host, port, err := net.SplitHostPort(authority)
|
||||||
|
if err != nil { // authority didn't have a port
|
||||||
|
port = "443"
|
||||||
|
if scheme == "http" {
|
||||||
|
port = "80"
|
||||||
|
}
|
||||||
|
host = authority
|
||||||
|
}
|
||||||
|
if a, err := idna.ToASCII(host); err == nil {
|
||||||
|
host = a
|
||||||
|
}
|
||||||
|
// IPv6 address literal, without a port:
|
||||||
|
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||||
|
return host + ":" + port
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(host, port)
|
||||||
|
}
|
35
vendor/github.com/lucas-clemente/quic-go/h2quic/gzipreader.go
generated
vendored
Normal file
35
vendor/github.com/lucas-clemente/quic-go/h2quic/gzipreader.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
// copied from net/transport.go
|
||||||
|
|
||||||
|
// gzipReader wraps a response body so it can lazily
|
||||||
|
// call gzip.NewReader on the first call to Read
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// call gzip.NewReader on the first call to Read
|
||||||
|
type gzipReader struct {
|
||||||
|
body io.ReadCloser // underlying Response.Body
|
||||||
|
zr *gzip.Reader // lazily-initialized gzip reader
|
||||||
|
zerr error // sticky error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gz *gzipReader) Read(p []byte) (n int, err error) {
|
||||||
|
if gz.zerr != nil {
|
||||||
|
return 0, gz.zerr
|
||||||
|
}
|
||||||
|
if gz.zr == nil {
|
||||||
|
gz.zr, err = gzip.NewReader(gz.body)
|
||||||
|
if err != nil {
|
||||||
|
gz.zerr = err
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gz.zr.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gz *gzipReader) Close() error {
|
||||||
|
return gz.body.Close()
|
||||||
|
}
|
80
vendor/github.com/lucas-clemente/quic-go/h2quic/request.go
generated
vendored
Normal file
80
vendor/github.com/lucas-clemente/quic-go/h2quic/request.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requestFromHeaders(headers []hpack.HeaderField) (*http.Request, error) {
|
||||||
|
var path, authority, method, contentLengthStr string
|
||||||
|
httpHeaders := http.Header{}
|
||||||
|
|
||||||
|
for _, h := range headers {
|
||||||
|
switch h.Name {
|
||||||
|
case ":path":
|
||||||
|
path = h.Value
|
||||||
|
case ":method":
|
||||||
|
method = h.Value
|
||||||
|
case ":authority":
|
||||||
|
authority = h.Value
|
||||||
|
case "content-length":
|
||||||
|
contentLengthStr = h.Value
|
||||||
|
default:
|
||||||
|
if !h.IsPseudo() {
|
||||||
|
httpHeaders.Add(h.Name, h.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
|
||||||
|
if len(httpHeaders["Cookie"]) > 0 {
|
||||||
|
httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(path) == 0 || len(authority) == 0 || len(method) == 0 {
|
||||||
|
return nil, errors.New(":path, :authority and :method must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentLength int64
|
||||||
|
if len(contentLengthStr) > 0 {
|
||||||
|
contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Request{
|
||||||
|
Method: method,
|
||||||
|
URL: u,
|
||||||
|
Proto: "HTTP/2.0",
|
||||||
|
ProtoMajor: 2,
|
||||||
|
ProtoMinor: 0,
|
||||||
|
Header: httpHeaders,
|
||||||
|
Body: nil,
|
||||||
|
ContentLength: contentLength,
|
||||||
|
Host: authority,
|
||||||
|
RequestURI: path,
|
||||||
|
TLS: &tls.ConnectionState{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostnameFromRequest(req *http.Request) string {
|
||||||
|
if len(req.Host) > 0 {
|
||||||
|
return req.Host
|
||||||
|
}
|
||||||
|
if req.URL != nil {
|
||||||
|
return req.URL.Host
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
29
vendor/github.com/lucas-clemente/quic-go/h2quic/request_body.go
generated
vendored
Normal file
29
vendor/github.com/lucas-clemente/quic-go/h2quic/request_body.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type requestBody struct {
|
||||||
|
requestRead bool
|
||||||
|
dataStream quic.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the requestBody can be used as a http.Request.Body
|
||||||
|
var _ io.ReadCloser = &requestBody{}
|
||||||
|
|
||||||
|
func newRequestBody(stream quic.Stream) *requestBody {
|
||||||
|
return &requestBody{dataStream: stream}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *requestBody) Read(p []byte) (int, error) {
|
||||||
|
b.requestRead = true
|
||||||
|
return b.dataStream.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *requestBody) Close() error {
|
||||||
|
// stream's Close() closes the write side, not the read side
|
||||||
|
return nil
|
||||||
|
}
|
201
vendor/github.com/lucas-clemente/quic-go/h2quic/request_writer.go
generated
vendored
Normal file
201
vendor/github.com/lucas-clemente/quic-go/h2quic/request_writer.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
"golang.org/x/net/lex/httplex"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type requestWriter struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
headerStream quic.Stream
|
||||||
|
|
||||||
|
henc *hpack.Encoder
|
||||||
|
hbuf bytes.Buffer // HPACK encoder writes into this
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultUserAgent = "quic-go"
|
||||||
|
|
||||||
|
func newRequestWriter(headerStream quic.Stream) *requestWriter {
|
||||||
|
rw := &requestWriter{
|
||||||
|
headerStream: headerStream,
|
||||||
|
}
|
||||||
|
rw.henc = hpack.NewEncoder(&rw.hbuf)
|
||||||
|
return rw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *requestWriter) WriteRequest(req *http.Request, dataStreamID protocol.StreamID, endStream, requestGzip bool) error {
|
||||||
|
// TODO: add support for trailers
|
||||||
|
// TODO: add support for gzip compression
|
||||||
|
// TODO: write continuation frames, if the header frame is too long
|
||||||
|
|
||||||
|
w.mutex.Lock()
|
||||||
|
defer w.mutex.Unlock()
|
||||||
|
|
||||||
|
w.encodeHeaders(req, requestGzip, "", actualContentLength(req))
|
||||||
|
h2framer := http2.NewFramer(w.headerStream, nil)
|
||||||
|
return h2framer.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: uint32(dataStreamID),
|
||||||
|
EndHeaders: true,
|
||||||
|
EndStream: endStream,
|
||||||
|
BlockFragment: w.hbuf.Bytes(),
|
||||||
|
Priority: http2.PriorityParam{Weight: 0xff},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// the rest of this files is copied from http2.Transport
|
||||||
|
func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) {
|
||||||
|
w.hbuf.Reset()
|
||||||
|
|
||||||
|
host := req.Host
|
||||||
|
if host == "" {
|
||||||
|
host = req.URL.Host
|
||||||
|
}
|
||||||
|
host, err := httplex.PunycodeHostPort(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var path string
|
||||||
|
if req.Method != "CONNECT" {
|
||||||
|
path = req.URL.RequestURI()
|
||||||
|
if !validPseudoPath(path) {
|
||||||
|
orig := path
|
||||||
|
path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host)
|
||||||
|
if !validPseudoPath(path) {
|
||||||
|
if req.URL.Opaque != "" {
|
||||||
|
return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid request :path %q", orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for any invalid headers and return an error before we
|
||||||
|
// potentially pollute our hpack state. (We want to be able to
|
||||||
|
// continue to reuse the hpack encoder for future requests)
|
||||||
|
for k, vv := range req.Header {
|
||||||
|
if !httplex.ValidHeaderFieldName(k) {
|
||||||
|
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
if !httplex.ValidHeaderFieldValue(v) {
|
||||||
|
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.1.2.3 Request Pseudo-Header Fields
|
||||||
|
// The :path pseudo-header field includes the path and query parts of the
|
||||||
|
// target URI (the path-absolute production and optionally a '?' character
|
||||||
|
// followed by the query production (see Sections 3.3 and 3.4 of
|
||||||
|
// [RFC3986]).
|
||||||
|
w.writeHeader(":authority", host)
|
||||||
|
w.writeHeader(":method", req.Method)
|
||||||
|
if req.Method != "CONNECT" {
|
||||||
|
w.writeHeader(":path", path)
|
||||||
|
w.writeHeader(":scheme", req.URL.Scheme)
|
||||||
|
}
|
||||||
|
if trailers != "" {
|
||||||
|
w.writeHeader("trailer", trailers)
|
||||||
|
}
|
||||||
|
|
||||||
|
var didUA bool
|
||||||
|
for k, vv := range req.Header {
|
||||||
|
lowKey := strings.ToLower(k)
|
||||||
|
switch lowKey {
|
||||||
|
case "host", "content-length":
|
||||||
|
// Host is :authority, already sent.
|
||||||
|
// Content-Length is automatic, set below.
|
||||||
|
continue
|
||||||
|
case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive":
|
||||||
|
// Per 8.1.2.2 Connection-Specific Header
|
||||||
|
// Fields, don't send connection-specific
|
||||||
|
// fields. We have already checked if any
|
||||||
|
// are error-worthy so just ignore the rest.
|
||||||
|
continue
|
||||||
|
case "user-agent":
|
||||||
|
// Match Go's http1 behavior: at most one
|
||||||
|
// User-Agent. If set to nil or empty string,
|
||||||
|
// then omit it. Otherwise if not mentioned,
|
||||||
|
// include the default (below).
|
||||||
|
didUA = true
|
||||||
|
if len(vv) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vv = vv[:1]
|
||||||
|
if vv[0] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
w.writeHeader(lowKey, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if shouldSendReqContentLength(req.Method, contentLength) {
|
||||||
|
w.writeHeader("content-length", strconv.FormatInt(contentLength, 10))
|
||||||
|
}
|
||||||
|
if addGzipHeader {
|
||||||
|
w.writeHeader("accept-encoding", "gzip")
|
||||||
|
}
|
||||||
|
if !didUA {
|
||||||
|
w.writeHeader("user-agent", defaultUserAgent)
|
||||||
|
}
|
||||||
|
return w.hbuf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *requestWriter) writeHeader(name, value string) {
|
||||||
|
utils.Debugf("http2: Transport encoding header %q = %q", name, value)
|
||||||
|
w.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldSendReqContentLength reports whether the http2.Transport should send
|
||||||
|
// a "content-length" request header. This logic is basically a copy of the net/http
|
||||||
|
// transferWriter.shouldSendContentLength.
|
||||||
|
// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown).
|
||||||
|
// -1 means unknown.
|
||||||
|
func shouldSendReqContentLength(method string, contentLength int64) bool {
|
||||||
|
if contentLength > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if contentLength < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// For zero bodies, whether we send a content-length depends on the method.
|
||||||
|
// It also kinda doesn't matter for http2 either way, with END_STREAM.
|
||||||
|
switch method {
|
||||||
|
case "POST", "PUT", "PATCH":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validPseudoPath(v string) bool {
|
||||||
|
return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
// actualContentLength returns a sanitized version of
|
||||||
|
// req.ContentLength, where 0 actually means zero (not unknown) and -1
|
||||||
|
// means unknown.
|
||||||
|
func actualContentLength(req *http.Request) int64 {
|
||||||
|
if req.Body == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if req.ContentLength != 0 {
|
||||||
|
return req.ContentLength
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
111
vendor/github.com/lucas-clemente/quic-go/h2quic/response.go
generated
vendored
Normal file
111
vendor/github.com/lucas-clemente/quic-go/h2quic/response.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// copied from net/http2/transport.go
|
||||||
|
|
||||||
|
var errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
|
||||||
|
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
|
||||||
|
|
||||||
|
// from the handleResponse function
|
||||||
|
func responseFromHeaders(f *http2.MetaHeadersFrame) (*http.Response, error) {
|
||||||
|
if f.Truncated {
|
||||||
|
return nil, errResponseHeaderListSize
|
||||||
|
}
|
||||||
|
|
||||||
|
status := f.PseudoValue("status")
|
||||||
|
if status == "" {
|
||||||
|
return nil, errors.New("missing status pseudo header")
|
||||||
|
}
|
||||||
|
statusCode, err := strconv.Atoi(status)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("malformed non-numeric status pseudo header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode == 100 {
|
||||||
|
// TODO: handle this
|
||||||
|
|
||||||
|
// traceGot100Continue(cs.trace)
|
||||||
|
// if cs.on100 != nil {
|
||||||
|
// cs.on100() // forces any write delay timer to fire
|
||||||
|
// }
|
||||||
|
// cs.pastHeaders = false // do it all again
|
||||||
|
// return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
header := make(http.Header)
|
||||||
|
res := &http.Response{
|
||||||
|
Proto: "HTTP/2.0",
|
||||||
|
ProtoMajor: 2,
|
||||||
|
Header: header,
|
||||||
|
StatusCode: statusCode,
|
||||||
|
Status: status + " " + http.StatusText(statusCode),
|
||||||
|
}
|
||||||
|
for _, hf := range f.RegularFields() {
|
||||||
|
key := http.CanonicalHeaderKey(hf.Name)
|
||||||
|
if key == "Trailer" {
|
||||||
|
t := res.Trailer
|
||||||
|
if t == nil {
|
||||||
|
t = make(http.Header)
|
||||||
|
res.Trailer = t
|
||||||
|
}
|
||||||
|
foreachHeaderElement(hf.Value, func(v string) {
|
||||||
|
t[http.CanonicalHeaderKey(v)] = nil
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
header[key] = append(header[key], hf.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// continuation of the handleResponse function
|
||||||
|
func setLength(res *http.Response, isHead, streamEnded bool) *http.Response {
|
||||||
|
if !streamEnded || isHead {
|
||||||
|
res.ContentLength = -1
|
||||||
|
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
||||||
|
if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
||||||
|
res.ContentLength = clen64
|
||||||
|
} else {
|
||||||
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
||||||
|
// more safe smuggling-wise to ignore.
|
||||||
|
}
|
||||||
|
} else if len(clens) > 1 {
|
||||||
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
||||||
|
// more safe smuggling-wise to ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from net/http/server.go
|
||||||
|
|
||||||
|
// foreachHeaderElement splits v according to the "#rule" construction
|
||||||
|
// in RFC 2616 section 2.1 and calls fn for each non-empty element.
|
||||||
|
func foreachHeaderElement(v string, fn func(string)) {
|
||||||
|
v = textproto.TrimString(v)
|
||||||
|
if v == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(v, ",") {
|
||||||
|
fn(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, f := range strings.Split(v, ",") {
|
||||||
|
if f = textproto.TrimString(f); f != "" {
|
||||||
|
fn(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer.go
generated
vendored
Normal file
108
vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
dataStreamID protocol.StreamID
|
||||||
|
dataStream quic.Stream
|
||||||
|
|
||||||
|
headerStream quic.Stream
|
||||||
|
headerStreamMutex *sync.Mutex
|
||||||
|
|
||||||
|
header http.Header
|
||||||
|
status int // status code passed to WriteHeader
|
||||||
|
headerWritten bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResponseWriter(headerStream quic.Stream, headerStreamMutex *sync.Mutex, dataStream quic.Stream, dataStreamID protocol.StreamID) *responseWriter {
|
||||||
|
return &responseWriter{
|
||||||
|
header: http.Header{},
|
||||||
|
headerStream: headerStream,
|
||||||
|
headerStreamMutex: headerStreamMutex,
|
||||||
|
dataStream: dataStream,
|
||||||
|
dataStreamID: dataStreamID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Header() http.Header {
|
||||||
|
return w.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) WriteHeader(status int) {
|
||||||
|
if w.headerWritten {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.headerWritten = true
|
||||||
|
w.status = status
|
||||||
|
|
||||||
|
var headers bytes.Buffer
|
||||||
|
enc := hpack.NewEncoder(&headers)
|
||||||
|
enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
|
||||||
|
|
||||||
|
for k, v := range w.header {
|
||||||
|
for index := range v {
|
||||||
|
enc.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v[index]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Infof("Responding with %d", status)
|
||||||
|
w.headerStreamMutex.Lock()
|
||||||
|
defer w.headerStreamMutex.Unlock()
|
||||||
|
h2framer := http2.NewFramer(w.headerStream, nil)
|
||||||
|
err := h2framer.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: uint32(w.dataStreamID),
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: headers.Bytes(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("could not write h2 header: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Write(p []byte) (int, error) {
|
||||||
|
if !w.headerWritten {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
if !bodyAllowedForStatus(w.status) {
|
||||||
|
return 0, http.ErrBodyNotAllowed
|
||||||
|
}
|
||||||
|
return w.dataStream.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Flush() {}
|
||||||
|
|
||||||
|
// TODO: Implement a functional CloseNotify method.
|
||||||
|
func (w *responseWriter) CloseNotify() <-chan bool { return make(<-chan bool) }
|
||||||
|
|
||||||
|
// test that we implement http.Flusher
|
||||||
|
var _ http.Flusher = &responseWriter{}
|
||||||
|
|
||||||
|
// test that we implement http.CloseNotifier
|
||||||
|
var _ http.CloseNotifier = &responseWriter{}
|
||||||
|
|
||||||
|
// copied from http2/http2.go
|
||||||
|
// bodyAllowedForStatus reports whether a given response status code
|
||||||
|
// permits a body. See RFC 2616, section 4.4.
|
||||||
|
func bodyAllowedForStatus(status int) bool {
|
||||||
|
switch {
|
||||||
|
case status >= 100 && status <= 199:
|
||||||
|
return false
|
||||||
|
case status == 204:
|
||||||
|
return false
|
||||||
|
case status == 304:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
144
vendor/github.com/lucas-clemente/quic-go/h2quic/roundtrip.go
generated
vendored
Normal file
144
vendor/github.com/lucas-clemente/quic-go/h2quic/roundtrip.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
|
||||||
|
"golang.org/x/net/lex/httplex"
|
||||||
|
)
|
||||||
|
|
||||||
|
type roundTripCloser interface {
|
||||||
|
http.RoundTripper
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTripper implements the http.RoundTripper interface
|
||||||
|
type RoundTripper struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
// DisableCompression, if true, prevents the Transport from
|
||||||
|
// requesting compression with an "Accept-Encoding: gzip"
|
||||||
|
// request header when the Request contains no existing
|
||||||
|
// Accept-Encoding value. If the Transport requests gzip on
|
||||||
|
// its own and gets a gzipped response, it's transparently
|
||||||
|
// decoded in the Response.Body. However, if the user
|
||||||
|
// explicitly requested gzip it is not automatically
|
||||||
|
// uncompressed.
|
||||||
|
DisableCompression bool
|
||||||
|
|
||||||
|
// TLSClientConfig specifies the TLS configuration to use with
|
||||||
|
// tls.Client. If nil, the default configuration is used.
|
||||||
|
TLSClientConfig *tls.Config
|
||||||
|
|
||||||
|
// QuicConfig is the quic.Config used for dialing new connections.
|
||||||
|
// If nil, reasonable default values will be used.
|
||||||
|
QuicConfig *quic.Config
|
||||||
|
|
||||||
|
clients map[string]roundTripCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ roundTripCloser = &RoundTripper{}
|
||||||
|
|
||||||
|
// RoundTrip does a round trip
|
||||||
|
func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
if req.URL == nil {
|
||||||
|
closeRequestBody(req)
|
||||||
|
return nil, errors.New("quic: nil Request.URL")
|
||||||
|
}
|
||||||
|
if req.URL.Host == "" {
|
||||||
|
closeRequestBody(req)
|
||||||
|
return nil, errors.New("quic: no Host in request URL")
|
||||||
|
}
|
||||||
|
if req.Header == nil {
|
||||||
|
closeRequestBody(req)
|
||||||
|
return nil, errors.New("quic: nil Request.Header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.URL.Scheme == "https" {
|
||||||
|
for k, vv := range req.Header {
|
||||||
|
if !httplex.ValidHeaderFieldName(k) {
|
||||||
|
return nil, fmt.Errorf("quic: invalid http header field name %q", k)
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
if !httplex.ValidHeaderFieldValue(v) {
|
||||||
|
return nil, fmt.Errorf("quic: invalid http header field value %q for key %v", v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
closeRequestBody(req)
|
||||||
|
return nil, fmt.Errorf("quic: unsupported protocol scheme: %s", req.URL.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method != "" && !validMethod(req.Method) {
|
||||||
|
closeRequestBody(req)
|
||||||
|
return nil, fmt.Errorf("quic: invalid method %q", req.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := authorityAddr("https", hostnameFromRequest(req))
|
||||||
|
return r.getClient(hostname).RoundTrip(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RoundTripper) getClient(hostname string) http.RoundTripper {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
|
||||||
|
if r.clients == nil {
|
||||||
|
r.clients = make(map[string]roundTripCloser)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, ok := r.clients[hostname]
|
||||||
|
if !ok {
|
||||||
|
client = newClient(hostname, r.TLSClientConfig, &roundTripperOpts{DisableCompression: r.DisableCompression}, r.QuicConfig)
|
||||||
|
r.clients[hostname] = client
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the QUIC connections that this RoundTripper has used
|
||||||
|
func (r *RoundTripper) Close() error {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
for _, client := range r.clients {
|
||||||
|
if err := client.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.clients = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeRequestBody(req *http.Request) {
|
||||||
|
if req.Body != nil {
|
||||||
|
req.Body.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validMethod(method string) bool {
|
||||||
|
/*
|
||||||
|
Method = "OPTIONS" ; Section 9.2
|
||||||
|
| "GET" ; Section 9.3
|
||||||
|
| "HEAD" ; Section 9.4
|
||||||
|
| "POST" ; Section 9.5
|
||||||
|
| "PUT" ; Section 9.6
|
||||||
|
| "DELETE" ; Section 9.7
|
||||||
|
| "TRACE" ; Section 9.8
|
||||||
|
| "CONNECT" ; Section 9.9
|
||||||
|
| extension-method
|
||||||
|
extension-method = token
|
||||||
|
token = 1*<any CHAR except CTLs or separators>
|
||||||
|
*/
|
||||||
|
return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from net/http/http.go
|
||||||
|
func isNotToken(r rune) bool {
|
||||||
|
return !httplex.IsTokenRune(r)
|
||||||
|
}
|
382
vendor/github.com/lucas-clemente/quic-go/h2quic/server.go
generated
vendored
Normal file
382
vendor/github.com/lucas-clemente/quic-go/h2quic/server.go
generated
vendored
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
quic "github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type streamCreator interface {
|
||||||
|
quic.Session
|
||||||
|
GetOrOpenStream(protocol.StreamID) (quic.Stream, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteCloser interface {
|
||||||
|
CloseRemote(protocol.ByteCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allows mocking of quic.Listen and quic.ListenAddr
|
||||||
|
var (
|
||||||
|
quicListen = quic.Listen
|
||||||
|
quicListenAddr = quic.ListenAddr
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server is a HTTP2 server listening for QUIC connections.
|
||||||
|
type Server struct {
|
||||||
|
*http.Server
|
||||||
|
|
||||||
|
// By providing a quic.Config, it is possible to set parameters of the QUIC connection.
|
||||||
|
// If nil, it uses reasonable default values.
|
||||||
|
QuicConfig *quic.Config
|
||||||
|
|
||||||
|
// Private flag for demo, do not use
|
||||||
|
CloseAfterFirstRequest bool
|
||||||
|
|
||||||
|
port uint32 // used atomically
|
||||||
|
|
||||||
|
listenerMutex sync.Mutex
|
||||||
|
listener quic.Listener
|
||||||
|
|
||||||
|
supportedVersionsAsString string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
|
||||||
|
func (s *Server) ListenAndServe() error {
|
||||||
|
if s.Server == nil {
|
||||||
|
return errors.New("use of h2quic.Server without http.Server")
|
||||||
|
}
|
||||||
|
return s.serveImpl(s.TLSConfig, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
|
||||||
|
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
|
||||||
|
var err error
|
||||||
|
certs := make([]tls.Certificate, 1)
|
||||||
|
certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// We currently only use the cert-related stuff from tls.Config,
|
||||||
|
// so we don't need to make a full copy.
|
||||||
|
config := &tls.Config{
|
||||||
|
Certificates: certs,
|
||||||
|
}
|
||||||
|
return s.serveImpl(config, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve an existing UDP connection.
|
||||||
|
func (s *Server) Serve(conn net.PacketConn) error {
|
||||||
|
return s.serveImpl(s.TLSConfig, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
|
||||||
|
if s.Server == nil {
|
||||||
|
return errors.New("use of h2quic.Server without http.Server")
|
||||||
|
}
|
||||||
|
s.listenerMutex.Lock()
|
||||||
|
if s.listener != nil {
|
||||||
|
s.listenerMutex.Unlock()
|
||||||
|
return errors.New("ListenAndServe may only be called once")
|
||||||
|
}
|
||||||
|
|
||||||
|
var ln quic.Listener
|
||||||
|
var err error
|
||||||
|
if conn == nil {
|
||||||
|
ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig)
|
||||||
|
} else {
|
||||||
|
ln, err = quicListen(conn, tlsConfig, s.QuicConfig)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.listenerMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.listener = ln
|
||||||
|
s.listenerMutex.Unlock()
|
||||||
|
|
||||||
|
for {
|
||||||
|
sess, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.handleHeaderStream(sess.(streamCreator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleHeaderStream(session streamCreator) {
|
||||||
|
stream, err := session.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
session.Close(qerr.Error(qerr.InvalidHeadersStreamData, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stream.StreamID() != 3 {
|
||||||
|
session.Close(qerr.Error(qerr.InternalError, "h2quic server BUG: header stream does not have stream ID 3"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hpackDecoder := hpack.NewDecoder(4096, nil)
|
||||||
|
h2framer := http2.NewFramer(nil, stream)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var headerStreamMutex sync.Mutex // Protects concurrent calls to Write()
|
||||||
|
for {
|
||||||
|
if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil {
|
||||||
|
// QuicErrors must originate from stream.Read() returning an error.
|
||||||
|
// In this case, the session has already logged the error, so we don't
|
||||||
|
// need to log it again.
|
||||||
|
if _, ok := err.(*qerr.QuicError); !ok {
|
||||||
|
utils.Errorf("error handling h2 request: %s", err.Error())
|
||||||
|
}
|
||||||
|
session.Close(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
|
||||||
|
h2frame, err := h2framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
|
||||||
|
}
|
||||||
|
h2headersFrame, ok := h2frame.(*http2.HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
|
||||||
|
}
|
||||||
|
if !h2headersFrame.HeadersEnded() {
|
||||||
|
return errors.New("http2 header continuation not implemented")
|
||||||
|
}
|
||||||
|
headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("invalid http2 headers encoding: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := requestFromHeaders(headers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.RemoteAddr = session.RemoteAddr().String()
|
||||||
|
|
||||||
|
if utils.Debug() {
|
||||||
|
utils.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID)
|
||||||
|
} else {
|
||||||
|
utils.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request
|
||||||
|
if dataStream == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamEnded bool
|
||||||
|
if h2headersFrame.StreamEnded() {
|
||||||
|
dataStream.(remoteCloser).CloseRemote(0)
|
||||||
|
streamEnded = true
|
||||||
|
_, _ = dataStream.Read([]byte{0}) // read the eof
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody := newRequestBody(dataStream)
|
||||||
|
req.Body = reqBody
|
||||||
|
|
||||||
|
responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
handler := s.Handler
|
||||||
|
if handler == nil {
|
||||||
|
handler = http.DefaultServeMux
|
||||||
|
}
|
||||||
|
panicked := false
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if p := recover(); p != nil {
|
||||||
|
// Copied from net/http/server.go
|
||||||
|
const size = 64 << 10
|
||||||
|
buf := make([]byte, size)
|
||||||
|
buf = buf[:runtime.Stack(buf, false)]
|
||||||
|
utils.Errorf("http: panic serving: %v\n%s", p, buf)
|
||||||
|
panicked = true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
handler.ServeHTTP(responseWriter, req)
|
||||||
|
}()
|
||||||
|
if panicked {
|
||||||
|
responseWriter.WriteHeader(500)
|
||||||
|
} else {
|
||||||
|
responseWriter.WriteHeader(200)
|
||||||
|
}
|
||||||
|
if responseWriter.dataStream != nil {
|
||||||
|
if !streamEnded && !reqBody.requestRead {
|
||||||
|
responseWriter.dataStream.Reset(nil)
|
||||||
|
}
|
||||||
|
responseWriter.dataStream.Close()
|
||||||
|
}
|
||||||
|
if s.CloseAfterFirstRequest {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
session.Close(nil)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
|
||||||
|
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
s.listenerMutex.Lock()
|
||||||
|
defer s.listenerMutex.Unlock()
|
||||||
|
if s.listener != nil {
|
||||||
|
err := s.listener.Close()
|
||||||
|
s.listener = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.
|
||||||
|
// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
|
||||||
|
func (s *Server) CloseGracefully(timeout time.Duration) error {
|
||||||
|
// TODO: implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
|
||||||
|
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
|
||||||
|
// Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
|
||||||
|
func (s *Server) SetQuicHeaders(hdr http.Header) error {
|
||||||
|
port := atomic.LoadUint32(&s.port)
|
||||||
|
|
||||||
|
if port == 0 {
|
||||||
|
// Extract port from s.Server.Addr
|
||||||
|
_, portStr, err := net.SplitHostPort(s.Server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
portInt, err := net.LookupPort("tcp", portStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
port = uint32(portInt)
|
||||||
|
atomic.StoreUint32(&s.port, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.supportedVersionsAsString == "" {
|
||||||
|
for i, v := range protocol.SupportedVersions {
|
||||||
|
s.supportedVersionsAsString += strconv.Itoa(int(v))
|
||||||
|
if i != len(protocol.SupportedVersions)-1 {
|
||||||
|
s.supportedVersionsAsString += ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServeQUIC listens on the UDP network address addr and calls the
|
||||||
|
// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is
|
||||||
|
// used when handler is nil.
|
||||||
|
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
|
||||||
|
server := &Server{
|
||||||
|
Server: &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
Handler: handler,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return server.ListenAndServeTLS(certFile, keyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe listens on the given network address for both, TLS and QUIC
|
||||||
|
// connetions in parallel. It returns if one of the two returns an error.
|
||||||
|
// http.DefaultServeMux is used when handler is nil.
|
||||||
|
// The correct Alt-Svc headers for QUIC are set.
|
||||||
|
func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error {
|
||||||
|
// Load certs
|
||||||
|
var err error
|
||||||
|
certs := make([]tls.Certificate, 1)
|
||||||
|
certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// We currently only use the cert-related stuff from tls.Config,
|
||||||
|
// so we don't need to make a full copy.
|
||||||
|
config := &tls.Config{
|
||||||
|
Certificates: certs,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the listeners
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", udpAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer udpConn.Close()
|
||||||
|
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tcpConn, err := net.ListenTCP("tcp", tcpAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tcpConn.Close()
|
||||||
|
|
||||||
|
// Start the servers
|
||||||
|
httpServer := &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
TLSConfig: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
quicServer := &Server{
|
||||||
|
Server: httpServer,
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler == nil {
|
||||||
|
handler = http.DefaultServeMux
|
||||||
|
}
|
||||||
|
httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
quicServer.SetQuicHeaders(w.Header())
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
hErr := make(chan error)
|
||||||
|
qErr := make(chan error)
|
||||||
|
go func() {
|
||||||
|
hErr <- httpServer.Serve(tcpConn)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
qErr <- quicServer.Serve(udpConn)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-hErr:
|
||||||
|
quicServer.Close()
|
||||||
|
return err
|
||||||
|
case err := <-qErr:
|
||||||
|
// Cannot close the HTTP server or wait for requests to complete properly :/
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
265
vendor/github.com/lucas-clemente/quic-go/handshake/connection_parameters_manager.go
generated
vendored
Normal file
265
vendor/github.com/lucas-clemente/quic-go/handshake/connection_parameters_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
package handshake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionParametersManager negotiates and stores the connection parameters
|
||||||
|
// A ConnectionParametersManager can be used for a server as well as a client
|
||||||
|
// For the server:
|
||||||
|
// 1. call SetFromMap with the values received in the CHLO. This sets the corresponding values here, subject to negotiation
|
||||||
|
// 2. call GetHelloMap to get the values to send in the SHLO
|
||||||
|
// For the client:
|
||||||
|
// 1. call GetHelloMap to get the values to send in a CHLO
|
||||||
|
// 2. call SetFromMap with the values received in the SHLO
|
||||||
|
type ConnectionParametersManager interface {
|
||||||
|
SetFromMap(map[Tag][]byte) error
|
||||||
|
GetHelloMap() (map[Tag][]byte, error)
|
||||||
|
|
||||||
|
GetSendStreamFlowControlWindow() protocol.ByteCount
|
||||||
|
GetSendConnectionFlowControlWindow() protocol.ByteCount
|
||||||
|
GetReceiveStreamFlowControlWindow() protocol.ByteCount
|
||||||
|
GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount
|
||||||
|
GetReceiveConnectionFlowControlWindow() protocol.ByteCount
|
||||||
|
GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount
|
||||||
|
GetMaxOutgoingStreams() uint32
|
||||||
|
GetMaxIncomingStreams() uint32
|
||||||
|
GetIdleConnectionStateLifetime() time.Duration
|
||||||
|
TruncateConnectionID() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type connectionParametersManager struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
version protocol.VersionNumber
|
||||||
|
perspective protocol.Perspective
|
||||||
|
|
||||||
|
flowControlNegotiated bool
|
||||||
|
|
||||||
|
truncateConnectionID bool
|
||||||
|
maxStreamsPerConnection uint32
|
||||||
|
maxIncomingDynamicStreamsPerConnection uint32
|
||||||
|
idleConnectionStateLifetime time.Duration
|
||||||
|
sendStreamFlowControlWindow protocol.ByteCount
|
||||||
|
sendConnectionFlowControlWindow protocol.ByteCount
|
||||||
|
receiveStreamFlowControlWindow protocol.ByteCount
|
||||||
|
receiveConnectionFlowControlWindow protocol.ByteCount
|
||||||
|
maxReceiveStreamFlowControlWindow protocol.ByteCount
|
||||||
|
maxReceiveConnectionFlowControlWindow protocol.ByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ConnectionParametersManager = &connectionParametersManager{}
|
||||||
|
|
||||||
|
// ErrMalformedTag is returned when the tag value cannot be read
|
||||||
|
var (
|
||||||
|
ErrMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value")
|
||||||
|
ErrFlowControlRenegotiationNotSupported = qerr.Error(qerr.InvalidCryptoMessageParameter, "renegotiation of flow control parameters not supported")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewConnectionParamatersManager creates a new connection parameters manager
|
||||||
|
func NewConnectionParamatersManager(
|
||||||
|
pers protocol.Perspective, v protocol.VersionNumber,
|
||||||
|
maxReceiveStreamFlowControlWindow protocol.ByteCount, maxReceiveConnectionFlowControlWindow protocol.ByteCount,
|
||||||
|
) ConnectionParametersManager {
|
||||||
|
h := &connectionParametersManager{
|
||||||
|
perspective: pers,
|
||||||
|
version: v,
|
||||||
|
sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client
|
||||||
|
sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client
|
||||||
|
receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
|
||||||
|
receiveConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
|
||||||
|
maxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
|
||||||
|
maxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.perspective == protocol.PerspectiveServer {
|
||||||
|
h.idleConnectionStateLifetime = protocol.DefaultIdleTimeout
|
||||||
|
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
|
||||||
|
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the client's perspective
|
||||||
|
} else {
|
||||||
|
h.idleConnectionStateLifetime = protocol.MaxIdleTimeoutClient
|
||||||
|
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
|
||||||
|
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the server's perspective
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFromMap reads all params
|
||||||
|
func (h *connectionParametersManager) SetFromMap(params map[Tag][]byte) error {
|
||||||
|
h.mutex.Lock()
|
||||||
|
defer h.mutex.Unlock()
|
||||||
|
|
||||||
|
if value, ok := params[TagTCID]; ok && h.perspective == protocol.PerspectiveServer {
|
||||||
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.truncateConnectionID = (clientValue == 0)
|
||||||
|
}
|
||||||
|
if value, ok := params[TagMSPC]; ok {
|
||||||
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.maxStreamsPerConnection = h.negotiateMaxStreamsPerConnection(clientValue)
|
||||||
|
}
|
||||||
|
if value, ok := params[TagMIDS]; ok {
|
||||||
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.maxIncomingDynamicStreamsPerConnection = h.negotiateMaxIncomingDynamicStreamsPerConnection(clientValue)
|
||||||
|
}
|
||||||
|
if value, ok := params[TagICSL]; ok {
|
||||||
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second)
|
||||||
|
}
|
||||||
|
if value, ok := params[TagSFCW]; ok {
|
||||||
|
if h.flowControlNegotiated {
|
||||||
|
return ErrFlowControlRenegotiationNotSupported
|
||||||
|
}
|
||||||
|
sendStreamFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.sendStreamFlowControlWindow = protocol.ByteCount(sendStreamFlowControlWindow)
|
||||||
|
}
|
||||||
|
if value, ok := params[TagCFCW]; ok {
|
||||||
|
if h.flowControlNegotiated {
|
||||||
|
return ErrFlowControlRenegotiationNotSupported
|
||||||
|
}
|
||||||
|
sendConnectionFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||||
|
if err != nil {
|
||||||
|
return ErrMalformedTag
|
||||||
|
}
|
||||||
|
h.sendConnectionFlowControlWindow = protocol.ByteCount(sendConnectionFlowControlWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, containsSFCW := params[TagSFCW]
|
||||||
|
_, containsCFCW := params[TagCFCW]
|
||||||
|
if containsCFCW || containsSFCW {
|
||||||
|
h.flowControlNegotiated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *connectionParametersManager) negotiateMaxStreamsPerConnection(clientValue uint32) uint32 {
|
||||||
|
return utils.MinUint32(clientValue, protocol.MaxStreamsPerConnection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *connectionParametersManager) negotiateMaxIncomingDynamicStreamsPerConnection(clientValue uint32) uint32 {
|
||||||
|
return utils.MinUint32(clientValue, protocol.MaxIncomingDynamicStreamsPerConnection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *connectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration {
|
||||||
|
if h.perspective == protocol.PerspectiveServer {
|
||||||
|
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutServer)
|
||||||
|
}
|
||||||
|
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHelloMap gets all parameters needed for the Hello message
|
||||||
|
func (h *connectionParametersManager) GetHelloMap() (map[Tag][]byte, error) {
|
||||||
|
sfcw := bytes.NewBuffer([]byte{})
|
||||||
|
utils.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow()))
|
||||||
|
cfcw := bytes.NewBuffer([]byte{})
|
||||||
|
utils.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow()))
|
||||||
|
mspc := bytes.NewBuffer([]byte{})
|
||||||
|
utils.WriteUint32(mspc, h.maxStreamsPerConnection)
|
||||||
|
mids := bytes.NewBuffer([]byte{})
|
||||||
|
utils.WriteUint32(mids, protocol.MaxIncomingDynamicStreamsPerConnection)
|
||||||
|
icsl := bytes.NewBuffer([]byte{})
|
||||||
|
utils.WriteUint32(icsl, uint32(h.GetIdleConnectionStateLifetime()/time.Second))
|
||||||
|
|
||||||
|
return map[Tag][]byte{
|
||||||
|
TagICSL: icsl.Bytes(),
|
||||||
|
TagMSPC: mspc.Bytes(),
|
||||||
|
TagMIDS: mids.Bytes(),
|
||||||
|
TagCFCW: cfcw.Bytes(),
|
||||||
|
TagSFCW: sfcw.Bytes(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSendStreamFlowControlWindow gets the size of the stream-level flow control window for sending data
|
||||||
|
func (h *connectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.sendStreamFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSendConnectionFlowControlWindow gets the size of the stream-level flow control window for sending data
|
||||||
|
func (h *connectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.sendConnectionFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceiveStreamFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
||||||
|
func (h *connectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.receiveStreamFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxReceiveStreamFlowControlWindow gets the maximum size of the stream-level flow control window for sending data
|
||||||
|
func (h *connectionParametersManager) GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount {
|
||||||
|
return h.maxReceiveStreamFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
||||||
|
func (h *connectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.receiveConnectionFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxReceiveConnectionFlowControlWindow gets the maximum size of the stream-level flow control window for sending data
|
||||||
|
func (h *connectionParametersManager) GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount {
|
||||||
|
return h.maxReceiveConnectionFlowControlWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxOutgoingStreams gets the maximum number of outgoing streams per connection
|
||||||
|
func (h *connectionParametersManager) GetMaxOutgoingStreams() uint32 {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
return h.maxIncomingDynamicStreamsPerConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxIncomingStreams get the maximum number of incoming streams per connection
|
||||||
|
func (h *connectionParametersManager) GetMaxIncomingStreams() uint32 {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
maxStreams := protocol.MaxIncomingDynamicStreamsPerConnection
|
||||||
|
return utils.MaxUint32(uint32(maxStreams)+protocol.MaxStreamsMinimumIncrement, uint32(float64(maxStreams)*protocol.MaxStreamsMultiplier))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIdleConnectionStateLifetime gets the idle timeout
|
||||||
|
func (h *connectionParametersManager) GetIdleConnectionStateLifetime() time.Duration {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.idleConnectionStateLifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncateConnectionID determines if the client requests truncated ConnectionIDs
|
||||||
|
func (h *connectionParametersManager) TruncateConnectionID() bool {
|
||||||
|
if h.perspective == protocol.PerspectiveClient {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
return h.truncateConnectionID
|
||||||
|
}
|
539
vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup_client.go
generated
vendored
Normal file
539
vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup_client.go
generated
vendored
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
package handshake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/crypto"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cryptoSetupClient struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
hostname string
|
||||||
|
connID protocol.ConnectionID
|
||||||
|
version protocol.VersionNumber
|
||||||
|
negotiatedVersions []protocol.VersionNumber
|
||||||
|
|
||||||
|
cryptoStream io.ReadWriter
|
||||||
|
|
||||||
|
serverConfig *serverConfigClient
|
||||||
|
|
||||||
|
stk []byte
|
||||||
|
sno []byte
|
||||||
|
nonc []byte
|
||||||
|
proof []byte
|
||||||
|
chloForSignature []byte
|
||||||
|
lastSentCHLO []byte
|
||||||
|
certManager crypto.CertManager
|
||||||
|
|
||||||
|
divNonceChan chan []byte
|
||||||
|
diversificationNonce []byte
|
||||||
|
|
||||||
|
clientHelloCounter int
|
||||||
|
serverVerified bool // has the certificate chain and the proof already been verified
|
||||||
|
keyDerivation KeyDerivationFunction
|
||||||
|
keyExchange KeyExchangeFunction
|
||||||
|
|
||||||
|
receivedSecurePacket bool
|
||||||
|
nullAEAD crypto.AEAD
|
||||||
|
secureAEAD crypto.AEAD
|
||||||
|
forwardSecureAEAD crypto.AEAD
|
||||||
|
aeadChanged chan<- protocol.EncryptionLevel
|
||||||
|
|
||||||
|
params *TransportParameters
|
||||||
|
connectionParameters ConnectionParametersManager
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CryptoSetup = &cryptoSetupClient{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoObitForClientNonce = errors.New("CryptoSetup BUG: No OBIT for client nonce available")
|
||||||
|
errClientNonceAlreadyExists = errors.New("CryptoSetup BUG: A client nonce was already generated")
|
||||||
|
errConflictingDiversificationNonces = errors.New("Received two different diversification nonces")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewCryptoSetupClient creates a new CryptoSetup instance for a client
|
||||||
|
func NewCryptoSetupClient(
|
||||||
|
hostname string,
|
||||||
|
connID protocol.ConnectionID,
|
||||||
|
version protocol.VersionNumber,
|
||||||
|
cryptoStream io.ReadWriter,
|
||||||
|
tlsConfig *tls.Config,
|
||||||
|
connectionParameters ConnectionParametersManager,
|
||||||
|
aeadChanged chan<- protocol.EncryptionLevel,
|
||||||
|
params *TransportParameters,
|
||||||
|
negotiatedVersions []protocol.VersionNumber,
|
||||||
|
) (CryptoSetup, error) {
|
||||||
|
return &cryptoSetupClient{
|
||||||
|
hostname: hostname,
|
||||||
|
connID: connID,
|
||||||
|
version: version,
|
||||||
|
cryptoStream: cryptoStream,
|
||||||
|
certManager: crypto.NewCertManager(tlsConfig),
|
||||||
|
connectionParameters: connectionParameters,
|
||||||
|
keyDerivation: crypto.DeriveKeysAESGCM,
|
||||||
|
keyExchange: getEphermalKEX,
|
||||||
|
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveClient, version),
|
||||||
|
aeadChanged: aeadChanged,
|
||||||
|
negotiatedVersions: negotiatedVersions,
|
||||||
|
divNonceChan: make(chan []byte),
|
||||||
|
params: params,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) HandleCryptoStream() error {
|
||||||
|
messageChan := make(chan HandshakeMessage)
|
||||||
|
errorChan := make(chan error)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
message, err := ParseHandshakeMessage(h.cryptoStream)
|
||||||
|
if err != nil {
|
||||||
|
errorChan <- qerr.Error(qerr.HandshakeFailed, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messageChan <- message
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := h.maybeUpgradeCrypto()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.mutex.RLock()
|
||||||
|
sendCHLO := h.secureAEAD == nil
|
||||||
|
h.mutex.RUnlock()
|
||||||
|
|
||||||
|
if sendCHLO {
|
||||||
|
err = h.sendCHLO()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var message HandshakeMessage
|
||||||
|
select {
|
||||||
|
case divNonce := <-h.divNonceChan:
|
||||||
|
if len(h.diversificationNonce) != 0 && !bytes.Equal(h.diversificationNonce, divNonce) {
|
||||||
|
return errConflictingDiversificationNonces
|
||||||
|
}
|
||||||
|
h.diversificationNonce = divNonce
|
||||||
|
// there's no message to process, but we should try upgrading the crypto again
|
||||||
|
continue
|
||||||
|
case message = <-messageChan:
|
||||||
|
case err = <-errorChan:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Debugf("Got %s", message)
|
||||||
|
switch message.Tag {
|
||||||
|
case TagREJ:
|
||||||
|
err = h.handleREJMessage(message.Data)
|
||||||
|
case TagSHLO:
|
||||||
|
err = h.handleSHLOMessage(message.Data)
|
||||||
|
default:
|
||||||
|
return qerr.InvalidCryptoMessageType
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if stk, ok := cryptoData[TagSTK]; ok {
|
||||||
|
h.stk = stk
|
||||||
|
}
|
||||||
|
|
||||||
|
if sno, ok := cryptoData[TagSNO]; ok {
|
||||||
|
h.sno = sno
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: what happens if the server sends a different server config in two packets?
|
||||||
|
if scfg, ok := cryptoData[TagSCFG]; ok {
|
||||||
|
h.serverConfig, err = parseServerConfig(scfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.serverConfig.IsExpired() {
|
||||||
|
return qerr.CryptoServerConfigExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that we have a server config, we can use its OBIT value to generate a client nonce
|
||||||
|
if len(h.nonc) == 0 {
|
||||||
|
err = h.generateClientNonce()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if proof, ok := cryptoData[TagPROF]; ok {
|
||||||
|
h.proof = proof
|
||||||
|
h.chloForSignature = h.lastSentCHLO
|
||||||
|
}
|
||||||
|
|
||||||
|
if crt, ok := cryptoData[TagCERT]; ok {
|
||||||
|
err := h.certManager.SetData(crt)
|
||||||
|
if err != nil {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.certManager.Verify(h.hostname)
|
||||||
|
if err != nil {
|
||||||
|
utils.Infof("Certificate validation failed: %s", err.Error())
|
||||||
|
return qerr.ProofInvalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.serverConfig != nil && len(h.proof) != 0 && h.certManager.GetLeafCert() != nil {
|
||||||
|
validProof := h.certManager.VerifyServerProof(h.proof, h.chloForSignature, h.serverConfig.Get())
|
||||||
|
if !validProof {
|
||||||
|
utils.Infof("Server proof verification failed")
|
||||||
|
return qerr.ProofInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
h.serverVerified = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) handleSHLOMessage(cryptoData map[Tag][]byte) error {
|
||||||
|
h.mutex.Lock()
|
||||||
|
defer h.mutex.Unlock()
|
||||||
|
|
||||||
|
if !h.receivedSecurePacket {
|
||||||
|
return qerr.Error(qerr.CryptoEncryptionLevelIncorrect, "unencrypted SHLO message")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sno, ok := cryptoData[TagSNO]; ok {
|
||||||
|
h.sno = sno
|
||||||
|
}
|
||||||
|
|
||||||
|
serverPubs, ok := cryptoData[TagPUBS]
|
||||||
|
if !ok {
|
||||||
|
return qerr.Error(qerr.CryptoMessageParameterNotFound, "PUBS")
|
||||||
|
}
|
||||||
|
|
||||||
|
verTag, ok := cryptoData[TagVER]
|
||||||
|
if !ok {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "server hello missing version list")
|
||||||
|
}
|
||||||
|
if !h.validateVersionList(verTag) {
|
||||||
|
return qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := append(h.nonc, h.sno...)
|
||||||
|
|
||||||
|
ephermalSharedSecret, err := h.serverConfig.kex.CalculateSharedKey(serverPubs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
leafCert := h.certManager.GetLeafCert()
|
||||||
|
|
||||||
|
h.forwardSecureAEAD, err = h.keyDerivation(
|
||||||
|
true,
|
||||||
|
ephermalSharedSecret,
|
||||||
|
nonce,
|
||||||
|
h.connID,
|
||||||
|
h.lastSentCHLO,
|
||||||
|
h.serverConfig.Get(),
|
||||||
|
leafCert,
|
||||||
|
nil,
|
||||||
|
protocol.PerspectiveClient,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.connectionParameters.SetFromMap(cryptoData)
|
||||||
|
if err != nil {
|
||||||
|
return qerr.InvalidCryptoMessageParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
h.aeadChanged <- protocol.EncryptionForwardSecure
|
||||||
|
close(h.aeadChanged)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) validateVersionList(verTags []byte) bool {
|
||||||
|
if len(h.negotiatedVersions) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(verTags)%4 != 0 || len(verTags)/4 != len(h.negotiatedVersions) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bytes.NewReader(verTags)
|
||||||
|
for _, negotiatedVersion := range h.negotiatedVersions {
|
||||||
|
verTag, err := utils.ReadUint32(b)
|
||||||
|
if err != nil { // should never occur, since the length was already checked
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ver := protocol.VersionTagToNumber(verTag)
|
||||||
|
if !protocol.IsSupportedVersion(protocol.SupportedVersions, ver) {
|
||||||
|
ver = protocol.VersionUnsupported
|
||||||
|
}
|
||||||
|
if ver != negotiatedVersion {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
if h.forwardSecureAEAD != nil {
|
||||||
|
data, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err == nil {
|
||||||
|
return data, protocol.EncryptionForwardSecure, nil
|
||||||
|
}
|
||||||
|
return nil, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.secureAEAD != nil {
|
||||||
|
data, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err == nil {
|
||||||
|
h.receivedSecurePacket = true
|
||||||
|
return data, protocol.EncryptionSecure, nil
|
||||||
|
}
|
||||||
|
if h.receivedSecurePacket {
|
||||||
|
return nil, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
return res, protocol.EncryptionUnencrypted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) GetSealer() (protocol.EncryptionLevel, Sealer) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
if h.forwardSecureAEAD != nil {
|
||||||
|
return protocol.EncryptionForwardSecure, h.sealForwardSecure
|
||||||
|
} else if h.secureAEAD != nil {
|
||||||
|
return protocol.EncryptionSecure, h.sealSecure
|
||||||
|
} else {
|
||||||
|
return protocol.EncryptionUnencrypted, h.sealUnencrypted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) {
|
||||||
|
return protocol.EncryptionUnencrypted, h.sealUnencrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) GetSealerWithEncryptionLevel(encLevel protocol.EncryptionLevel) (Sealer, error) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
switch encLevel {
|
||||||
|
case protocol.EncryptionUnencrypted:
|
||||||
|
return h.sealUnencrypted, nil
|
||||||
|
case protocol.EncryptionSecure:
|
||||||
|
if h.secureAEAD == nil {
|
||||||
|
return nil, errors.New("CryptoSetupClient: no secureAEAD")
|
||||||
|
}
|
||||||
|
return h.sealSecure, nil
|
||||||
|
case protocol.EncryptionForwardSecure:
|
||||||
|
if h.forwardSecureAEAD == nil {
|
||||||
|
return nil, errors.New("CryptoSetupClient: no forwardSecureAEAD")
|
||||||
|
}
|
||||||
|
return h.sealForwardSecure, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("CryptoSetupClient: no encryption level specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) sealUnencrypted(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.nullAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) sealSecure(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) sealForwardSecure(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) DiversificationNonce() []byte {
|
||||||
|
panic("not needed for cryptoSetupClient")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) SetDiversificationNonce(data []byte) {
|
||||||
|
h.divNonceChan <- data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) sendCHLO() error {
|
||||||
|
h.clientHelloCounter++
|
||||||
|
if h.clientHelloCounter > protocol.MaxClientHellos {
|
||||||
|
return qerr.Error(qerr.CryptoTooManyRejects, fmt.Sprintf("More than %d rejects", protocol.MaxClientHellos))
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
|
tags, err := h.getTags()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.addPadding(tags)
|
||||||
|
message := HandshakeMessage{
|
||||||
|
Tag: TagCHLO,
|
||||||
|
Data: tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Debugf("Sending %s", message)
|
||||||
|
message.Write(b)
|
||||||
|
|
||||||
|
_, err = h.cryptoStream.Write(b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.lastSentCHLO = b.Bytes()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) getTags() (map[Tag][]byte, error) {
|
||||||
|
tags, err := h.connectionParameters.GetHelloMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tags[TagSNI] = []byte(h.hostname)
|
||||||
|
tags[TagPDMD] = []byte("X509")
|
||||||
|
|
||||||
|
ccs := h.certManager.GetCommonCertificateHashes()
|
||||||
|
if len(ccs) > 0 {
|
||||||
|
tags[TagCCS] = ccs
|
||||||
|
}
|
||||||
|
|
||||||
|
versionTag := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(versionTag, protocol.VersionNumberToTag(h.version))
|
||||||
|
tags[TagVER] = versionTag
|
||||||
|
|
||||||
|
if h.params.RequestConnectionIDTruncation {
|
||||||
|
tags[TagTCID] = []byte{0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
if len(h.stk) > 0 {
|
||||||
|
tags[TagSTK] = h.stk
|
||||||
|
}
|
||||||
|
if len(h.sno) > 0 {
|
||||||
|
tags[TagSNO] = h.sno
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.serverConfig != nil {
|
||||||
|
tags[TagSCID] = h.serverConfig.ID
|
||||||
|
|
||||||
|
leafCert := h.certManager.GetLeafCert()
|
||||||
|
if leafCert != nil {
|
||||||
|
certHash, _ := h.certManager.GetLeafCertHash()
|
||||||
|
xlct := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(xlct, certHash)
|
||||||
|
|
||||||
|
tags[TagNONC] = h.nonc
|
||||||
|
tags[TagXLCT] = xlct
|
||||||
|
tags[TagKEXS] = []byte("C255")
|
||||||
|
tags[TagAEAD] = []byte("AESG")
|
||||||
|
tags[TagPUBS] = h.serverConfig.kex.PublicKey() // TODO: check if 3 bytes need to be prepended
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a TagPAD to a tagMap, such that the total size will be bigger than the ClientHelloMinimumSize
|
||||||
|
func (h *cryptoSetupClient) addPadding(tags map[Tag][]byte) {
|
||||||
|
var size int
|
||||||
|
for _, tag := range tags {
|
||||||
|
size += 8 + len(tag) // 4 bytes for the tag + 4 bytes for the offset + the length of the data
|
||||||
|
}
|
||||||
|
paddingSize := protocol.ClientHelloMinimumSize - size
|
||||||
|
if paddingSize > 0 {
|
||||||
|
tags[TagPAD] = bytes.Repeat([]byte{0}, paddingSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) maybeUpgradeCrypto() error {
|
||||||
|
if !h.serverVerified {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h.mutex.Lock()
|
||||||
|
defer h.mutex.Unlock()
|
||||||
|
|
||||||
|
leafCert := h.certManager.GetLeafCert()
|
||||||
|
if h.secureAEAD == nil && (h.serverConfig != nil && len(h.serverConfig.sharedSecret) > 0 && len(h.nonc) > 0 && len(leafCert) > 0 && len(h.diversificationNonce) > 0 && len(h.lastSentCHLO) > 0) {
|
||||||
|
var err error
|
||||||
|
var nonce []byte
|
||||||
|
if h.sno == nil {
|
||||||
|
nonce = h.nonc
|
||||||
|
} else {
|
||||||
|
nonce = append(h.nonc, h.sno...)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.secureAEAD, err = h.keyDerivation(
|
||||||
|
false,
|
||||||
|
h.serverConfig.sharedSecret,
|
||||||
|
nonce,
|
||||||
|
h.connID,
|
||||||
|
h.lastSentCHLO,
|
||||||
|
h.serverConfig.Get(),
|
||||||
|
leafCert,
|
||||||
|
h.diversificationNonce,
|
||||||
|
protocol.PerspectiveClient,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.aeadChanged <- protocol.EncryptionSecure
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) generateClientNonce() error {
|
||||||
|
if len(h.nonc) > 0 {
|
||||||
|
return errClientNonceAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
nonc := make([]byte, 32)
|
||||||
|
binary.BigEndian.PutUint32(nonc, uint32(time.Now().Unix()))
|
||||||
|
|
||||||
|
if len(h.serverConfig.obit) != 8 {
|
||||||
|
return errNoObitForClientNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(nonc[4:12], h.serverConfig.obit)
|
||||||
|
|
||||||
|
_, err := rand.Read(nonc[12:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.nonc = nonc
|
||||||
|
return nil
|
||||||
|
}
|
470
vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup_server.go
generated
vendored
Normal file
470
vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup_server.go
generated
vendored
Normal file
|
@ -0,0 +1,470 @@
|
||||||
|
package handshake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/crypto"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyDerivationFunction is used for key derivation
|
||||||
|
type KeyDerivationFunction func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error)
|
||||||
|
|
||||||
|
// KeyExchangeFunction is used to make a new KEX
|
||||||
|
type KeyExchangeFunction func() crypto.KeyExchange
|
||||||
|
|
||||||
|
// The CryptoSetupServer handles all things crypto for the Session
|
||||||
|
type cryptoSetupServer struct {
|
||||||
|
connID protocol.ConnectionID
|
||||||
|
remoteAddr net.Addr
|
||||||
|
scfg *ServerConfig
|
||||||
|
stkGenerator *STKGenerator
|
||||||
|
diversificationNonce []byte
|
||||||
|
|
||||||
|
version protocol.VersionNumber
|
||||||
|
supportedVersions []protocol.VersionNumber
|
||||||
|
|
||||||
|
acceptSTKCallback func(net.Addr, *STK) bool
|
||||||
|
|
||||||
|
nullAEAD crypto.AEAD
|
||||||
|
secureAEAD crypto.AEAD
|
||||||
|
forwardSecureAEAD crypto.AEAD
|
||||||
|
receivedForwardSecurePacket bool
|
||||||
|
sentSHLO bool
|
||||||
|
receivedSecurePacket bool
|
||||||
|
aeadChanged chan<- protocol.EncryptionLevel
|
||||||
|
|
||||||
|
keyDerivation KeyDerivationFunction
|
||||||
|
keyExchange KeyExchangeFunction
|
||||||
|
|
||||||
|
cryptoStream io.ReadWriter
|
||||||
|
|
||||||
|
connectionParameters ConnectionParametersManager
|
||||||
|
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CryptoSetup = &cryptoSetupServer{}
|
||||||
|
|
||||||
|
// ErrHOLExperiment is returned when the client sends the FHL2 tag in the CHLO
|
||||||
|
// this is an expiremnt implemented by Chrome in QUIC 36, which we don't support
|
||||||
|
// TODO: remove this when dropping support for QUIC 36
|
||||||
|
var ErrHOLExperiment = qerr.Error(qerr.InvalidCryptoMessageParameter, "HOL experiment. Unsupported")
|
||||||
|
|
||||||
|
// NewCryptoSetup creates a new CryptoSetup instance for a server
|
||||||
|
func NewCryptoSetup(
|
||||||
|
connID protocol.ConnectionID,
|
||||||
|
remoteAddr net.Addr,
|
||||||
|
version protocol.VersionNumber,
|
||||||
|
scfg *ServerConfig,
|
||||||
|
cryptoStream io.ReadWriter,
|
||||||
|
connectionParametersManager ConnectionParametersManager,
|
||||||
|
supportedVersions []protocol.VersionNumber,
|
||||||
|
acceptSTK func(net.Addr, *STK) bool,
|
||||||
|
aeadChanged chan<- protocol.EncryptionLevel,
|
||||||
|
) (CryptoSetup, error) {
|
||||||
|
stkGenerator, err := NewSTKGenerator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cryptoSetupServer{
|
||||||
|
connID: connID,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
version: version,
|
||||||
|
supportedVersions: supportedVersions,
|
||||||
|
scfg: scfg,
|
||||||
|
stkGenerator: stkGenerator,
|
||||||
|
keyDerivation: crypto.DeriveKeysAESGCM,
|
||||||
|
keyExchange: getEphermalKEX,
|
||||||
|
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveServer, version),
|
||||||
|
cryptoStream: cryptoStream,
|
||||||
|
connectionParameters: connectionParametersManager,
|
||||||
|
acceptSTKCallback: acceptSTK,
|
||||||
|
aeadChanged: aeadChanged,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleCryptoStream reads and writes messages on the crypto stream
|
||||||
|
func (h *cryptoSetupServer) HandleCryptoStream() error {
|
||||||
|
for {
|
||||||
|
var chloData bytes.Buffer
|
||||||
|
message, err := ParseHandshakeMessage(io.TeeReader(h.cryptoStream, &chloData))
|
||||||
|
if err != nil {
|
||||||
|
return qerr.HandshakeFailed
|
||||||
|
}
|
||||||
|
if message.Tag != TagCHLO {
|
||||||
|
return qerr.InvalidCryptoMessageType
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Debugf("Got %s", message)
|
||||||
|
done, err := h.handleMessage(chloData.Bytes(), message.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if done {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) handleMessage(chloData []byte, cryptoData map[Tag][]byte) (bool, error) {
|
||||||
|
if _, isHOLExperiment := cryptoData[TagFHL2]; isHOLExperiment {
|
||||||
|
return false, ErrHOLExperiment
|
||||||
|
}
|
||||||
|
|
||||||
|
sniSlice, ok := cryptoData[TagSNI]
|
||||||
|
if !ok {
|
||||||
|
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
|
||||||
|
}
|
||||||
|
sni := string(sniSlice)
|
||||||
|
if sni == "" {
|
||||||
|
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent version downgrade attacks
|
||||||
|
// see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/N-de9j63tCk for a discussion and examples
|
||||||
|
verSlice, ok := cryptoData[TagVER]
|
||||||
|
if !ok {
|
||||||
|
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "client hello missing version tag")
|
||||||
|
}
|
||||||
|
if len(verSlice) != 4 {
|
||||||
|
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "incorrect version tag")
|
||||||
|
}
|
||||||
|
verTag := binary.LittleEndian.Uint32(verSlice)
|
||||||
|
ver := protocol.VersionTagToNumber(verTag)
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
certUncompressed, err := h.scfg.certChain.GetLeafCert(sni)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.isInchoateCHLO(cryptoData, certUncompressed) {
|
||||||
|
// We have a CHLO with a proper server config ID, do a 0-RTT handshake
|
||||||
|
reply, err = h.handleCHLO(sni, chloData, cryptoData)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, err = h.cryptoStream.Write(reply)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have an inchoate or non-matching CHLO, we now send a rejection
|
||||||
|
reply, err = h.handleInchoateCHLO(sni, chloData, cryptoData)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, err = h.cryptoStream.Write(reply)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a message
|
||||||
|
func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
if h.forwardSecureAEAD != nil {
|
||||||
|
res, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err == nil {
|
||||||
|
if !h.receivedForwardSecurePacket { // this is the first forward secure packet we receive from the client
|
||||||
|
h.receivedForwardSecurePacket = true
|
||||||
|
close(h.aeadChanged)
|
||||||
|
}
|
||||||
|
return res, protocol.EncryptionForwardSecure, nil
|
||||||
|
}
|
||||||
|
if h.receivedForwardSecurePacket {
|
||||||
|
return nil, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h.secureAEAD != nil {
|
||||||
|
res, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err == nil {
|
||||||
|
h.receivedSecurePacket = true
|
||||||
|
return res, protocol.EncryptionSecure, nil
|
||||||
|
}
|
||||||
|
if h.receivedSecurePacket {
|
||||||
|
return nil, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData)
|
||||||
|
if err != nil {
|
||||||
|
return res, protocol.EncryptionUnspecified, err
|
||||||
|
}
|
||||||
|
return res, protocol.EncryptionUnencrypted, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) GetSealer() (protocol.EncryptionLevel, Sealer) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
if h.forwardSecureAEAD != nil {
|
||||||
|
return protocol.EncryptionForwardSecure, h.sealForwardSecure
|
||||||
|
}
|
||||||
|
return protocol.EncryptionUnencrypted, h.sealUnencrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
if h.secureAEAD != nil {
|
||||||
|
return protocol.EncryptionSecure, h.sealSecure
|
||||||
|
}
|
||||||
|
return protocol.EncryptionUnencrypted, h.sealUnencrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) GetSealerWithEncryptionLevel(encLevel protocol.EncryptionLevel) (Sealer, error) {
|
||||||
|
h.mutex.RLock()
|
||||||
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
|
switch encLevel {
|
||||||
|
case protocol.EncryptionUnencrypted:
|
||||||
|
return h.sealUnencrypted, nil
|
||||||
|
case protocol.EncryptionSecure:
|
||||||
|
if h.secureAEAD == nil {
|
||||||
|
return nil, errors.New("CryptoSetupServer: no secureAEAD")
|
||||||
|
}
|
||||||
|
return h.sealSecure, nil
|
||||||
|
case protocol.EncryptionForwardSecure:
|
||||||
|
if h.forwardSecureAEAD == nil {
|
||||||
|
return nil, errors.New("CryptoSetupServer: no forwardSecureAEAD")
|
||||||
|
}
|
||||||
|
return h.sealForwardSecure, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("CryptoSetupServer: no encryption level specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) sealUnencrypted(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.nullAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) sealSecure(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) sealForwardSecure(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||||
|
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) isInchoateCHLO(cryptoData map[Tag][]byte, cert []byte) bool {
|
||||||
|
if _, ok := cryptoData[TagPUBS]; !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
scid, ok := cryptoData[TagSCID]
|
||||||
|
if !ok || !bytes.Equal(h.scfg.ID, scid) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
xlctTag, ok := cryptoData[TagXLCT]
|
||||||
|
if !ok || len(xlctTag) != 8 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
xlct := binary.LittleEndian.Uint64(xlctTag)
|
||||||
|
if crypto.HashCert(cert) != xlct {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !h.acceptSTK(cryptoData[TagSTK])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) acceptSTK(token []byte) bool {
|
||||||
|
stk, err := h.stkGenerator.DecodeToken(token)
|
||||||
|
if err != nil {
|
||||||
|
utils.Debugf("STK invalid: %s", err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return h.acceptSTKCallback(h.remoteAddr, stk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) handleInchoateCHLO(sni string, chlo []byte, cryptoData map[Tag][]byte) ([]byte, error) {
|
||||||
|
if len(chlo) < protocol.ClientHelloMinimumSize {
|
||||||
|
return nil, qerr.Error(qerr.CryptoInvalidValueLength, "CHLO too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := h.stkGenerator.NewToken(h.remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
replyMap := map[Tag][]byte{
|
||||||
|
TagSCFG: h.scfg.Get(),
|
||||||
|
TagSTK: token,
|
||||||
|
TagSVID: []byte("quic-go"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.acceptSTK(cryptoData[TagSTK]) {
|
||||||
|
proof, err := h.scfg.Sign(sni, chlo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commonSetHashes := cryptoData[TagCCS]
|
||||||
|
cachedCertsHashes := cryptoData[TagCCRT]
|
||||||
|
|
||||||
|
certCompressed, err := h.scfg.GetCertsCompressed(sni, commonSetHashes, cachedCertsHashes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Token was valid, send more details
|
||||||
|
replyMap[TagPROF] = proof
|
||||||
|
replyMap[TagCERT] = certCompressed
|
||||||
|
}
|
||||||
|
|
||||||
|
message := HandshakeMessage{
|
||||||
|
Tag: TagREJ,
|
||||||
|
Data: replyMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverReply bytes.Buffer
|
||||||
|
message.Write(&serverReply)
|
||||||
|
utils.Debugf("Sending %s", message)
|
||||||
|
return serverReply.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[Tag][]byte) ([]byte, error) {
|
||||||
|
// We have a CHLO matching our server config, we can continue with the 0-RTT handshake
|
||||||
|
sharedSecret, err := h.scfg.kex.CalculateSharedKey(cryptoData[TagPUBS])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.mutex.Lock()
|
||||||
|
defer h.mutex.Unlock()
|
||||||
|
|
||||||
|
certUncompressed, err := h.scfg.certChain.GetLeafCert(sni)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverNonce := make([]byte, 32)
|
||||||
|
if _, err = rand.Read(serverNonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.diversificationNonce = make([]byte, 32)
|
||||||
|
if _, err = rand.Read(h.diversificationNonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientNonce := cryptoData[TagNONC]
|
||||||
|
err = h.validateClientNonce(clientNonce)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aead := cryptoData[TagAEAD]
|
||||||
|
if !bytes.Equal(aead, []byte("AESG")) {
|
||||||
|
return nil, qerr.Error(qerr.CryptoNoSupport, "Unsupported AEAD or KEXS")
|
||||||
|
}
|
||||||
|
|
||||||
|
kexs := cryptoData[TagKEXS]
|
||||||
|
if !bytes.Equal(kexs, []byte("C255")) {
|
||||||
|
return nil, qerr.Error(qerr.CryptoNoSupport, "Unsupported AEAD or KEXS")
|
||||||
|
}
|
||||||
|
|
||||||
|
h.secureAEAD, err = h.keyDerivation(
|
||||||
|
false,
|
||||||
|
sharedSecret,
|
||||||
|
clientNonce,
|
||||||
|
h.connID,
|
||||||
|
data,
|
||||||
|
h.scfg.Get(),
|
||||||
|
certUncompressed,
|
||||||
|
h.diversificationNonce,
|
||||||
|
protocol.PerspectiveServer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.aeadChanged <- protocol.EncryptionSecure
|
||||||
|
|
||||||
|
// Generate a new curve instance to derive the forward secure key
|
||||||
|
var fsNonce bytes.Buffer
|
||||||
|
fsNonce.Write(clientNonce)
|
||||||
|
fsNonce.Write(serverNonce)
|
||||||
|
ephermalKex := h.keyExchange()
|
||||||
|
ephermalSharedSecret, err := ephermalKex.CalculateSharedKey(cryptoData[TagPUBS])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.forwardSecureAEAD, err = h.keyDerivation(
|
||||||
|
true,
|
||||||
|
ephermalSharedSecret,
|
||||||
|
fsNonce.Bytes(),
|
||||||
|
h.connID,
|
||||||
|
data,
|
||||||
|
h.scfg.Get(),
|
||||||
|
certUncompressed,
|
||||||
|
nil,
|
||||||
|
protocol.PerspectiveServer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.connectionParameters.SetFromMap(cryptoData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
replyMap, err := h.connectionParameters.GetHelloMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// add crypto parameters
|
||||||
|
verTag := &bytes.Buffer{}
|
||||||
|
for _, v := range h.supportedVersions {
|
||||||
|
utils.WriteUint32(verTag, protocol.VersionNumberToTag(v))
|
||||||
|
}
|
||||||
|
replyMap[TagPUBS] = ephermalKex.PublicKey()
|
||||||
|
replyMap[TagSNO] = serverNonce
|
||||||
|
replyMap[TagVER] = verTag.Bytes()
|
||||||
|
|
||||||
|
// note that the SHLO *has* to fit into one packet
|
||||||
|
message := HandshakeMessage{
|
||||||
|
Tag: TagSHLO,
|
||||||
|
Data: replyMap,
|
||||||
|
}
|
||||||
|
var reply bytes.Buffer
|
||||||
|
message.Write(&reply)
|
||||||
|
utils.Debugf("Sending %s", message)
|
||||||
|
|
||||||
|
h.aeadChanged <- protocol.EncryptionForwardSecure
|
||||||
|
|
||||||
|
return reply.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiversificationNonce returns the diversification nonce
|
||||||
|
func (h *cryptoSetupServer) DiversificationNonce() []byte {
|
||||||
|
return h.diversificationNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) SetDiversificationNonce(data []byte) {
|
||||||
|
panic("not needed for cryptoSetupServer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupServer) validateClientNonce(nonce []byte) error {
|
||||||
|
if len(nonce) != 32 {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "invalid client nonce length")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(nonce[4:12], h.scfg.obit) {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "OBIT not matching")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
50
vendor/github.com/lucas-clemente/quic-go/handshake/ephermal_cache.go
generated
vendored
Normal file
50
vendor/github.com/lucas-clemente/quic-go/handshake/ephermal_cache.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package handshake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/crypto"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kexLifetime = protocol.EphermalKeyLifetime
|
||||||
|
kexCurrent crypto.KeyExchange
|
||||||
|
kexCurrentTime time.Time
|
||||||
|
kexMutex sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// getEphermalKEX returns the currently active KEX, which changes every protocol.EphermalKeyLifetime
|
||||||
|
// See the explanation from the QUIC crypto doc:
|
||||||
|
//
|
||||||
|
// A single connection is the usual scope for forward security, but the security
|
||||||
|
// difference between an ephemeral key used for a single connection, and one
|
||||||
|
// used for all connections for 60 seconds is negligible. Thus we can amortise
|
||||||
|
// the Diffie-Hellman key generation at the server over all the connections in a
|
||||||
|
// small time span.
|
||||||
|
func getEphermalKEX() (res crypto.KeyExchange) {
|
||||||
|
kexMutex.RLock()
|
||||||
|
res = kexCurrent
|
||||||
|
t := kexCurrentTime
|
||||||
|
kexMutex.RUnlock()
|
||||||
|
if res != nil && time.Since(t) < kexLifetime {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
kexMutex.Lock()
|
||||||
|
defer kexMutex.Unlock()
|
||||||
|
// Check if still unfulfilled
|
||||||
|
if kexCurrent == nil || time.Since(kexCurrentTime) > kexLifetime {
|
||||||
|
kex, err := crypto.NewCurve25519KEX()
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("could not set KEX: %s", err.Error())
|
||||||
|
return kexCurrent
|
||||||
|
}
|
||||||
|
kexCurrent = kex
|
||||||
|
kexCurrentTime = time.Now()
|
||||||
|
return kexCurrent
|
||||||
|
}
|
||||||
|
return kexCurrent
|
||||||
|
}
|
136
vendor/github.com/lucas-clemente/quic-go/handshake/handshake_message.go
generated
vendored
Normal file
136
vendor/github.com/lucas-clemente/quic-go/handshake/handshake_message.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
package handshake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A HandshakeMessage is a handshake message
|
||||||
|
type HandshakeMessage struct {
|
||||||
|
Tag Tag
|
||||||
|
Data map[Tag][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fmt.Stringer = &HandshakeMessage{}
|
||||||
|
|
||||||
|
// ParseHandshakeMessage reads a crypto message
|
||||||
|
func ParseHandshakeMessage(r io.Reader) (HandshakeMessage, error) {
|
||||||
|
slice4 := make([]byte, 4)
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(r, slice4); err != nil {
|
||||||
|
return HandshakeMessage{}, err
|
||||||
|
}
|
||||||
|
messageTag := Tag(binary.LittleEndian.Uint32(slice4))
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(r, slice4); err != nil {
|
||||||
|
return HandshakeMessage{}, err
|
||||||
|
}
|
||||||
|
nPairs := binary.LittleEndian.Uint32(slice4)
|
||||||
|
|
||||||
|
if nPairs > protocol.CryptoMaxParams {
|
||||||
|
return HandshakeMessage{}, qerr.CryptoTooManyEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
index := make([]byte, nPairs*8)
|
||||||
|
if _, err := io.ReadFull(r, index); err != nil {
|
||||||
|
return HandshakeMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap := map[Tag][]byte{}
|
||||||
|
|
||||||
|
var dataStart uint32
|
||||||
|
for indexPos := 0; indexPos < int(nPairs)*8; indexPos += 8 {
|
||||||
|
tag := Tag(binary.LittleEndian.Uint32(index[indexPos : indexPos+4]))
|
||||||
|
dataEnd := binary.LittleEndian.Uint32(index[indexPos+4 : indexPos+8])
|
||||||
|
|
||||||
|
dataLen := dataEnd - dataStart
|
||||||
|
if dataLen > protocol.CryptoParameterMaxLength {
|
||||||
|
return HandshakeMessage{}, qerr.Error(qerr.CryptoInvalidValueLength, "value too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, dataLen)
|
||||||
|
if _, err := io.ReadFull(r, data); err != nil {
|
||||||
|
return HandshakeMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap[tag] = data
|
||||||
|
dataStart = dataEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandshakeMessage{
|
||||||
|
Tag: messageTag,
|
||||||
|
Data: resultMap}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes a crypto message
|
||||||
|
func (h HandshakeMessage) Write(b *bytes.Buffer) {
|
||||||
|
data := h.Data
|
||||||
|
utils.WriteUint32(b, uint32(h.Tag))
|
||||||
|
utils.WriteUint16(b, uint16(len(data)))
|
||||||
|
utils.WriteUint16(b, 0)
|
||||||
|
|
||||||
|
// Save current position in the buffer, so that we can update the index in-place later
|
||||||
|
indexStart := b.Len()
|
||||||
|
|
||||||
|
indexData := make([]byte, 8*len(data))
|
||||||
|
b.Write(indexData) // Will be updated later
|
||||||
|
|
||||||
|
offset := uint32(0)
|
||||||
|
for i, t := range h.getTagsSorted() {
|
||||||
|
v := data[Tag(t)]
|
||||||
|
b.Write(v)
|
||||||
|
offset += uint32(len(v))
|
||||||
|
binary.LittleEndian.PutUint32(indexData[i*8:], t)
|
||||||
|
binary.LittleEndian.PutUint32(indexData[i*8+4:], offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we write the index data for real
|
||||||
|
copy(b.Bytes()[indexStart:], indexData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HandshakeMessage) getTagsSorted() []uint32 {
|
||||||
|
tags := make([]uint32, len(h.Data))
|
||||||
|
i := 0
|
||||||
|
for t := range h.Data {
|
||||||
|
tags[i] = uint32(t)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort(utils.Uint32Slice(tags))
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HandshakeMessage) String() string {
|
||||||
|
var pad string
|
||||||
|
res := tagToString(h.Tag) + ":\n"
|
||||||
|
for _, t := range h.getTagsSorted() {
|
||||||
|
tag := Tag(t)
|
||||||
|
if tag == TagPAD {
|
||||||
|
pad = fmt.Sprintf("\t%s: (%d bytes)\n", tagToString(tag), len(h.Data[tag]))
|
||||||
|
} else {
|
||||||
|
res += fmt.Sprintf("\t%s: %#v\n", tagToString(tag), string(h.Data[tag]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pad) > 0 {
|
||||||
|
res += pad
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagToString(tag Tag) string {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(b, uint32(tag))
|
||||||
|
for i := range b {
|
||||||
|
if b[i] == 0 {
|
||||||
|
b[i] = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue