mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-25 02:57:24 +00:00
Introduce Makefile and go vendor directory for faster build (#67)
This commit is contained in:
parent
b4970f37c4
commit
42e281234c
1709 changed files with 521045 additions and 14 deletions
2
vendor/github.com/pion/datachannel/.gitignore
generated
vendored
Normal file
2
vendor/github.com/pion/datachannel/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# vim temporary files
|
||||
*.sw[poe]
|
||||
11
vendor/github.com/pion/datachannel/.golangci.yml
generated
vendored
Normal file
11
vendor/github.com/pion/datachannel/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
24
vendor/github.com/pion/datachannel/.travis.yml
generated
vendored
Normal file
24
vendor/github.com/pion/datachannel/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.x" # use the latest Go release
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
# slack:
|
||||
# secure: TODO
|
||||
|
||||
before_script:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.13
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- golangci-lint run ./...
|
||||
- go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
|
||||
- goveralls -coverprofile=cover.out -service=travis-ci
|
||||
- bash .github/assert-contributors.sh
|
||||
- bash .github/lint-disallowed-functions-in-library.sh
|
||||
- bash .github/lint-commit-message.sh
|
||||
20
vendor/github.com/pion/datachannel/DESIGN.md
generated
vendored
Normal file
20
vendor/github.com/pion/datachannel/DESIGN.md
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<h1 align="center">
|
||||
Design
|
||||
</h1>
|
||||
|
||||
### Portable
|
||||
Pion Data Channels is written in Go and extremely portable. Anywhere Golang runs, Pion Data Channels should work as well! Instead of dealing with complicated
|
||||
cross-compiling of multiple libraries, you now can run anywhere with one `go build`
|
||||
|
||||
### Simple API
|
||||
The API is based on an io.ReadWriteCloser.
|
||||
|
||||
### Readable
|
||||
If code comes from an RFC we try to make sure everything is commented with a link to the spec.
|
||||
This makes learning and debugging easier, this library was written to also serve as a guide for others.
|
||||
|
||||
### Tested
|
||||
Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
|
||||
|
||||
### Shared libraries
|
||||
Every pion product is built using shared libraries, allowing others to review and reuse our libraries.
|
||||
21
vendor/github.com/pion/datachannel/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pion/datachannel/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
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.
|
||||
42
vendor/github.com/pion/datachannel/README.md
generated
vendored
Normal file
42
vendor/github.com/pion/datachannel/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<h1 align="center">
|
||||
<br>
|
||||
Pion Data Channels
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of WebRTC Data Channels</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-datachannel-gray.svg?longCache=true&colorB=brightgreen" alt="Pion Data Channels"></a>
|
||||
<!--<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>-->
|
||||
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/pion/datachannel"><img src="https://travis-ci.org/pion/datachannel.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://godoc.org/github.com/pion/datachannel"><img src="https://godoc.org/github.com/pion/datachannel?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://coveralls.io/github/pion/datachannel"><img src="https://coveralls.io/repos/github/pion/datachannel/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/datachannel"><img src="https://goreportcard.com/badge/github.com/pion/datachannel" alt="Go Report Card"></a>
|
||||
<!--<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
|
||||
|
||||
### Roadmap
|
||||
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||
|
||||
### Community
|
||||
Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
|
||||
|
||||
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||
|
||||
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||
|
||||
### Contributing
|
||||
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
|
||||
|
||||
* [John Bradley](https://github.com/kc5nra) - *Original Author*
|
||||
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
|
||||
* [Michiel De Backker](https://github.com/backkem) - *Public API*
|
||||
* [Yutaka Takeda](https://github.com/enobufs) - *PR-SCTP*
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
318
vendor/github.com/pion/datachannel/datachannel.go
generated
Normal file
318
vendor/github.com/pion/datachannel/datachannel.go
generated
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
package datachannel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/sctp"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const receiveMTU = 8192
|
||||
|
||||
// Reader is an extended io.Reader
|
||||
// that also returns if the message is text.
|
||||
type Reader interface {
|
||||
ReadDataChannel([]byte) (int, bool, error)
|
||||
}
|
||||
|
||||
// Writer is an extended io.Writer
|
||||
// that also allows indicating if a message is text.
|
||||
type Writer interface {
|
||||
WriteDataChannel([]byte, bool) (int, error)
|
||||
}
|
||||
|
||||
// ReadWriteCloser is an extended io.ReadWriteCloser
|
||||
// that also implements our Reader and Writer.
|
||||
type ReadWriteCloser interface {
|
||||
io.Reader
|
||||
io.Writer
|
||||
Reader
|
||||
Writer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// DataChannel represents a data channel
|
||||
type DataChannel struct {
|
||||
Config
|
||||
stream *sctp.Stream
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// Config is used to configure the data channel.
|
||||
type Config struct {
|
||||
ChannelType ChannelType
|
||||
Priority uint16
|
||||
ReliabilityParameter uint32
|
||||
Label string
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
func newDataChannel(stream *sctp.Stream, config *Config) (*DataChannel, error) {
|
||||
switch config.ChannelType {
|
||||
case ChannelTypeReliable:
|
||||
stream.SetReliabilityParams(false, sctp.ReliabilityTypeReliable, config.ReliabilityParameter)
|
||||
case ChannelTypeReliableUnordered:
|
||||
stream.SetReliabilityParams(true, sctp.ReliabilityTypeReliable, config.ReliabilityParameter)
|
||||
case ChannelTypePartialReliableRexmit:
|
||||
stream.SetReliabilityParams(false, sctp.ReliabilityTypeRexmit, config.ReliabilityParameter)
|
||||
case ChannelTypePartialReliableRexmitUnordered:
|
||||
stream.SetReliabilityParams(true, sctp.ReliabilityTypeRexmit, config.ReliabilityParameter)
|
||||
case ChannelTypePartialReliableTimed:
|
||||
stream.SetReliabilityParams(false, sctp.ReliabilityTypeTimed, config.ReliabilityParameter)
|
||||
case ChannelTypePartialReliableTimedUnordered:
|
||||
stream.SetReliabilityParams(true, sctp.ReliabilityTypeTimed, config.ReliabilityParameter)
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to create datachannel, invalid ChannelType: %v ", config.ChannelType)
|
||||
}
|
||||
|
||||
return &DataChannel{
|
||||
Config: *config,
|
||||
stream: stream,
|
||||
log: config.LoggerFactory.NewLogger("datachannel"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Dial opens a data channels over SCTP
|
||||
func Dial(a *sctp.Association, id uint16, config *Config) (*DataChannel, error) {
|
||||
stream, err := a.OpenStream(id, sctp.PayloadTypeWebRTCBinary)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dc, err := Client(stream, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
// Client opens a data channel over an SCTP stream
|
||||
func Client(stream *sctp.Stream, config *Config) (*DataChannel, error) {
|
||||
msg := &channelOpen{
|
||||
ChannelType: config.ChannelType,
|
||||
Priority: config.Priority,
|
||||
ReliabilityParameter: config.ReliabilityParameter,
|
||||
|
||||
Label: []byte(config.Label),
|
||||
Protocol: []byte(""),
|
||||
}
|
||||
|
||||
rawMsg, err := msg.Marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal ChannelOpen %v", err)
|
||||
}
|
||||
|
||||
_, err = stream.WriteSCTP(rawMsg, sctp.PayloadTypeWebRTCDCEP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send ChannelOpen %v", err)
|
||||
}
|
||||
|
||||
return newDataChannel(stream, config)
|
||||
}
|
||||
|
||||
// Accept is used to accept incoming data channels over SCTP
|
||||
func Accept(a *sctp.Association, config *Config) (*DataChannel, error) {
|
||||
stream, err := a.AcceptStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
|
||||
|
||||
dc, err := Server(stream, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
// Server accepts a data channel over an SCTP stream
|
||||
func Server(stream *sctp.Stream, config *Config) (*DataChannel, error) {
|
||||
buffer := make([]byte, receiveMTU) // TODO: Can probably be smaller
|
||||
n, ppi, err := stream.ReadSCTP(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ppi != sctp.PayloadTypeWebRTCDCEP {
|
||||
return nil, fmt.Errorf("unexpected packet type: %s", ppi)
|
||||
}
|
||||
|
||||
openMsg, err := parseExpectDataChannelOpen(buffer[:n])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse DataChannelOpen packet")
|
||||
}
|
||||
|
||||
config.ChannelType = openMsg.ChannelType
|
||||
config.Priority = openMsg.Priority
|
||||
config.ReliabilityParameter = openMsg.ReliabilityParameter
|
||||
config.Label = string(openMsg.Label)
|
||||
|
||||
dataChannel, err := newDataChannel(stream, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dataChannel.writeDataChannelAck()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dataChannel, nil
|
||||
|
||||
}
|
||||
|
||||
// Read reads a packet of len(p) bytes as binary data
|
||||
func (c *DataChannel) Read(p []byte) (int, error) {
|
||||
n, _, err := c.ReadDataChannel(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadDataChannel reads a packet of len(p) bytes
|
||||
func (c *DataChannel) ReadDataChannel(p []byte) (int, bool, error) {
|
||||
for {
|
||||
n, ppi, err := c.stream.ReadSCTP(p)
|
||||
if err == io.EOF {
|
||||
// When the peer sees that an incoming stream was
|
||||
// reset, it also resets its corresponding outgoing stream.
|
||||
closeErr := c.stream.Close()
|
||||
if closeErr != nil {
|
||||
return 0, false, closeErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
var isString bool
|
||||
switch ppi {
|
||||
case sctp.PayloadTypeWebRTCDCEP:
|
||||
err = c.handleDCEP(p[:n])
|
||||
if err != nil {
|
||||
c.log.Errorf("Failed to handle DCEP: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
continue
|
||||
case sctp.PayloadTypeWebRTCString, sctp.PayloadTypeWebRTCStringEmpty:
|
||||
isString = true
|
||||
}
|
||||
return n, isString, err
|
||||
}
|
||||
}
|
||||
|
||||
// StreamIdentifier returns the Stream identifier associated to the stream.
|
||||
func (c *DataChannel) StreamIdentifier() uint16 {
|
||||
return c.stream.StreamIdentifier()
|
||||
}
|
||||
|
||||
func (c *DataChannel) handleDCEP(data []byte) error {
|
||||
msg, err := parse(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to parse DataChannel packet")
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *channelOpen:
|
||||
err = c.writeDataChannelAck()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to ACK channel open: %v", err)
|
||||
}
|
||||
// TODO: Should not happen?
|
||||
|
||||
case *channelAck:
|
||||
// TODO: handle ChannelAck (https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-5.2)
|
||||
// TODO: handle?
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unhandled DataChannel message %v", msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p as binary data
|
||||
func (c *DataChannel) Write(p []byte) (n int, err error) {
|
||||
return c.WriteDataChannel(p, false)
|
||||
}
|
||||
|
||||
// WriteDataChannel writes len(p) bytes from p
|
||||
func (c *DataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) {
|
||||
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-12#section-6.6
|
||||
// SCTP does not support the sending of empty user messages. Therefore,
|
||||
// if an empty message has to be sent, the appropriate PPID (WebRTC
|
||||
// String Empty or WebRTC Binary Empty) is used and the SCTP user
|
||||
// message of one zero byte is sent. When receiving an SCTP user
|
||||
// message with one of these PPIDs, the receiver MUST ignore the SCTP
|
||||
// user message and process it as an empty message.
|
||||
var ppi sctp.PayloadProtocolIdentifier
|
||||
switch {
|
||||
case !isString && len(p) > 0:
|
||||
ppi = sctp.PayloadTypeWebRTCBinary
|
||||
case !isString && len(p) == 0:
|
||||
ppi = sctp.PayloadTypeWebRTCBinaryEmpty
|
||||
case isString && len(p) > 0:
|
||||
ppi = sctp.PayloadTypeWebRTCString
|
||||
case isString && len(p) == 0:
|
||||
ppi = sctp.PayloadTypeWebRTCStringEmpty
|
||||
}
|
||||
|
||||
return c.stream.WriteSCTP(p, ppi)
|
||||
}
|
||||
|
||||
func (c *DataChannel) writeDataChannelAck() error {
|
||||
ack := channelAck{}
|
||||
ackMsg, err := ack.Marshal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal ChannelOpen ACK: %v", err)
|
||||
}
|
||||
|
||||
_, err = c.stream.WriteSCTP(ackMsg, sctp.PayloadTypeWebRTCDCEP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send ChannelOpen ACK: %v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the DataChannel and the underlying SCTP stream.
|
||||
func (c *DataChannel) Close() error {
|
||||
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7
|
||||
// Closing of a data channel MUST be signaled by resetting the
|
||||
// corresponding outgoing streams [RFC6525]. This means that if one
|
||||
// side decides to close the data channel, it resets the corresponding
|
||||
// outgoing stream. When the peer sees that an incoming stream was
|
||||
// reset, it also resets its corresponding outgoing stream. Once this
|
||||
// is completed, the data channel is closed. Resetting a stream sets
|
||||
// the Stream Sequence Numbers (SSNs) of the stream back to 'zero' with
|
||||
// a corresponding notification to the application layer that the reset
|
||||
// has been performed. Streams are available for reuse after a reset
|
||||
// has been performed.
|
||||
return c.stream.Close()
|
||||
}
|
||||
|
||||
// BufferedAmount returns the number of bytes of data currently queued to be
|
||||
// sent over this stream.
|
||||
func (c *DataChannel) BufferedAmount() uint64 {
|
||||
return c.stream.BufferedAmount()
|
||||
}
|
||||
|
||||
// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing
|
||||
// data that is considered "low." Defaults to 0.
|
||||
func (c *DataChannel) BufferedAmountLowThreshold() uint64 {
|
||||
return c.stream.BufferedAmountLowThreshold()
|
||||
}
|
||||
|
||||
// SetBufferedAmountLowThreshold is used to update the threshold.
|
||||
// See BufferedAmountLowThreshold().
|
||||
func (c *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
|
||||
c.stream.SetBufferedAmountLowThreshold(th)
|
||||
}
|
||||
|
||||
// OnBufferedAmountLow sets the callback handler which would be called when the
|
||||
// number of bytes of outgoing data buffered is lower than the threshold.
|
||||
func (c *DataChannel) OnBufferedAmountLow(f func()) {
|
||||
c.stream.OnBufferedAmountLow(f)
|
||||
}
|
||||
10
vendor/github.com/pion/datachannel/go.mod
generated
vendored
Normal file
10
vendor/github.com/pion/datachannel/go.mod
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
module github.com/pion/datachannel
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pion/logging v0.2.1
|
||||
github.com/pion/sctp v1.6.3
|
||||
github.com/pion/transport v0.7.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
)
|
||||
19
vendor/github.com/pion/datachannel/go.sum
generated
vendored
Normal file
19
vendor/github.com/pion/datachannel/go.sum
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pion/logging v0.2.1 h1:LwASkBKZ+2ysGJ+jLv1E/9H1ge0k1nTfi1X+5zirkDk=
|
||||
github.com/pion/logging v0.2.1/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/sctp v1.6.3 h1:SC4vKOjcddK8tXiTNj05a+0/GyPpCmuNfeBA/rzNFqs=
|
||||
github.com/pion/sctp v1.6.3/go.mod h1:cCqpLdYvgEUdl715+qbWtgT439CuQrAgy8BZTp0aEfA=
|
||||
github.com/pion/transport v0.7.0 h1:EsXN8TglHMlKZMo4ZGqwK6QgXBu0WYg7wfGMWIXsS+w=
|
||||
github.com/pion/transport v0.7.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
94
vendor/github.com/pion/datachannel/message.go
generated
Normal file
94
vendor/github.com/pion/datachannel/message.go
generated
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package datachannel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// message is a parsed DataChannel message
|
||||
type message interface {
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal([]byte) error
|
||||
}
|
||||
|
||||
// messageType is the first byte in a DataChannel message that specifies type
|
||||
type messageType byte
|
||||
|
||||
// DataChannel Message Types
|
||||
const (
|
||||
dataChannelAck messageType = 0x02
|
||||
dataChannelOpen messageType = 0x03
|
||||
)
|
||||
|
||||
func (t messageType) String() string {
|
||||
switch t {
|
||||
case dataChannelAck:
|
||||
return "DataChannelAck"
|
||||
case dataChannelOpen:
|
||||
return "DataChannelOpen"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown MessageType: %d", t)
|
||||
}
|
||||
}
|
||||
|
||||
// parse accepts raw input and returns a DataChannel message
|
||||
func parse(raw []byte) (message, error) {
|
||||
if len(raw) == 0 {
|
||||
return nil, errors.Errorf("DataChannel message is not long enough to determine type ")
|
||||
}
|
||||
|
||||
var msg message
|
||||
switch messageType(raw[0]) {
|
||||
case dataChannelOpen:
|
||||
msg = &channelOpen{}
|
||||
case dataChannelAck:
|
||||
msg = &channelAck{}
|
||||
default:
|
||||
return nil, errors.Errorf("Unknown MessageType %v", messageType(raw[0]))
|
||||
}
|
||||
|
||||
if err := msg.Unmarshal(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// parseExpectDataChannelOpen parses a DataChannelOpen message
|
||||
// or throws an error
|
||||
func parseExpectDataChannelOpen(raw []byte) (*channelOpen, error) {
|
||||
if len(raw) == 0 {
|
||||
return nil, errors.Errorf("the DataChannel message is not long enough to determine type")
|
||||
}
|
||||
|
||||
if actualTyp := messageType(raw[0]); actualTyp != dataChannelOpen {
|
||||
return nil, errors.Errorf("expected DataChannelOpen but got %s", actualTyp)
|
||||
}
|
||||
|
||||
msg := &channelOpen{}
|
||||
if err := msg.Unmarshal(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// parseExpectDataChannelAck parses a DataChannelAck message
|
||||
// or throws an error
|
||||
// func parseExpectDataChannelAck(raw []byte) (*channelAck, error) {
|
||||
// if len(raw) == 0 {
|
||||
// return nil, errors.Errorf("the DataChannel message is not long enough to determine type")
|
||||
// }
|
||||
//
|
||||
// if actualTyp := messageType(raw[0]); actualTyp != dataChannelAck {
|
||||
// return nil, errors.Errorf("expected DataChannelAck but got %s", actualTyp)
|
||||
// }
|
||||
//
|
||||
// msg := &channelAck{}
|
||||
// if err := msg.Unmarshal(raw); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return msg, nil
|
||||
// }
|
||||
22
vendor/github.com/pion/datachannel/message_channel_ack.go
generated
Normal file
22
vendor/github.com/pion/datachannel/message_channel_ack.go
generated
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package datachannel
|
||||
|
||||
// channelAck is used to ACK a DataChannel open
|
||||
type channelAck struct{}
|
||||
|
||||
const (
|
||||
channelOpenAckLength = 4
|
||||
)
|
||||
|
||||
// Marshal returns raw bytes for the given message
|
||||
func (c *channelAck) Marshal() ([]byte, error) {
|
||||
raw := make([]byte, channelOpenAckLength)
|
||||
raw[0] = uint8(dataChannelAck)
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the struct with the given raw data
|
||||
func (c *channelAck) Unmarshal(raw []byte) error {
|
||||
// Message type already checked in Parse and there is no further data
|
||||
return nil
|
||||
}
|
||||
123
vendor/github.com/pion/datachannel/message_channel_open.go
generated
Normal file
123
vendor/github.com/pion/datachannel/message_channel_open.go
generated
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package datachannel
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
/*
|
||||
channelOpen represents a DATA_CHANNEL_OPEN Message
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Message Type | Channel Type | Priority |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Reliability Parameter |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Label Length | Protocol Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Label |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Protocol |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type channelOpen struct {
|
||||
ChannelType ChannelType
|
||||
Priority uint16
|
||||
ReliabilityParameter uint32
|
||||
|
||||
Label []byte
|
||||
Protocol []byte
|
||||
}
|
||||
|
||||
const (
|
||||
channelOpenHeaderLength = 12
|
||||
)
|
||||
|
||||
// ChannelType determines the reliability of the WebRTC DataChannel
|
||||
type ChannelType byte
|
||||
|
||||
// ChannelType enums
|
||||
const (
|
||||
// ChannelTypeReliable determines the Data Channel provides a
|
||||
// reliable in-order bi-directional communication.
|
||||
ChannelTypeReliable ChannelType = 0x00
|
||||
// ChannelTypeReliableUnordered determines the Data Channel
|
||||
// provides a reliable unordered bi-directional communication.
|
||||
ChannelTypeReliableUnordered ChannelType = 0x80
|
||||
// ChannelTypePartialReliableRexmit determines the Data Channel
|
||||
// provides a partially-reliable in-order bi-directional communication.
|
||||
// User messages will not be retransmitted more times than specified in the Reliability Parameter.
|
||||
ChannelTypePartialReliableRexmit ChannelType = 0x01
|
||||
// ChannelTypePartialReliableRexmitUnordered determines
|
||||
// the Data Channel provides a partial reliable unordered bi-directional communication.
|
||||
// User messages will not be retransmitted more times than specified in the Reliability Parameter.
|
||||
ChannelTypePartialReliableRexmitUnordered ChannelType = 0x81
|
||||
// ChannelTypePartialReliableTimed determines the Data Channel
|
||||
// provides a partial reliable in-order bi-directional communication.
|
||||
// User messages might not be transmitted or retransmitted after
|
||||
// a specified life-time given in milli- seconds in the Reliability Parameter.
|
||||
// This life-time starts when providing the user message to the protocol stack.
|
||||
ChannelTypePartialReliableTimed ChannelType = 0x02
|
||||
// The Data Channel provides a partial reliable unordered bi-directional
|
||||
// communication. User messages might not be transmitted or retransmitted
|
||||
// after a specified life-time given in milli- seconds in the Reliability Parameter.
|
||||
// This life-time starts when providing the user message to the protocol stack.
|
||||
ChannelTypePartialReliableTimedUnordered ChannelType = 0x82
|
||||
)
|
||||
|
||||
// ChannelPriority enums
|
||||
const (
|
||||
ChannelPriorityBelowNormal uint16 = 128
|
||||
ChannelPriorityNormal uint16 = 256
|
||||
ChannelPriorityHigh uint16 = 512
|
||||
ChannelPriorityExtraHigh uint16 = 1024
|
||||
)
|
||||
|
||||
// Marshal returns raw bytes for the given message
|
||||
func (c *channelOpen) Marshal() ([]byte, error) {
|
||||
labelLength := len(c.Label)
|
||||
protocolLength := len(c.Protocol)
|
||||
|
||||
totalLen := channelOpenHeaderLength + labelLength + protocolLength
|
||||
raw := make([]byte, totalLen)
|
||||
|
||||
raw[0] = uint8(dataChannelOpen)
|
||||
raw[1] = byte(c.ChannelType)
|
||||
binary.BigEndian.PutUint16(raw[2:], c.Priority)
|
||||
binary.BigEndian.PutUint32(raw[4:], c.ReliabilityParameter)
|
||||
binary.BigEndian.PutUint16(raw[8:], uint16(labelLength))
|
||||
binary.BigEndian.PutUint16(raw[10:], uint16(protocolLength))
|
||||
endLabel := channelOpenHeaderLength + labelLength
|
||||
copy(raw[channelOpenHeaderLength:endLabel], c.Label)
|
||||
copy(raw[endLabel:endLabel+protocolLength], c.Protocol)
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the struct with the given raw data
|
||||
func (c *channelOpen) Unmarshal(raw []byte) error {
|
||||
if len(raw) < channelOpenHeaderLength {
|
||||
return errors.Errorf("Length of input is not long enough to satisfy header %d", len(raw))
|
||||
}
|
||||
c.ChannelType = ChannelType(raw[1])
|
||||
c.Priority = binary.BigEndian.Uint16(raw[2:])
|
||||
c.ReliabilityParameter = binary.BigEndian.Uint32(raw[4:])
|
||||
|
||||
labelLength := binary.BigEndian.Uint16(raw[8:])
|
||||
protocolLength := binary.BigEndian.Uint16(raw[10:])
|
||||
|
||||
if len(raw) != int(channelOpenHeaderLength+labelLength+protocolLength) {
|
||||
return errors.Errorf("Label + Protocol length don't match full packet length")
|
||||
}
|
||||
|
||||
c.Label = raw[channelOpenHeaderLength : channelOpenHeaderLength+labelLength]
|
||||
c.Protocol = raw[channelOpenHeaderLength+labelLength : channelOpenHeaderLength+labelLength+protocolLength]
|
||||
return nil
|
||||
}
|
||||
21
vendor/github.com/pion/dtls/.editorconfig
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/.editorconfig
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# http://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[{*.yml,*.yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles always use tabs for indentation
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
2
vendor/github.com/pion/dtls/.gitignore
generated
vendored
Normal file
2
vendor/github.com/pion/dtls/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
vendor
|
||||
*-fuzz.zip
|
||||
29
vendor/github.com/pion/dtls/.golangci.yml
generated
vendored
Normal file
29
vendor/github.com/pion/dtls/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
||||
- dupl
|
||||
- gocyclo
|
||||
- gochecknoglobals
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-per-linter: 0
|
||||
max-same-issues: 50
|
||||
|
||||
exclude-rules:
|
||||
- path: internal/crypto/ccm
|
||||
text: "L' should not be capitalized"
|
||||
linters:
|
||||
- gocritic
|
||||
- path: cipher_suite
|
||||
text: "don't use ALL_CAPS in Go names; use CamelCase"
|
||||
linters:
|
||||
- golint
|
||||
19
vendor/github.com/pion/dtls/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/pion/dtls/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.x" # use the latest Go release
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
before_script:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
|
||||
|
||||
script:
|
||||
- golangci-lint run ./...
|
||||
- rm -rf examples # Remove examples, no test coverage for them
|
||||
- go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash .github/assert-contributors.sh
|
||||
- bash .github/lint-disallowed-functions-in-library.sh
|
||||
- bash .github/lint-commit-message.sh
|
||||
21
vendor/github.com/pion/dtls/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
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.
|
||||
6
vendor/github.com/pion/dtls/Makefile
generated
vendored
Normal file
6
vendor/github.com/pion/dtls/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
fuzz-build-record-layer: fuzz-prepare
|
||||
go-fuzz-build -tags gofuzz -func FuzzRecordLayer
|
||||
fuzz-run-record-layer:
|
||||
go-fuzz -bin dtls-fuzz.zip -workdir fuzz
|
||||
fuzz-prepare:
|
||||
@GO111MODULE=on go mod vendor
|
||||
85
vendor/github.com/pion/dtls/README.md
generated
vendored
Normal file
85
vendor/github.com/pion/dtls/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<h1 align="center">
|
||||
<br>
|
||||
Pion DTLS
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of DTLS</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-dtls-gray.svg?longCache=true&colorB=brightgreen" alt="Pion DTLS"></a>
|
||||
<a href="https://sourcegraph.com/github.com/pion/dtls/pkg/dtls?badge"><img src="https://sourcegraph.com/github.com/pion/dtls/pkg/dtls/-/badge.svg" alt="Sourcegraph Widget"></a>
|
||||
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/pion/dtls"><img src="https://travis-ci.org/pion/dtls.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://godoc.org/github.com/pion/dtls"><img src="https://godoc.org/github.com/pion/dtls?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://codecov.io/gh/pion/dtls"><img src="https://codecov.io/gh/pion/dtls/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/dtls"><img src="https://goreportcard.com/badge/github.com/pion/dtls" alt="Go Report Card"></a>
|
||||
<a href="https://www.codacy.com/app/Sean-Der/dtls"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
Go DTLS 1.2 implementation. The original user is pion-WebRTC, but we would love to see it work for everyone.
|
||||
|
||||
A long term goal is a professional security review, and maye inclusion in stdlib.
|
||||
|
||||
### Goals/Progress
|
||||
This will only be targeting DTLS 1.2, and the most modern/common cipher suites.
|
||||
We would love contributes that fall under the 'Planned Features' and fixing any bugs!
|
||||
|
||||
#### Current features
|
||||
* DTLS 1.2 Client/Server
|
||||
* Forward secrecy using ECDHE; with curve25519 and nistp256 (non-PFS will not be supported)
|
||||
* AES_128_GCM, AES_256_CBC
|
||||
* Packet loss and re-ordering is handled during handshaking
|
||||
* Key export (RFC5705)
|
||||
|
||||
#### Planned Features
|
||||
* Extended master secret support (RFC7627)
|
||||
* Chacha20Poly1305
|
||||
|
||||
#### Excluded Features
|
||||
* DTLS 1.0
|
||||
* Renegotiation
|
||||
* Compression
|
||||
|
||||
### Pion DTLS
|
||||
For a DTLS 1.2 Server that listens on 127.0.0.1:4444
|
||||
```sh
|
||||
go run examples/listen/main.go
|
||||
```
|
||||
|
||||
For a DTLS 1.2 Client that connects to 127.0.0.1:4444
|
||||
```sh
|
||||
go run examples/dial/main.go
|
||||
```
|
||||
|
||||
### OpenSSL
|
||||
Pion DTLS can connect to itself and OpenSSL.
|
||||
```
|
||||
// Generate a certificate
|
||||
openssl ecparam -out key.pem -name prime256v1 -genkey
|
||||
openssl req -new -sha256 -key key.pem -out server.csr
|
||||
openssl x509 -req -sha256 -days 365 -in server.csr -signkey key.pem -out cert.pem
|
||||
|
||||
// Use with examples/dial/main.go
|
||||
openssl s_server -dtls1_2 -cert cert.pem -key key.pem -accept 4444
|
||||
|
||||
// Use with examples/listen/main.go
|
||||
openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -debug -cert cert.pem -key key.pem
|
||||
```
|
||||
|
||||
### Contributing
|
||||
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
|
||||
|
||||
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
|
||||
* [Michiel De Backker](https://github.com/backkem) - *Public API*
|
||||
* [Chris Hiszpanski](https://github.com/thinkski) - *Support Signature Algorithms Extension*
|
||||
* [Iñigo Garcia Olaizola](https://github.com/igolaizola) - *Support serialization and resumption"*
|
||||
* [Daniele Sluijters](https://github.com/daenney) - *AES-CCM support*
|
||||
* [Jin Lei](https://github.com/jinleileiking) - *Logging*
|
||||
* [Hugo Arregui](https://github.com/hugoArregui)
|
||||
* [Lander Noterman](https://github.com/LanderN)
|
||||
* [Aleksandr Razumov](https://github.com/ernado) - *Fuzzing*
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
145
vendor/github.com/pion/dtls/alert.go
generated
Normal file
145
vendor/github.com/pion/dtls/alert.go
generated
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package dtls
|
||||
|
||||
import "fmt"
|
||||
|
||||
type alertLevel byte
|
||||
|
||||
const (
|
||||
alertLevelWarning alertLevel = 1
|
||||
alertLevelFatal alertLevel = 2
|
||||
)
|
||||
|
||||
func (a alertLevel) String() string {
|
||||
switch a {
|
||||
case alertLevelWarning:
|
||||
return "LevelWarning"
|
||||
case alertLevelFatal:
|
||||
return "LevelFatal"
|
||||
default:
|
||||
return "Invalid alert level"
|
||||
}
|
||||
}
|
||||
|
||||
type alertDescription byte
|
||||
|
||||
const (
|
||||
alertCloseNotify alertDescription = 0
|
||||
alertUnexpectedMessage alertDescription = 10
|
||||
alertBadRecordMac alertDescription = 20
|
||||
alertDecryptionFailed alertDescription = 21
|
||||
alertRecordOverflow alertDescription = 22
|
||||
alertDecompressionFailure alertDescription = 30
|
||||
alertHandshakeFailure alertDescription = 40
|
||||
alertNoCertificate alertDescription = 41
|
||||
alertBadCertificate alertDescription = 42
|
||||
alertUnsupportedCertificate alertDescription = 43
|
||||
alertCertificateRevoked alertDescription = 44
|
||||
alertCertificateExpired alertDescription = 45
|
||||
alertCertificateUnknown alertDescription = 46
|
||||
alertIllegalParameter alertDescription = 47
|
||||
alertUnknownCA alertDescription = 48
|
||||
alertAccessDenied alertDescription = 49
|
||||
alertDecodeError alertDescription = 50
|
||||
alertDecryptError alertDescription = 51
|
||||
alertExportRestriction alertDescription = 60
|
||||
alertProtocolVersion alertDescription = 70
|
||||
alertInsufficientSecurity alertDescription = 71
|
||||
alertInternalError alertDescription = 80
|
||||
alertUserCanceled alertDescription = 90
|
||||
alertNoRenegotiation alertDescription = 100
|
||||
alertUnsupportedExtension alertDescription = 110
|
||||
)
|
||||
|
||||
func (a alertDescription) String() string {
|
||||
switch a {
|
||||
case alertCloseNotify:
|
||||
return "CloseNotify"
|
||||
case alertUnexpectedMessage:
|
||||
return "UnexpectedMessage"
|
||||
case alertBadRecordMac:
|
||||
return "BadRecordMac"
|
||||
case alertDecryptionFailed:
|
||||
return "DecryptionFailed"
|
||||
case alertRecordOverflow:
|
||||
return "RecordOverflow"
|
||||
case alertDecompressionFailure:
|
||||
return "DecompressionFailure"
|
||||
case alertHandshakeFailure:
|
||||
return "HandshakeFailure"
|
||||
case alertNoCertificate:
|
||||
return "NoCertificate"
|
||||
case alertBadCertificate:
|
||||
return "BadCertificate"
|
||||
case alertUnsupportedCertificate:
|
||||
return "UnsupportedCertificate"
|
||||
case alertCertificateRevoked:
|
||||
return "CertificateRevoked"
|
||||
case alertCertificateExpired:
|
||||
return "CertificateExpired"
|
||||
case alertCertificateUnknown:
|
||||
return "CertificateUnknown"
|
||||
case alertIllegalParameter:
|
||||
return "IllegalParameter"
|
||||
case alertUnknownCA:
|
||||
return "UnknownCA"
|
||||
case alertAccessDenied:
|
||||
return "AccessDenied"
|
||||
case alertDecodeError:
|
||||
return "DecodeError"
|
||||
case alertDecryptError:
|
||||
return "DecryptError"
|
||||
case alertExportRestriction:
|
||||
return "ExportRestriction"
|
||||
case alertProtocolVersion:
|
||||
return "ProtocolVersion"
|
||||
case alertInsufficientSecurity:
|
||||
return "InsufficientSecurity"
|
||||
case alertInternalError:
|
||||
return "InternalError"
|
||||
case alertUserCanceled:
|
||||
return "UserCanceled"
|
||||
case alertNoRenegotiation:
|
||||
return "NoRenegotiation"
|
||||
case alertUnsupportedExtension:
|
||||
return "UnsupportedExtension"
|
||||
default:
|
||||
return "Invalid alert description"
|
||||
}
|
||||
}
|
||||
|
||||
// One of the content types supported by the TLS record layer is the
|
||||
// alert type. Alert messages convey the severity of the message
|
||||
// (warning or fatal) and a description of the alert. Alert messages
|
||||
// with a level of fatal result in the immediate termination of the
|
||||
// connection. In this case, other connections corresponding to the
|
||||
// session may continue, but the session identifier MUST be invalidated,
|
||||
// preventing the failed session from being used to establish new
|
||||
// connections. Like other messages, alert messages are encrypted and
|
||||
// compressed, as specified by the current connection state.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.2
|
||||
type alert struct {
|
||||
alertLevel alertLevel
|
||||
alertDescription alertDescription
|
||||
}
|
||||
|
||||
func (a alert) contentType() contentType {
|
||||
return contentTypeAlert
|
||||
}
|
||||
|
||||
func (a *alert) Marshal() ([]byte, error) {
|
||||
return []byte{byte(a.alertLevel), byte(a.alertDescription)}, nil
|
||||
}
|
||||
|
||||
func (a *alert) Unmarshal(data []byte) error {
|
||||
if len(data) != 2 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
a.alertLevel = alertLevel(data[0])
|
||||
a.alertDescription = alertDescription(data[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *alert) String() string {
|
||||
return fmt.Sprintf("Alert %s: %s", a.alertLevel, a.alertDescription)
|
||||
}
|
||||
23
vendor/github.com/pion/dtls/application_data.go
generated
Normal file
23
vendor/github.com/pion/dtls/application_data.go
generated
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package dtls
|
||||
|
||||
// Application data messages are carried by the record layer and are
|
||||
// fragmented, compressed, and encrypted based on the current connection
|
||||
// state. The messages are treated as transparent data to the record
|
||||
// layer.
|
||||
// https://tools.ietf.org/html/rfc5246#section-10
|
||||
type applicationData struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (a applicationData) contentType() contentType {
|
||||
return contentTypeApplicationData
|
||||
}
|
||||
|
||||
func (a *applicationData) Marshal() ([]byte, error) {
|
||||
return append([]byte{}, a.data...), nil
|
||||
}
|
||||
|
||||
func (a *applicationData) Unmarshal(data []byte) error {
|
||||
a.data = append([]byte{}, data...)
|
||||
return nil
|
||||
}
|
||||
25
vendor/github.com/pion/dtls/change_cipher_spec.go
generated
Normal file
25
vendor/github.com/pion/dtls/change_cipher_spec.go
generated
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package dtls
|
||||
|
||||
// The change cipher spec protocol exists to signal transitions in
|
||||
// ciphering strategies. The protocol consists of a single message,
|
||||
// which is encrypted and compressed under the current (not the pending)
|
||||
// connection state. The message consists of a single byte of value 1.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.1
|
||||
type changeCipherSpec struct {
|
||||
}
|
||||
|
||||
func (c changeCipherSpec) contentType() contentType {
|
||||
return contentTypeChangeCipherSpec
|
||||
}
|
||||
|
||||
func (c *changeCipherSpec) Marshal() ([]byte, error) {
|
||||
return []byte{0x01}, nil
|
||||
}
|
||||
|
||||
func (c *changeCipherSpec) Unmarshal(data []byte) error {
|
||||
if len(data) == 1 && data[0] == 0x01 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errInvalidCipherSpec
|
||||
}
|
||||
135
vendor/github.com/pion/dtls/cipher_suite.go
generated
Normal file
135
vendor/github.com/pion/dtls/cipher_suite.go
generated
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// CipherSuiteID is an ID for our supported CipherSuites
|
||||
type CipherSuiteID uint16
|
||||
|
||||
// Supported Cipher Suites
|
||||
const (
|
||||
// AES-128-GCM-SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = 0xc02b
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = 0xc02f
|
||||
|
||||
// AES-256-CBC-SHA
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = 0x0035
|
||||
)
|
||||
|
||||
type cipherSuite interface {
|
||||
String() string
|
||||
ID() CipherSuiteID
|
||||
certificateType() clientCertificateType
|
||||
hashFunc() func() hash.Hash
|
||||
isPSK() bool
|
||||
|
||||
// Generate the internal encryption state
|
||||
init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error
|
||||
|
||||
encrypt(pkt *recordLayer, raw []byte) ([]byte, error)
|
||||
decrypt(in []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||
// A cipherSuite is a specific combination of key agreement, cipher and MAC
|
||||
// function.
|
||||
func cipherSuiteForID(id CipherSuiteID) cipherSuite {
|
||||
switch id {
|
||||
case cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256{}.ID():
|
||||
return &cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256{}
|
||||
case cipherSuiteTLSEcdheRsaWithAes128GcmSha256{}.ID():
|
||||
return &cipherSuiteTLSEcdheRsaWithAes128GcmSha256{}
|
||||
case cipherSuiteTLSEcdheEcdsaWithAes256CbcSha{}.ID():
|
||||
return &cipherSuiteTLSEcdheEcdsaWithAes256CbcSha{}
|
||||
case cipherSuiteTLSEcdheRsaWithAes256CbcSha{}.ID():
|
||||
return &cipherSuiteTLSEcdheRsaWithAes256CbcSha{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CipherSuites we support in order of preference
|
||||
func defaultCipherSuites() []cipherSuite {
|
||||
return []cipherSuite{
|
||||
&cipherSuiteTLSEcdheRsaWithAes256CbcSha{},
|
||||
&cipherSuiteTLSEcdheEcdsaWithAes256CbcSha{},
|
||||
&cipherSuiteTLSEcdheRsaWithAes128GcmSha256{},
|
||||
&cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256{},
|
||||
}
|
||||
}
|
||||
|
||||
func decodeCipherSuites(buf []byte) ([]cipherSuite, error) {
|
||||
if len(buf) < 2 {
|
||||
return nil, errDTLSPacketInvalidLength
|
||||
}
|
||||
cipherSuitesCount := int(binary.BigEndian.Uint16(buf[0:])) / 2
|
||||
rtrn := []cipherSuite{}
|
||||
for i := 0; i < cipherSuitesCount; i++ {
|
||||
if len(buf) < (i*2 + 4) {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
id := CipherSuiteID(binary.BigEndian.Uint16(buf[(i*2)+2:]))
|
||||
if c := cipherSuiteForID(id); c != nil {
|
||||
rtrn = append(rtrn, c)
|
||||
}
|
||||
}
|
||||
return rtrn, nil
|
||||
}
|
||||
|
||||
func encodeCipherSuites(c []cipherSuite) []byte {
|
||||
out := []byte{0x00, 0x00}
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(c)*2))
|
||||
for i := len(c); i > 0; i-- {
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(c[i-1].ID()))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func parseCipherSuites(userSelectedSuites []CipherSuiteID, excludePSK, excludeNonPSK bool) ([]cipherSuite, error) {
|
||||
cipherSuitesForIDs := func(ids []CipherSuiteID) ([]cipherSuite, error) {
|
||||
cipherSuites := []cipherSuite{}
|
||||
for _, id := range ids {
|
||||
c := cipherSuiteForID(id)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("CipherSuite with id(%d) is not valid", id)
|
||||
}
|
||||
cipherSuites = append(cipherSuites, c)
|
||||
}
|
||||
return cipherSuites, nil
|
||||
}
|
||||
|
||||
var (
|
||||
cipherSuites []cipherSuite
|
||||
err error
|
||||
i int
|
||||
)
|
||||
if len(userSelectedSuites) != 0 {
|
||||
cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
cipherSuites = defaultCipherSuites()
|
||||
}
|
||||
|
||||
for _, c := range cipherSuites {
|
||||
if excludePSK && c.isPSK() || excludeNonPSK && !c.isPSK() {
|
||||
continue
|
||||
}
|
||||
cipherSuites[i] = c
|
||||
i++
|
||||
}
|
||||
|
||||
cipherSuites = cipherSuites[:i]
|
||||
if len(cipherSuites) == 0 {
|
||||
return nil, errNoAvailableCipherSuites
|
||||
}
|
||||
|
||||
return cipherSuites, nil
|
||||
}
|
||||
68
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
generated
Normal file
68
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
generated
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
type cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256 struct {
|
||||
gcm *cryptoGCM
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) certificateType() clientCertificateType {
|
||||
return clientCertificateTypeECDSASign
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) ID() CipherSuiteID {
|
||||
return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) String() string {
|
||||
return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) hashFunc() func() hash.Hash {
|
||||
return sha256.New
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) isPSK() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||
const (
|
||||
prfMacLen = 0
|
||||
prfKeyLen = 16
|
||||
prfIvLen = 4
|
||||
)
|
||||
|
||||
keys, err := prfEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isClient {
|
||||
c.gcm, err = newCryptoGCM(keys.clientWriteKey, keys.clientWriteIV, keys.serverWriteKey, keys.serverWriteIV)
|
||||
} else {
|
||||
c.gcm, err = newCryptoGCM(keys.serverWriteKey, keys.serverWriteIV, keys.clientWriteKey, keys.clientWriteIV)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
|
||||
if c.gcm == nil {
|
||||
return nil, errors.New("CipherSuite has not been initalized, unable to encrypt")
|
||||
}
|
||||
|
||||
return c.gcm.encrypt(pkt, raw)
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256) decrypt(raw []byte) ([]byte, error) {
|
||||
if c.gcm == nil {
|
||||
return nil, errors.New("CipherSuite has not been initalized, unable to decrypt ")
|
||||
}
|
||||
|
||||
return c.gcm.decrypt(raw)
|
||||
}
|
||||
74
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
generated
Normal file
74
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
generated
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
type cipherSuiteTLSEcdheEcdsaWithAes256CbcSha struct {
|
||||
cbc *cryptoCBC
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) certificateType() clientCertificateType {
|
||||
return clientCertificateTypeECDSASign
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) ID() CipherSuiteID {
|
||||
return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) String() string {
|
||||
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) hashFunc() func() hash.Hash {
|
||||
return sha256.New
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) isPSK() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||
const (
|
||||
prfMacLen = 20
|
||||
prfKeyLen = 32
|
||||
prfIvLen = 16
|
||||
)
|
||||
|
||||
keys, err := prfEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isClient {
|
||||
c.cbc, err = newCryptoCBC(
|
||||
keys.clientWriteKey, keys.clientWriteIV, keys.clientMACKey,
|
||||
keys.serverWriteKey, keys.serverWriteIV, keys.serverMACKey,
|
||||
)
|
||||
} else {
|
||||
c.cbc, err = newCryptoCBC(
|
||||
keys.serverWriteKey, keys.serverWriteIV, keys.serverMACKey,
|
||||
keys.clientWriteKey, keys.clientWriteIV, keys.clientMACKey,
|
||||
)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
|
||||
if c.cbc == nil {
|
||||
return nil, errors.New("CipherSuite has not been initalized, unable to encrypt")
|
||||
}
|
||||
|
||||
return c.cbc.encrypt(pkt, raw)
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLSEcdheEcdsaWithAes256CbcSha) decrypt(raw []byte) ([]byte, error) {
|
||||
if c.cbc == nil {
|
||||
return nil, errors.New("CipherSuite has not been initalized, unable to decrypt ")
|
||||
}
|
||||
|
||||
return c.cbc.decrypt(raw)
|
||||
}
|
||||
17
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
generated
Normal file
17
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package dtls
|
||||
|
||||
type cipherSuiteTLSEcdheRsaWithAes128GcmSha256 struct {
|
||||
cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes128GcmSha256) certificateType() clientCertificateType {
|
||||
return clientCertificateTypeRSASign
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes128GcmSha256) ID() CipherSuiteID {
|
||||
return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes128GcmSha256) String() string {
|
||||
return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
}
|
||||
17
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_rsa_with_aes_256_cbc_sha.go
generated
Normal file
17
vendor/github.com/pion/dtls/cipher_suite_tls_ecdhe_rsa_with_aes_256_cbc_sha.go
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package dtls
|
||||
|
||||
type cipherSuiteTLSEcdheRsaWithAes256CbcSha struct {
|
||||
cipherSuiteTLSEcdheEcdsaWithAes256CbcSha
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes256CbcSha) certificateType() clientCertificateType {
|
||||
return clientCertificateTypeRSASign
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes256CbcSha) ID() CipherSuiteID {
|
||||
return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
||||
}
|
||||
|
||||
func (c cipherSuiteTLSEcdheRsaWithAes256CbcSha) String() string {
|
||||
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
|
||||
}
|
||||
14
vendor/github.com/pion/dtls/client_certificate_type.go
generated
Normal file
14
vendor/github.com/pion/dtls/client_certificate_type.go
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package dtls
|
||||
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
|
||||
type clientCertificateType byte
|
||||
|
||||
const (
|
||||
clientCertificateTypeRSASign clientCertificateType = 1
|
||||
clientCertificateTypeECDSASign clientCertificateType = 64
|
||||
)
|
||||
|
||||
var clientCertificateTypes = map[clientCertificateType]bool{
|
||||
clientCertificateTypeRSASign: true,
|
||||
clientCertificateTypeECDSASign: true,
|
||||
}
|
||||
377
vendor/github.com/pion/dtls/client_handlers.go
generated
Normal file
377
vendor/github.com/pion/dtls/client_handlers.go
generated
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func clientHandshakeHandler(c *Conn) error {
|
||||
handleSingleHandshake := func(buf []byte) error {
|
||||
rawHandshake := &handshake{}
|
||||
if err := rawHandshake.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.log.Tracef("[handshake] <- %s", rawHandshake.handshakeMessage.handshakeType().String())
|
||||
switch h := rawHandshake.handshakeMessage.(type) {
|
||||
case *handshakeMessageHelloVerifyRequest:
|
||||
c.cookie = append([]byte{}, h.cookie...)
|
||||
|
||||
case *handshakeMessageServerHello:
|
||||
for _, extension := range h.extensions {
|
||||
if e, ok := extension.(*extensionUseSRTP); ok {
|
||||
profile, ok := findMatchingSRTPProfile(e.protectionProfiles, c.localSRTPProtectionProfiles)
|
||||
if !ok {
|
||||
return fmt.Errorf("Server responded with SRTP Profile we do not support")
|
||||
}
|
||||
c.state.srtpProtectionProfile = profile
|
||||
}
|
||||
}
|
||||
if len(c.localSRTPProtectionProfiles) > 0 && c.state.srtpProtectionProfile == 0 {
|
||||
return fmt.Errorf("SRTP support was requested but server did not respond with use_srtp extension")
|
||||
}
|
||||
if _, ok := findMatchingCipherSuite([]cipherSuite{h.cipherSuite}, c.localCipherSuites); !ok {
|
||||
return errCipherSuiteNoIntersection
|
||||
}
|
||||
|
||||
c.state.cipherSuite = h.cipherSuite
|
||||
c.state.remoteRandom = h.random
|
||||
c.log.Tracef("[handshake] use cipher suite: %s", h.cipherSuite.String())
|
||||
|
||||
case *handshakeMessageCertificate:
|
||||
c.state.remoteCertificate = h.certificate
|
||||
|
||||
case *handshakeMessageServerKeyExchange:
|
||||
c.remoteKeypair = &namedCurveKeypair{h.namedCurve, h.publicKey, nil}
|
||||
|
||||
clientRandom, err := c.state.localRandom.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serverRandom, err := c.state.remoteRandom.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.localKeypair, err = generateKeypair(h.namedCurve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preMasterSecret, err := prfPreMasterSecret(c.remoteKeypair.publicKey, c.localKeypair.privateKey, c.localKeypair.curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.state.masterSecret, err = prfMasterSecret(preMasterSecret, clientRandom, serverRandom, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.state.cipherSuite.init(c.state.masterSecret, clientRandom, serverRandom /* isClient */, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedHash := valueKeySignature(clientRandom, serverRandom, h.publicKey, h.namedCurve, h.hashAlgorithm)
|
||||
if err := verifyKeySignature(expectedHash, h.signature, h.hashAlgorithm, c.state.remoteCertificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *handshakeMessageCertificateRequest:
|
||||
c.remoteRequestedCertificate = true
|
||||
case *handshakeMessageServerHelloDone:
|
||||
case *handshakeMessageFinished:
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
handshakeCachePullRule{handshakeTypeCertificateVerify, true},
|
||||
handshakeCachePullRule{handshakeTypeFinished, true},
|
||||
)
|
||||
|
||||
expectedVerifyData, err := prfVerifyDataServer(c.state.masterSecret, plainText, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(expectedVerifyData, h.verifyData) {
|
||||
return errVerifyDataMismatch
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unhandled handshake %d", h.handshakeType())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
switch c.currFlight.get() {
|
||||
case flight1:
|
||||
// HelloVerifyRequest can be skipped by the server, so allow ServerHello during flight1 also
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeHelloVerifyRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
)
|
||||
|
||||
switch {
|
||||
case expectedMessages[0] != nil:
|
||||
if err := handleSingleHandshake(expectedMessages[0].data); err != nil {
|
||||
return err
|
||||
}
|
||||
c.state.localSequenceNumber++
|
||||
case expectedMessages[1] != nil:
|
||||
if err := handleSingleHandshake(expectedMessages[1].data); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return nil // We have no messages we can handle yet
|
||||
}
|
||||
|
||||
c.log.Tracef("[handshake] Flight 1 changed to %s", flight3.String())
|
||||
if err := c.currFlight.set(flight3); err != nil {
|
||||
return err
|
||||
}
|
||||
case flight3:
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
)
|
||||
// We don't have enough data to even assert validity
|
||||
if expectedMessages[0] == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
expectedSeqnum := expectedMessages[0].messageSequence
|
||||
for i, msg := range expectedMessages {
|
||||
switch {
|
||||
// handshakeMessageCertificateRequest can be nil, just make sure we have no gaps
|
||||
case i == 3 && msg == nil:
|
||||
continue
|
||||
case msg == nil:
|
||||
return nil // We don't have all messages yet, try again later
|
||||
case msg.messageSequence != expectedSeqnum:
|
||||
return nil // We have a gap, still waiting on messages
|
||||
}
|
||||
expectedSeqnum++
|
||||
}
|
||||
|
||||
for _, msg := range expectedMessages {
|
||||
if msg != nil {
|
||||
if err := handleSingleHandshake(msg.data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
c.state.localSequenceNumber++
|
||||
c.log.Tracef("[handshake] Flight 3 changed to %s", flight5.String())
|
||||
if err := c.currFlight.set(flight5); err != nil {
|
||||
return err
|
||||
}
|
||||
case flight5:
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeFinished, false},
|
||||
)
|
||||
|
||||
if expectedMessages[0] == nil {
|
||||
return nil
|
||||
} else if err := handleSingleHandshake(expectedMessages[0].data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.setLocalEpoch(1)
|
||||
c.state.localSequenceNumber = 1
|
||||
c.signalHandshakeComplete()
|
||||
default:
|
||||
return fmt.Errorf("client asked to handle unknown flight (%d)", c.currFlight.get())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func clientFlightHandler(c *Conn) (bool, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
switch c.currFlight.get() {
|
||||
case flight1:
|
||||
fallthrough
|
||||
case flight3:
|
||||
extensions := []extension{
|
||||
&extensionSupportedEllipticCurves{
|
||||
ellipticCurves: []namedCurve{namedCurveX25519, namedCurveP256},
|
||||
},
|
||||
&extensionSupportedPointFormats{
|
||||
pointFormats: []ellipticCurvePointFormat{ellipticCurvePointFormatUncompressed},
|
||||
},
|
||||
&extensionSupportedSignatureAlgorithms{
|
||||
signatureHashAlgorithms: []signatureHashAlgorithm{
|
||||
{HashAlgorithmSHA256, signatureAlgorithmECDSA},
|
||||
{HashAlgorithmSHA384, signatureAlgorithmECDSA},
|
||||
{HashAlgorithmSHA512, signatureAlgorithmECDSA},
|
||||
{HashAlgorithmSHA256, signatureAlgorithmRSA},
|
||||
{HashAlgorithmSHA384, signatureAlgorithmRSA},
|
||||
{HashAlgorithmSHA512, signatureAlgorithmRSA},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(c.localSRTPProtectionProfiles) > 0 {
|
||||
extensions = append(extensions, &extensionUseSRTP{
|
||||
protectionProfiles: c.localSRTPProtectionProfiles,
|
||||
})
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(c.state.localSequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageClientHello{
|
||||
version: protocolVersion1_2,
|
||||
cookie: c.cookie,
|
||||
random: c.state.localRandom,
|
||||
cipherSuites: c.localCipherSuites,
|
||||
compressionMethods: defaultCompressionMethods,
|
||||
extensions: extensions,
|
||||
}},
|
||||
}, false)
|
||||
case flight5:
|
||||
// TODO: Better way to end handshake
|
||||
if c.getRemoteEpoch() != 0 && c.getLocalEpoch() == 1 {
|
||||
// Handshake is done
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sequenceNumber := c.state.localSequenceNumber
|
||||
if c.remoteRequestedCertificate {
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(c.state.localSequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageCertificate{
|
||||
certificate: c.localCertificate,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageClientKeyExchange{
|
||||
publicKey: c.localKeypair.publicKey,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
|
||||
if c.remoteRequestedCertificate {
|
||||
if len(c.localCertificateVerify) == 0 {
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
)
|
||||
|
||||
certVerify, err := generateCertificateVerify(plainText, c.localPrivateKey)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
c.localCertificateVerify = certVerify
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageCertificateVerify{
|
||||
hashAlgorithm: HashAlgorithmSHA256,
|
||||
signatureAlgorithm: signatureAlgorithmECDSA,
|
||||
signature: c.localCertificateVerify,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &changeCipherSpec{},
|
||||
}, false)
|
||||
|
||||
if len(c.localVerifyData) == 0 {
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
handshakeCachePullRule{handshakeTypeCertificateVerify, true},
|
||||
)
|
||||
|
||||
var err error
|
||||
c.localVerifyData, err = prfVerifyDataClient(c.state.masterSecret, plainText, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix hard-coded epoch & sequenceNumber, taking retransmitting into account.
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
epoch: 1,
|
||||
sequenceNumber: 0, // sequenceNumber restarts per epoch
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber), // KeyExchange + 1
|
||||
},
|
||||
handshakeMessage: &handshakeMessageFinished{
|
||||
verifyData: c.localVerifyData,
|
||||
}},
|
||||
}, true)
|
||||
default:
|
||||
return false, fmt.Errorf("unhandled flight %s", c.currFlight.get())
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
45
vendor/github.com/pion/dtls/compression_method.go
generated
Normal file
45
vendor/github.com/pion/dtls/compression_method.go
generated
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package dtls
|
||||
|
||||
type compressionMethodID byte
|
||||
|
||||
const (
|
||||
compressionMethodNull compressionMethodID = 0
|
||||
)
|
||||
|
||||
type compressionMethod struct {
|
||||
id compressionMethodID
|
||||
}
|
||||
|
||||
var compressionMethods = map[compressionMethodID]*compressionMethod{
|
||||
compressionMethodNull: {id: compressionMethodNull},
|
||||
}
|
||||
|
||||
var defaultCompressionMethods = []*compressionMethod{
|
||||
compressionMethods[compressionMethodNull],
|
||||
}
|
||||
|
||||
func decodeCompressionMethods(buf []byte) ([]*compressionMethod, error) {
|
||||
if len(buf) < 1 {
|
||||
return nil, errDTLSPacketInvalidLength
|
||||
}
|
||||
compressionMethodsCount := int(buf[0])
|
||||
c := []*compressionMethod{}
|
||||
for i := 0; i < compressionMethodsCount; i++ {
|
||||
if len(buf) <= i+1 {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
id := compressionMethodID(buf[i+1])
|
||||
if compressionMethod, ok := compressionMethods[id]; ok {
|
||||
c = append(c, compressionMethod)
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func encodeCompressionMethods(c []*compressionMethod) []byte {
|
||||
out := []byte{byte(len(c))}
|
||||
for i := len(c); i > 0; i-- {
|
||||
out = append(out, byte(c[i-1].id))
|
||||
}
|
||||
return out
|
||||
}
|
||||
61
vendor/github.com/pion/dtls/config.go
generated
Normal file
61
vendor/github.com/pion/dtls/config.go
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
// Config is used to configure a DTLS client or server.
|
||||
// After a Config is passed to a DTLS function it must not be modified.
|
||||
type Config struct {
|
||||
// Certificates contains certificate chain to present to the other side of the connection.
|
||||
// Server MUST set this if PSK is non-nil
|
||||
// client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil
|
||||
Certificate *x509.Certificate
|
||||
|
||||
// PrivateKey contains matching private key for the certificate
|
||||
// only ECDSA is supported
|
||||
PrivateKey crypto.PrivateKey
|
||||
|
||||
// CipherSuites is a list of supported cipher suites.
|
||||
// If CipherSuites is nil, a default list is used
|
||||
CipherSuites []CipherSuiteID
|
||||
|
||||
// SRTPProtectionProfiles are the supported protection profiles
|
||||
// Clients will send this via use_srtp and assert that the server properly responds
|
||||
// Servers will assert that clients send one of these profiles and will respond as needed
|
||||
SRTPProtectionProfiles []SRTPProtectionProfile
|
||||
|
||||
// ClientAuth determines the server's policy for
|
||||
// TLS Client Authentication. The default is NoClientCert.
|
||||
ClientAuth ClientAuthType
|
||||
|
||||
// FlightInterval controls how often we send outbound handshake messages
|
||||
// defaults to time.Second
|
||||
FlightInterval time.Duration
|
||||
|
||||
// PSK sets the pre-shared key used by this DTLS connection
|
||||
// If PSK is non-nil only PSK CipherSuites will be used
|
||||
PSK PSKCallback
|
||||
PSKIdentityHint []byte
|
||||
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
// PSKCallback is called once we have the remote's PSKIdentityHint.
|
||||
// If the remote provided none it will be nil
|
||||
type PSKCallback func([]byte) ([]byte, error)
|
||||
|
||||
// ClientAuthType declares the policy the server will follow for
|
||||
// TLS Client Authentication.
|
||||
type ClientAuthType int
|
||||
|
||||
// ClientAuthType enums
|
||||
const (
|
||||
NoClientCert ClientAuthType = iota
|
||||
RequestClientCert
|
||||
RequireAnyClientCert
|
||||
)
|
||||
529
vendor/github.com/pion/dtls/conn.go
generated
Normal file
529
vendor/github.com/pion/dtls/conn.go
generated
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
initialTickerInterval = time.Second
|
||||
cookieLength = 20
|
||||
defaultNamedCurve = namedCurveX25519
|
||||
inboundBufferSize = 8192
|
||||
)
|
||||
|
||||
var invalidKeyingLabels = map[string]bool{
|
||||
"client finished": true,
|
||||
"server finished": true,
|
||||
"master secret": true,
|
||||
"key expansion": true,
|
||||
}
|
||||
|
||||
type handshakeMessageHandler func(*Conn) error
|
||||
type flightHandler func(*Conn) (bool, error)
|
||||
|
||||
// Conn represents a DTLS connection
|
||||
type Conn struct {
|
||||
lock sync.RWMutex // Internal lock (must not be public)
|
||||
nextConn net.Conn // Embedded Conn, typically a udpconn we read/write from
|
||||
fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling
|
||||
handshakeCache *handshakeCache // caching of handshake messages for verifyData generation
|
||||
decrypted chan []byte // Decrypted Application Data, pull by calling `Read`
|
||||
workerTicker *time.Ticker
|
||||
|
||||
state State // Internal state
|
||||
|
||||
remoteRequestedCertificate bool // Did we get a CertificateRequest
|
||||
|
||||
localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support
|
||||
localCipherSuites []cipherSuite // Available CipherSuites, if empty use default list
|
||||
|
||||
clientAuth ClientAuthType // If we are a client should we request a client certificate
|
||||
|
||||
currFlight *flight
|
||||
namedCurve namedCurve
|
||||
localCertificate *x509.Certificate
|
||||
localPrivateKey crypto.PrivateKey
|
||||
localKeypair, remoteKeypair *namedCurveKeypair
|
||||
cookie []byte
|
||||
|
||||
localPSKCallback PSKCallback
|
||||
localPSKIdentityHint []byte
|
||||
|
||||
localCertificateVerify []byte // cache CertificateVerify
|
||||
localVerifyData []byte // cached VerifyData
|
||||
localKeySignature []byte // cached keySignature
|
||||
remoteCertificateVerified bool
|
||||
|
||||
handshakeMessageHandler handshakeMessageHandler
|
||||
flightHandler flightHandler
|
||||
handshakeCompleted chan bool
|
||||
|
||||
connErr atomic.Value
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
func createConn(nextConn net.Conn, flightHandler flightHandler, handshakeMessageHandler handshakeMessageHandler, config *Config, isClient bool) (*Conn, error) {
|
||||
switch {
|
||||
case config == nil:
|
||||
return nil, errNoConfigProvided
|
||||
case nextConn == nil:
|
||||
return nil, errNilNextConn
|
||||
case config.Certificate != nil && (config.PSK != nil || config.PSKIdentityHint != nil):
|
||||
return nil, errPSKAndCertificate
|
||||
}
|
||||
|
||||
if config.PrivateKey != nil {
|
||||
if _, ok := config.PrivateKey.(*ecdsa.PrivateKey); !ok {
|
||||
return nil, errInvalidPrivateKey
|
||||
}
|
||||
}
|
||||
|
||||
cipherSuites, err := parseCipherSuites(config.CipherSuites, config.PSK == nil, config.PSK != nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workerInterval := initialTickerInterval
|
||||
if config.FlightInterval != 0 {
|
||||
workerInterval = config.FlightInterval
|
||||
}
|
||||
|
||||
loggerFactory := config.LoggerFactory
|
||||
if loggerFactory == nil {
|
||||
loggerFactory = logging.NewDefaultLoggerFactory()
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
nextConn: nextConn,
|
||||
currFlight: newFlight(isClient),
|
||||
fragmentBuffer: newFragmentBuffer(),
|
||||
handshakeCache: newHandshakeCache(),
|
||||
handshakeMessageHandler: handshakeMessageHandler,
|
||||
flightHandler: flightHandler,
|
||||
localCertificate: config.Certificate,
|
||||
localPrivateKey: config.PrivateKey,
|
||||
clientAuth: config.ClientAuth,
|
||||
localSRTPProtectionProfiles: config.SRTPProtectionProfiles,
|
||||
localCipherSuites: cipherSuites,
|
||||
namedCurve: defaultNamedCurve,
|
||||
|
||||
localPSKCallback: config.PSK,
|
||||
localPSKIdentityHint: config.PSKIdentityHint,
|
||||
|
||||
decrypted: make(chan []byte),
|
||||
workerTicker: time.NewTicker(workerInterval),
|
||||
handshakeCompleted: make(chan bool),
|
||||
log: loggerFactory.NewLogger("dtls"),
|
||||
}
|
||||
|
||||
var zeroEpoch uint16
|
||||
c.state.localEpoch.Store(zeroEpoch)
|
||||
c.state.remoteEpoch.Store(zeroEpoch)
|
||||
c.state.isClient = isClient
|
||||
|
||||
if err = c.state.localRandom.populate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isClient {
|
||||
c.cookie = make([]byte, cookieLength)
|
||||
if _, err = rand.Read(c.cookie); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger outbound
|
||||
c.startHandshakeOutbound()
|
||||
|
||||
// Handle inbound
|
||||
go c.inboundLoop()
|
||||
|
||||
<-c.handshakeCompleted
|
||||
c.log.Trace("Handshake Completed")
|
||||
return c, c.getConnErr()
|
||||
}
|
||||
|
||||
// Dial connects to the given network address and establishes a DTLS connection on top
|
||||
func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) {
|
||||
pConn, err := net.DialUDP(network, nil, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Client(pConn, config)
|
||||
}
|
||||
|
||||
// Client establishes a DTLS connection over an existing conn
|
||||
func Client(conn net.Conn, config *Config) (*Conn, error) {
|
||||
return createConn(conn, clientFlightHandler, clientHandshakeHandler, config, true)
|
||||
}
|
||||
|
||||
// Server listens for incoming DTLS connections
|
||||
func Server(conn net.Conn, config *Config) (*Conn, error) {
|
||||
if config == nil {
|
||||
return nil, errNoConfigProvided
|
||||
} else if config.PSK == nil && config.Certificate == nil {
|
||||
return nil, errServerMustHaveCertificate
|
||||
}
|
||||
|
||||
return createConn(conn, serverFlightHandler, serverHandshakeHandler, config, false)
|
||||
}
|
||||
|
||||
// Read reads data from the connection.
|
||||
func (c *Conn) Read(p []byte) (n int, err error) {
|
||||
out, ok := <-c.decrypted
|
||||
if !ok {
|
||||
return 0, c.getConnErr()
|
||||
}
|
||||
if len(p) < len(out) {
|
||||
return 0, errBufferTooSmall
|
||||
}
|
||||
|
||||
copy(p, out)
|
||||
return len(out), nil
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the DTLS connection
|
||||
func (c *Conn) Write(p []byte) (int, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if c.getLocalEpoch() == 0 {
|
||||
return 0, errHandshakeInProgress
|
||||
} else if c.getConnErr() != nil {
|
||||
return 0, c.getConnErr()
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
epoch: c.getLocalEpoch(),
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &applicationData{
|
||||
data: p,
|
||||
},
|
||||
}, true)
|
||||
c.state.localSequenceNumber++
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *Conn) Close() error {
|
||||
c.notify(alertLevelFatal, alertCloseNotify)
|
||||
c.stopWithError(ErrConnClosed)
|
||||
if err := c.getConnErr(); err != ErrConnClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoteCertificate exposes the remote certificate
|
||||
func (c *Conn) RemoteCertificate() *x509.Certificate {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.state.remoteCertificate
|
||||
}
|
||||
|
||||
// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile
|
||||
func (c *Conn) SelectedSRTPProtectionProfile() (SRTPProtectionProfile, bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
if c.state.srtpProtectionProfile == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return c.state.srtpProtectionProfile, true
|
||||
}
|
||||
|
||||
// ExportKeyingMaterial from https://tools.ietf.org/html/rfc5705
|
||||
// This allows protocols to use DTLS for key establishment, but
|
||||
// then use some of the keying material for their own purposes
|
||||
func (c *Conn) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if c.getLocalEpoch() == 0 {
|
||||
return nil, errHandshakeInProgress
|
||||
} else if len(context) != 0 {
|
||||
return nil, errContextUnsupported
|
||||
} else if _, ok := invalidKeyingLabels[label]; ok {
|
||||
return nil, errReservedExportKeyingMaterial
|
||||
}
|
||||
|
||||
localRandom, err := c.state.localRandom.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteRandom, err := c.state.remoteRandom.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seed := []byte(label)
|
||||
if c.state.isClient {
|
||||
seed = append(append(seed, localRandom...), remoteRandom...)
|
||||
} else {
|
||||
seed = append(append(seed, remoteRandom...), localRandom...)
|
||||
}
|
||||
return prfPHash(c.state.masterSecret, seed, length, c.state.cipherSuite.hashFunc())
|
||||
}
|
||||
|
||||
func (c *Conn) internalSend(pkt *recordLayer, shouldEncrypt bool) {
|
||||
|
||||
raw, err := pkt.Marshal()
|
||||
if err != nil {
|
||||
c.stopWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if h, ok := pkt.content.(*handshake); ok {
|
||||
c.log.Tracef("[handshake] -> %s", h.handshakeHeader.handshakeType.String())
|
||||
c.handshakeCache.push(raw[recordLayerHeaderSize:], h.handshakeHeader.messageSequence, h.handshakeHeader.handshakeType, c.state.isClient)
|
||||
}
|
||||
|
||||
if shouldEncrypt {
|
||||
raw, err = c.state.cipherSuite.encrypt(pkt, raw)
|
||||
if err != nil {
|
||||
c.stopWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := c.nextConn.Write(raw); err != nil {
|
||||
c.stopWithError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) inboundLoop() {
|
||||
defer func() {
|
||||
close(c.decrypted)
|
||||
}()
|
||||
|
||||
b := make([]byte, inboundBufferSize)
|
||||
for {
|
||||
i, err := c.nextConn.Read(b)
|
||||
if err != nil {
|
||||
c.stopWithError(err)
|
||||
return
|
||||
} else if c.getConnErr() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pkts, err := unpackDatagram(b[:i])
|
||||
if err != nil {
|
||||
c.stopWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, p := range pkts {
|
||||
err := c.handleIncomingPacket(p)
|
||||
if err != nil {
|
||||
c.stopWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) handleIncomingPacket(buf []byte) error {
|
||||
// TODO: avoid separate unmarshal
|
||||
h := &recordLayerHeader{}
|
||||
if err := h.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.epoch < c.getRemoteEpoch() {
|
||||
if _, err := c.flightHandler(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if h.epoch != 0 {
|
||||
if c.state.cipherSuite == nil {
|
||||
c.log.Debug("handleIncoming: Handshake not finished, dropping packet")
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
buf, err = c.state.cipherSuite.decrypt(buf)
|
||||
if err != nil {
|
||||
c.log.Debugf("decrypt failed: %s", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if isHandshake {
|
||||
newHandshakeMessage := false
|
||||
for out := c.fragmentBuffer.pop(); out != nil; out = c.fragmentBuffer.pop() {
|
||||
rawHandshake := &handshake{}
|
||||
if err := rawHandshake.Unmarshal(out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.handshakeCache.push(out, rawHandshake.handshakeHeader.messageSequence, rawHandshake.handshakeHeader.handshakeType, !c.state.isClient) {
|
||||
newHandshakeMessage = true
|
||||
}
|
||||
}
|
||||
if !newHandshakeMessage {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.handshakeMessageHandler(c)
|
||||
}
|
||||
|
||||
r := &recordLayer{}
|
||||
if err := r.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch content := r.content.(type) {
|
||||
case *alert:
|
||||
c.log.Tracef("<- %s", content.String())
|
||||
if content.alertDescription == alertCloseNotify {
|
||||
return c.Close()
|
||||
}
|
||||
return fmt.Errorf("alert: %v", content)
|
||||
case *changeCipherSpec:
|
||||
c.log.Trace("<- ChangeCipherSpec")
|
||||
c.setRemoteEpoch(c.getRemoteEpoch() + 1)
|
||||
case *applicationData:
|
||||
c.decrypted <- content.data
|
||||
default:
|
||||
return fmt.Errorf("unhandled contentType %d", content.contentType())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) notify(level alertLevel, desc alertDescription) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
epoch: c.getLocalEpoch(),
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &alert{
|
||||
alertLevel: level,
|
||||
alertDescription: desc,
|
||||
},
|
||||
}, true)
|
||||
|
||||
c.state.localSequenceNumber++
|
||||
}
|
||||
|
||||
func (c *Conn) signalHandshakeComplete() {
|
||||
select {
|
||||
case <-c.handshakeCompleted:
|
||||
default:
|
||||
close(c.handshakeCompleted)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) startHandshakeOutbound() {
|
||||
go func() {
|
||||
for {
|
||||
var (
|
||||
isFinished bool
|
||||
err error
|
||||
)
|
||||
select {
|
||||
case <-c.handshakeCompleted:
|
||||
return
|
||||
case <-c.workerTicker.C:
|
||||
isFinished, err = c.flightHandler(c)
|
||||
case <-c.currFlight.workerTrigger:
|
||||
isFinished, err = c.flightHandler(c)
|
||||
}
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
c.stopWithError(err)
|
||||
return
|
||||
case c.getConnErr() != nil:
|
||||
return
|
||||
case isFinished:
|
||||
return // Handshake is complete
|
||||
}
|
||||
}
|
||||
}()
|
||||
c.currFlight.workerTrigger <- struct{}{}
|
||||
}
|
||||
|
||||
func (c *Conn) stopWithError(err error) {
|
||||
if connErr := c.nextConn.Close(); connErr != nil {
|
||||
if err != ErrConnClosed {
|
||||
connErr = fmt.Errorf("%v\n%v", err, connErr)
|
||||
}
|
||||
err = connErr
|
||||
}
|
||||
|
||||
c.connErr.Store(struct{ error }{err})
|
||||
|
||||
c.workerTicker.Stop()
|
||||
|
||||
c.signalHandshakeComplete()
|
||||
}
|
||||
|
||||
func (c *Conn) getConnErr() error {
|
||||
err, _ := c.connErr.Load().(struct{ error })
|
||||
return err.error
|
||||
}
|
||||
|
||||
func (c *Conn) setLocalEpoch(epoch uint16) {
|
||||
c.state.localEpoch.Store(epoch)
|
||||
}
|
||||
|
||||
func (c *Conn) getLocalEpoch() uint16 {
|
||||
return c.state.localEpoch.Load().(uint16)
|
||||
}
|
||||
|
||||
func (c *Conn) setRemoteEpoch(epoch uint16) {
|
||||
c.state.remoteEpoch.Store(epoch)
|
||||
}
|
||||
|
||||
func (c *Conn) getRemoteEpoch() uint16 {
|
||||
return c.state.remoteEpoch.Load().(uint16)
|
||||
}
|
||||
|
||||
// LocalAddr is a stub
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.nextConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr is a stub
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.nextConn.RemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline is a stub
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return c.nextConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline is a stub
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.nextConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline is a stub
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return c.nextConn.SetWriteDeadline(t)
|
||||
}
|
||||
17
vendor/github.com/pion/dtls/content.go
generated
Normal file
17
vendor/github.com/pion/dtls/content.go
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package dtls
|
||||
|
||||
// https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||
type contentType uint8
|
||||
|
||||
const (
|
||||
contentTypeChangeCipherSpec contentType = 20
|
||||
contentTypeAlert contentType = 21
|
||||
contentTypeHandshake contentType = 22
|
||||
contentTypeApplicationData contentType = 23
|
||||
)
|
||||
|
||||
type content interface {
|
||||
contentType() contentType
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal(data []byte) error
|
||||
}
|
||||
122
vendor/github.com/pion/dtls/crypto.go
generated
Normal file
122
vendor/github.com/pion/dtls/crypto.go
generated
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type ecdsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
func valueKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve namedCurve, hashAlgorithm HashAlgorithm) []byte {
|
||||
serverECDHParams := make([]byte, 4)
|
||||
serverECDHParams[0] = 3 // named curve
|
||||
binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve))
|
||||
serverECDHParams[3] = byte(len(publicKey))
|
||||
|
||||
plaintext := []byte{}
|
||||
plaintext = append(plaintext, clientRandom...)
|
||||
plaintext = append(plaintext, serverRandom...)
|
||||
plaintext = append(plaintext, serverECDHParams...)
|
||||
plaintext = append(plaintext, publicKey...)
|
||||
return hashAlgorithm.digest(plaintext)
|
||||
}
|
||||
|
||||
// If the client provided a "signature_algorithms" extension, then all
|
||||
// certificates provided by the server MUST be signed by a
|
||||
// hash/signature algorithm pair that appears in that extension
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.2
|
||||
func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve namedCurve, privateKey crypto.PrivateKey, hashAlgorithm HashAlgorithm) ([]byte, error) {
|
||||
hashed := valueKeySignature(clientRandom, serverRandom, publicKey, namedCurve, hashAlgorithm)
|
||||
switch p := privateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
return p.Sign(rand.Reader, hashed, crypto.SHA256)
|
||||
case *rsa.PrivateKey:
|
||||
return p.Sign(rand.Reader, hashed, crypto.SHA256)
|
||||
}
|
||||
|
||||
return nil, errKeySignatureGenerateUnimplemented
|
||||
}
|
||||
|
||||
func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm HashAlgorithm, certificate *x509.Certificate) error {
|
||||
switch p := certificate.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
ecdsaSig := &ecdsaSignature{}
|
||||
if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
|
||||
return err
|
||||
}
|
||||
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
||||
return errInvalidECDSASignature
|
||||
}
|
||||
if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
|
||||
return errKeySignatureMismatch
|
||||
}
|
||||
return nil
|
||||
case *rsa.PublicKey:
|
||||
switch certificate.SignatureAlgorithm {
|
||||
case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||
return rsa.VerifyPKCS1v15(p, hashAlgorithm.cryptoHash(), hash, remoteKeySignature)
|
||||
}
|
||||
}
|
||||
|
||||
return errKeySignatureVerifyUnimplemented
|
||||
}
|
||||
|
||||
// If the server has sent a CertificateRequest message, the client MUST send the Certificate
|
||||
// message. The ClientKeyExchange message is now sent, and the content
|
||||
// of that message will depend on the public key algorithm selected
|
||||
// between the ClientHello and the ServerHello. If the client has sent
|
||||
// a certificate with signing ability, a digitally-signed
|
||||
// CertificateVerify message is sent to explicitly verify possession of
|
||||
// the private key in the certificate.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.3
|
||||
func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey) ([]byte, error) {
|
||||
h := sha256.New()
|
||||
if _, err := h.Write(handshakeBodies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashed := h.Sum(nil)
|
||||
|
||||
switch p := privateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
return p.Sign(rand.Reader, hashed, crypto.SHA256)
|
||||
case *rsa.PrivateKey:
|
||||
return p.Sign(rand.Reader, hashed, crypto.SHA256)
|
||||
}
|
||||
|
||||
return nil, errInvalidSignatureAlgorithm
|
||||
}
|
||||
|
||||
func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm HashAlgorithm, remoteKeySignature []byte, certificate *x509.Certificate) error {
|
||||
hash := hashAlgorithm.digest(handshakeBodies)
|
||||
switch p := certificate.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
ecdsaSig := &ecdsaSignature{}
|
||||
if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
|
||||
return err
|
||||
}
|
||||
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
||||
return errInvalidECDSASignature
|
||||
}
|
||||
if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
|
||||
return errKeySignatureMismatch
|
||||
}
|
||||
return nil
|
||||
case *rsa.PublicKey:
|
||||
switch certificate.SignatureAlgorithm {
|
||||
case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||
return rsa.VerifyPKCS1v15(p, hashAlgorithm.cryptoHash(), hash, remoteKeySignature)
|
||||
}
|
||||
}
|
||||
|
||||
return errKeySignatureVerifyUnimplemented
|
||||
}
|
||||
133
vendor/github.com/pion/dtls/crypto_cbc.go
generated
Normal file
133
vendor/github.com/pion/dtls/crypto_cbc.go
generated
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1" // #nosec
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// block ciphers using cipher block chaining.
|
||||
type cbcMode interface {
|
||||
cipher.BlockMode
|
||||
SetIV([]byte)
|
||||
}
|
||||
|
||||
// State needed to handle encrypted input/output
|
||||
type cryptoCBC struct {
|
||||
writeCBC, readCBC cbcMode
|
||||
writeMac, readMac []byte
|
||||
}
|
||||
|
||||
// Currently hardcoded to be SHA1 only
|
||||
var cryptoCBCMacFunc = sha1.New
|
||||
|
||||
func newCryptoCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte) (*cryptoCBC, error) {
|
||||
writeBlock, err := aes.NewCipher(localKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
readBlock, err := aes.NewCipher(remoteKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cryptoCBC{
|
||||
writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode),
|
||||
writeMac: localMac,
|
||||
|
||||
readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode),
|
||||
readMac: remoteMac,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *cryptoCBC) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
|
||||
payload := raw[recordLayerHeaderSize:]
|
||||
raw = raw[:recordLayerHeaderSize]
|
||||
blockSize := c.writeCBC.BlockSize()
|
||||
|
||||
// Generate + Append MAC
|
||||
h := pkt.recordLayerHeader
|
||||
|
||||
MAC, err := prfMac(h.epoch, h.sequenceNumber, h.contentType, h.protocolVersion, payload, c.writeMac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload = append(payload, MAC...)
|
||||
|
||||
// Generate + Append padding
|
||||
padding := make([]byte, blockSize-len(payload)%blockSize)
|
||||
paddingLen := len(padding)
|
||||
for i := 0; i < paddingLen; i++ {
|
||||
padding[i] = byte(paddingLen - 1)
|
||||
}
|
||||
payload = append(payload, padding...)
|
||||
|
||||
// Generate IV
|
||||
iv := make([]byte, blockSize)
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set IV + Encrypt + Prepend IV
|
||||
c.writeCBC.SetIV(iv)
|
||||
c.writeCBC.CryptBlocks(payload, payload)
|
||||
payload = append(iv, payload...)
|
||||
|
||||
// Prepend unencrypte header with encrypted payload
|
||||
raw = append(raw, payload...)
|
||||
|
||||
// Update recordLayer size to include IV+MAC+Padding
|
||||
binary.BigEndian.PutUint16(raw[recordLayerHeaderSize-2:], uint16(len(raw)-recordLayerHeaderSize))
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (c *cryptoCBC) decrypt(in []byte) ([]byte, error) {
|
||||
body := in[recordLayerHeaderSize:]
|
||||
blockSize := c.readCBC.BlockSize()
|
||||
mac := cryptoCBCMacFunc()
|
||||
|
||||
var h recordLayerHeader
|
||||
err := h.Unmarshal(in)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case h.contentType == contentTypeChangeCipherSpec:
|
||||
// Nothing to encrypt with ChangeCipherSpec
|
||||
return in, nil
|
||||
case len(body)%blockSize != 0 || len(body) < blockSize+max(mac.Size()+1, blockSize):
|
||||
return nil, errNotEnoughRoomForNonce
|
||||
}
|
||||
|
||||
// Set + remove per record IV
|
||||
c.readCBC.SetIV(body[:blockSize])
|
||||
body = body[blockSize:]
|
||||
|
||||
// Decrypt
|
||||
c.readCBC.CryptBlocks(body, body)
|
||||
|
||||
// Padding+MAC needs to be checked in constant time
|
||||
// Otherwise we reveal information about the level of correctness
|
||||
paddingLen, paddingGood := examinePadding(body)
|
||||
|
||||
macSize := mac.Size()
|
||||
if len(body) < macSize {
|
||||
return nil, errInvalidMAC
|
||||
}
|
||||
|
||||
dataEnd := len(body) - macSize - paddingLen
|
||||
|
||||
expectedMAC := body[dataEnd : dataEnd+macSize]
|
||||
actualMAC, err := prfMac(h.epoch, h.sequenceNumber, h.contentType, h.protocolVersion, body[:dataEnd], c.readMac)
|
||||
|
||||
// Compute Local MAC and compare
|
||||
if paddingGood != 255 || err != nil || !hmac.Equal(actualMAC, expectedMAC) {
|
||||
return nil, errInvalidMAC
|
||||
}
|
||||
|
||||
return append(in[:recordLayerHeaderSize], body[:dataEnd]...), nil
|
||||
}
|
||||
105
vendor/github.com/pion/dtls/crypto_gcm.go
generated
Normal file
105
vendor/github.com/pion/dtls/crypto_gcm.go
generated
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const cryptoGCMTagLength = 16
|
||||
|
||||
// State needed to handle encrypted input/output
|
||||
type cryptoGCM struct {
|
||||
localGCM, remoteGCM cipher.AEAD
|
||||
localWriteIV, remoteWriteIV []byte
|
||||
}
|
||||
|
||||
func newCryptoGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*cryptoGCM, error) {
|
||||
localBlock, err := aes.NewCipher(localKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localGCM, err := cipher.NewGCM(localBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remoteBlock, err := aes.NewCipher(remoteKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteGCM, err := cipher.NewGCM(remoteBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cryptoGCM{
|
||||
localGCM: localGCM,
|
||||
localWriteIV: localWriteIV,
|
||||
remoteGCM: remoteGCM,
|
||||
remoteWriteIV: remoteWriteIV,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *cryptoGCM) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
|
||||
payload := raw[recordLayerHeaderSize:]
|
||||
raw = raw[:recordLayerHeaderSize]
|
||||
|
||||
nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...)
|
||||
if _, err := rand.Read(nonce[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var additionalData [13]byte
|
||||
// SequenceNumber MUST be set first
|
||||
// we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48)
|
||||
binary.BigEndian.PutUint64(additionalData[:], pkt.recordLayerHeader.sequenceNumber)
|
||||
binary.BigEndian.PutUint16(additionalData[:], pkt.recordLayerHeader.epoch)
|
||||
additionalData[8] = byte(pkt.content.contentType())
|
||||
additionalData[9] = pkt.recordLayerHeader.protocolVersion.major
|
||||
additionalData[10] = pkt.recordLayerHeader.protocolVersion.minor
|
||||
binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(len(payload)))
|
||||
encryptedPayload := c.localGCM.Seal(nil, nonce, payload, additionalData[:])
|
||||
|
||||
encryptedPayload = append(nonce[4:], encryptedPayload...)
|
||||
raw = append(raw, encryptedPayload...)
|
||||
|
||||
// Update recordLayer size to include explicit nonce
|
||||
binary.BigEndian.PutUint16(raw[recordLayerHeaderSize-2:], uint16(len(raw)-recordLayerHeaderSize))
|
||||
return raw, nil
|
||||
|
||||
}
|
||||
|
||||
func (c *cryptoGCM) decrypt(in []byte) ([]byte, error) {
|
||||
var h recordLayerHeader
|
||||
err := h.Unmarshal(in)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case h.contentType == contentTypeChangeCipherSpec:
|
||||
// Nothing to encrypt with ChangeCipherSpec
|
||||
return in, nil
|
||||
case len(in) <= (8 + recordLayerHeaderSize):
|
||||
return nil, errNotEnoughRoomForNonce
|
||||
}
|
||||
|
||||
nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordLayerHeaderSize:recordLayerHeaderSize+8]...)
|
||||
out := in[recordLayerHeaderSize+8:]
|
||||
|
||||
var additionalData [13]byte
|
||||
// SequenceNumber MUST be set first
|
||||
// we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48)
|
||||
binary.BigEndian.PutUint64(additionalData[:], h.sequenceNumber)
|
||||
binary.BigEndian.PutUint16(additionalData[:], h.epoch)
|
||||
additionalData[8] = byte(h.contentType)
|
||||
additionalData[9] = h.protocolVersion.major
|
||||
additionalData[10] = h.protocolVersion.minor
|
||||
binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(len(out)-cryptoGCMTagLength))
|
||||
out, err = c.remoteGCM.Open(out[:0], nonce, out, additionalData[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decryptPacket: %v", err)
|
||||
}
|
||||
return append(in[:recordLayerHeaderSize], out...), nil
|
||||
}
|
||||
12
vendor/github.com/pion/dtls/curve_type.go
generated
Normal file
12
vendor/github.com/pion/dtls/curve_type.go
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package dtls
|
||||
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
|
||||
type ellipticCurveType byte
|
||||
|
||||
const (
|
||||
ellipticCurveTypeNamedCurve ellipticCurveType = 0x03
|
||||
)
|
||||
|
||||
var ellipticCurveTypes = map[ellipticCurveType]bool{
|
||||
ellipticCurveTypeNamedCurve: true,
|
||||
}
|
||||
50
vendor/github.com/pion/dtls/errors.go
generated
Normal file
50
vendor/github.com/pion/dtls/errors.go
generated
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package dtls
|
||||
|
||||
import "errors"
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrConnClosed = errors.New("dtls: conn is closed")
|
||||
|
||||
errBufferTooSmall = errors.New("dtls: buffer is too small")
|
||||
errCertificateUnset = errors.New("dtls: handshakeMessageCertificate can not be marshalled without a certificate")
|
||||
errClientCertificateRequired = errors.New("dtls: server required client verification, but got none")
|
||||
errClientCertificateNotVerified = errors.New("dtls: client sent certificate but did not verify it")
|
||||
errCertificateVerifyNoCertificate = errors.New("dtls: client sent certificate verify but we have no certificate to verify")
|
||||
errCipherSuiteNoIntersection = errors.New("dtls: Client+Server do not support any shared cipher suites")
|
||||
errCipherSuiteUnset = errors.New("dtls: server hello can not be created without a cipher suite")
|
||||
errCompressionmethodUnset = errors.New("dtls: server hello can not be created without a compression method")
|
||||
errContextUnsupported = errors.New("dtls: context is not supported for ExportKeyingMaterial")
|
||||
errCookieMismatch = errors.New("dtls: Client+Server cookie does not match")
|
||||
errCookieTooLong = errors.New("dtls: cookie must not be longer then 255 bytes")
|
||||
errDTLSPacketInvalidLength = errors.New("dtls: packet is too short")
|
||||
errHandshakeInProgress = errors.New("dtls: Handshake is in progress")
|
||||
errHandshakeMessageUnset = errors.New("dtls: handshake message unset, unable to marshal")
|
||||
errInvalidCipherSpec = errors.New("dtls: cipher spec invalid")
|
||||
errInvalidCipherSuite = errors.New("dtls: invalid or unknown cipher suite")
|
||||
errInvalidCompressionMethod = errors.New("dtls: invalid or unknown compression method")
|
||||
errInvalidContentType = errors.New("dtls: invalid content type")
|
||||
errInvalidECDSASignature = errors.New("dtls: ECDSA signature contained zero or negative values")
|
||||
errInvalidEllipticCurveType = errors.New("dtls: invalid or unknown elliptic curve type")
|
||||
errInvalidExtensionType = errors.New("dtls: invalid extension type")
|
||||
errInvalidHashAlgorithm = errors.New("dtls: invalid hash algorithm")
|
||||
errInvalidMAC = errors.New("dtls: invalid mac")
|
||||
errInvalidNamedCurve = errors.New("dtls: invalid named curve")
|
||||
errInvalidPrivateKey = errors.New("dtls: invalid private key type")
|
||||
errInvalidSignatureAlgorithm = errors.New("dtls: invalid signature algorithm")
|
||||
errKeySignatureGenerateUnimplemented = errors.New("dtls: Unable to generate key signature, unimplemented")
|
||||
errKeySignatureMismatch = errors.New("dtls: Expected and actual key signature do not match")
|
||||
errKeySignatureVerifyUnimplemented = errors.New("dtls: Unable to verify key signature, unimplemented")
|
||||
errLengthMismatch = errors.New("dtls: data length and declared length do not match")
|
||||
errNilNextConn = errors.New("dtls: Conn can not be created with a nil nextConn")
|
||||
errNotEnoughRoomForNonce = errors.New("dtls: Buffer not long enough to contain nonce")
|
||||
errNotImplemented = errors.New("dtls: feature has not been implemented yet")
|
||||
errReservedExportKeyingMaterial = errors.New("dtls: ExportKeyingMaterial can not be used with a reserved label")
|
||||
errSequenceNumberOverflow = errors.New("dtls: sequence number overflow")
|
||||
errServerMustHaveCertificate = errors.New("dtls: Certificate is mandatory for server")
|
||||
errUnableToMarshalFragmented = errors.New("dtls: unable to marshal fragmented handshakes")
|
||||
errVerifyDataMismatch = errors.New("dtls: Expected and actual verify data does not match")
|
||||
errNoConfigProvided = errors.New("dtls: No config provided")
|
||||
errPSKAndCertificate = errors.New("dtls: Certificate and PSK or PSK Identity Hint provided")
|
||||
errNoAvailableCipherSuites = errors.New("dtls: Connection can not be created, no CipherSuites satisfy this Config")
|
||||
)
|
||||
79
vendor/github.com/pion/dtls/extension.go
generated
Normal file
79
vendor/github.com/pion/dtls/extension.go
generated
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
|
||||
type extensionValue uint16
|
||||
|
||||
const (
|
||||
extensionSupportedEllipticCurvesValue extensionValue = 10
|
||||
extensionSupportedPointFormatsValue extensionValue = 11
|
||||
extensionSupportedSignatureAlgorithmsValue extensionValue = 13
|
||||
extensionUseSRTPValue extensionValue = 14
|
||||
)
|
||||
|
||||
type extension interface {
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal(data []byte) error
|
||||
|
||||
extensionValue() extensionValue
|
||||
}
|
||||
|
||||
func decodeExtensions(buf []byte) ([]extension, error) {
|
||||
if len(buf) < 2 {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
declaredLen := binary.BigEndian.Uint16(buf)
|
||||
if len(buf)-2 != int(declaredLen) {
|
||||
return nil, errLengthMismatch
|
||||
}
|
||||
|
||||
extensions := []extension{}
|
||||
unmarshalAndAppend := func(data []byte, e extension) error {
|
||||
err := e.Unmarshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
extensions = append(extensions, e)
|
||||
return nil
|
||||
}
|
||||
|
||||
for offset := 2; offset < len(buf); {
|
||||
if len(buf) < (offset + 2) {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
var err error
|
||||
switch extensionValue(binary.BigEndian.Uint16(buf[offset:])) {
|
||||
case extensionSupportedEllipticCurvesValue:
|
||||
err = unmarshalAndAppend(buf[offset:], &extensionSupportedEllipticCurves{})
|
||||
case extensionUseSRTPValue:
|
||||
err = unmarshalAndAppend(buf[offset:], &extensionUseSRTP{})
|
||||
default:
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(buf) < (offset + 4) {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
extensionLength := binary.BigEndian.Uint16(buf[offset+2:])
|
||||
offset += (4 + int(extensionLength))
|
||||
}
|
||||
return extensions, nil
|
||||
}
|
||||
|
||||
func encodeExtensions(e []extension) ([]byte, error) {
|
||||
extensions := []byte{}
|
||||
for _, e := range e {
|
||||
raw, err := e.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensions = append(extensions, raw...)
|
||||
}
|
||||
out := []byte{0x00, 0x00}
|
||||
binary.BigEndian.PutUint16(out, uint16(len(extensions)))
|
||||
return append(out, extensions...), nil
|
||||
}
|
||||
54
vendor/github.com/pion/dtls/extension_supported_elliptic_curves.go
generated
Normal file
54
vendor/github.com/pion/dtls/extension_supported_elliptic_curves.go
generated
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
extensionSupportedGroupsHeaderSize = 6
|
||||
)
|
||||
|
||||
// https://tools.ietf.org/html/rfc8422#section-5.1.1
|
||||
type extensionSupportedEllipticCurves struct {
|
||||
ellipticCurves []namedCurve
|
||||
}
|
||||
|
||||
func (e extensionSupportedEllipticCurves) extensionValue() extensionValue {
|
||||
return extensionSupportedEllipticCurvesValue
|
||||
}
|
||||
|
||||
func (e *extensionSupportedEllipticCurves) Marshal() ([]byte, error) {
|
||||
out := make([]byte, extensionSupportedGroupsHeaderSize)
|
||||
|
||||
binary.BigEndian.PutUint16(out, uint16(e.extensionValue()))
|
||||
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(e.ellipticCurves)*2)))
|
||||
binary.BigEndian.PutUint16(out[4:], uint16(len(e.ellipticCurves)*2))
|
||||
|
||||
for _, v := range e.ellipticCurves {
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *extensionSupportedEllipticCurves) Unmarshal(data []byte) error {
|
||||
if len(data) <= extensionSupportedGroupsHeaderSize {
|
||||
return errBufferTooSmall
|
||||
} else if extensionValue(binary.BigEndian.Uint16(data)) != e.extensionValue() {
|
||||
return errInvalidExtensionType
|
||||
}
|
||||
|
||||
groupCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
|
||||
if extensionSupportedGroupsHeaderSize+(groupCount*2) > len(data) {
|
||||
return errLengthMismatch
|
||||
}
|
||||
|
||||
for i := 0; i < groupCount; i++ {
|
||||
supportedGroupID := namedCurve(binary.BigEndian.Uint16(data[(extensionSupportedGroupsHeaderSize + (i * 2)):]))
|
||||
if _, ok := namedCurves[supportedGroupID]; ok {
|
||||
e.ellipticCurves = append(e.ellipticCurves, supportedGroupID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
vendor/github.com/pion/dtls/extension_supported_point_formats.go
generated
Normal file
56
vendor/github.com/pion/dtls/extension_supported_point_formats.go
generated
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package dtls
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
extensionSupportedPointFormatsSize = 5
|
||||
)
|
||||
|
||||
type ellipticCurvePointFormat byte
|
||||
|
||||
const ellipticCurvePointFormatUncompressed ellipticCurvePointFormat = 0
|
||||
|
||||
// https://tools.ietf.org/html/rfc4492#section-5.1.2
|
||||
type extensionSupportedPointFormats struct {
|
||||
pointFormats []ellipticCurvePointFormat
|
||||
}
|
||||
|
||||
func (e extensionSupportedPointFormats) extensionValue() extensionValue {
|
||||
return extensionSupportedPointFormatsValue
|
||||
}
|
||||
|
||||
func (e *extensionSupportedPointFormats) Marshal() ([]byte, error) {
|
||||
out := make([]byte, extensionSupportedPointFormatsSize)
|
||||
|
||||
binary.BigEndian.PutUint16(out, uint16(e.extensionValue()))
|
||||
binary.BigEndian.PutUint16(out[2:], uint16(1+(len(e.pointFormats))))
|
||||
out[4] = byte(len(e.pointFormats))
|
||||
|
||||
for _, v := range e.pointFormats {
|
||||
out = append(out, byte(v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *extensionSupportedPointFormats) Unmarshal(data []byte) error {
|
||||
if len(data) <= extensionSupportedPointFormatsSize {
|
||||
return errBufferTooSmall
|
||||
} else if extensionValue(binary.BigEndian.Uint16(data)) != e.extensionValue() {
|
||||
return errInvalidExtensionType
|
||||
}
|
||||
|
||||
pointFormatCount := int(binary.BigEndian.Uint16(data[4:]))
|
||||
if extensionSupportedGroupsHeaderSize+(pointFormatCount) > len(data) {
|
||||
return errLengthMismatch
|
||||
}
|
||||
|
||||
for i := 0; i < pointFormatCount; i++ {
|
||||
p := ellipticCurvePointFormat(data[extensionSupportedPointFormatsSize+i])
|
||||
switch p {
|
||||
case ellipticCurvePointFormatUncompressed:
|
||||
e.pointFormats = append(e.pointFormats, p)
|
||||
default:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
60
vendor/github.com/pion/dtls/extension_supported_signature_algorithms.go
generated
Normal file
60
vendor/github.com/pion/dtls/extension_supported_signature_algorithms.go
generated
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
extensionSupportedSignatureAlgorithmsHeaderSize = 6
|
||||
)
|
||||
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||
type extensionSupportedSignatureAlgorithms struct {
|
||||
signatureHashAlgorithms []signatureHashAlgorithm
|
||||
}
|
||||
|
||||
func (e extensionSupportedSignatureAlgorithms) extensionValue() extensionValue {
|
||||
return extensionSupportedSignatureAlgorithmsValue
|
||||
}
|
||||
|
||||
func (e *extensionSupportedSignatureAlgorithms) Marshal() ([]byte, error) {
|
||||
out := make([]byte, extensionSupportedSignatureAlgorithmsHeaderSize)
|
||||
|
||||
binary.BigEndian.PutUint16(out, uint16(e.extensionValue()))
|
||||
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(e.signatureHashAlgorithms)*2)))
|
||||
binary.BigEndian.PutUint16(out[4:], uint16(len(e.signatureHashAlgorithms)*2))
|
||||
for _, v := range e.signatureHashAlgorithms {
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
out[len(out)-2] = byte(v.hash)
|
||||
out[len(out)-1] = byte(v.signature)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *extensionSupportedSignatureAlgorithms) Unmarshal(data []byte) error {
|
||||
if len(data) <= extensionSupportedSignatureAlgorithmsHeaderSize {
|
||||
return errBufferTooSmall
|
||||
} else if extensionValue(binary.BigEndian.Uint16(data)) != e.extensionValue() {
|
||||
return errInvalidExtensionType
|
||||
}
|
||||
|
||||
algorithmCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
|
||||
if extensionSupportedSignatureAlgorithmsHeaderSize+(algorithmCount*2) > len(data) {
|
||||
return errLengthMismatch
|
||||
}
|
||||
for i := 0; i < algorithmCount; i++ {
|
||||
supportedHashAlgorithm := HashAlgorithm(data[extensionSupportedSignatureAlgorithmsHeaderSize+(i*2)])
|
||||
supportedSignatureAlgorithm := signatureAlgorithm(data[extensionSupportedSignatureAlgorithmsHeaderSize+(i*2)+1])
|
||||
if _, ok := hashAlgorithms[supportedHashAlgorithm]; ok {
|
||||
if _, ok := signatureAlgorithms[supportedSignatureAlgorithm]; ok {
|
||||
e.signatureHashAlgorithms = append(e.signatureHashAlgorithms, signatureHashAlgorithm{
|
||||
supportedHashAlgorithm,
|
||||
supportedSignatureAlgorithm,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
53
vendor/github.com/pion/dtls/extension_use_srtp.go
generated
Normal file
53
vendor/github.com/pion/dtls/extension_use_srtp.go
generated
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package dtls
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
extensionUseSRTPHeaderSize = 6
|
||||
)
|
||||
|
||||
// https://tools.ietf.org/html/rfc8422
|
||||
type extensionUseSRTP struct {
|
||||
protectionProfiles []SRTPProtectionProfile
|
||||
}
|
||||
|
||||
func (e extensionUseSRTP) extensionValue() extensionValue {
|
||||
return extensionUseSRTPValue
|
||||
}
|
||||
|
||||
func (e *extensionUseSRTP) Marshal() ([]byte, error) {
|
||||
out := make([]byte, extensionUseSRTPHeaderSize)
|
||||
|
||||
binary.BigEndian.PutUint16(out, uint16(e.extensionValue()))
|
||||
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(e.protectionProfiles)*2)+ /* MKI Length */ 1))
|
||||
binary.BigEndian.PutUint16(out[4:], uint16(len(e.protectionProfiles)*2))
|
||||
|
||||
for _, v := range e.protectionProfiles {
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
|
||||
}
|
||||
|
||||
out = append(out, 0x00) /* MKI Length */
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *extensionUseSRTP) Unmarshal(data []byte) error {
|
||||
if len(data) <= extensionUseSRTPHeaderSize {
|
||||
return errBufferTooSmall
|
||||
} else if extensionValue(binary.BigEndian.Uint16(data)) != e.extensionValue() {
|
||||
return errInvalidExtensionType
|
||||
}
|
||||
|
||||
profileCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
|
||||
if extensionSupportedGroupsHeaderSize+(profileCount*2) > len(data) {
|
||||
return errLengthMismatch
|
||||
}
|
||||
|
||||
for i := 0; i < profileCount; i++ {
|
||||
supportedProfile := SRTPProtectionProfile(binary.BigEndian.Uint16(data[(extensionUseSRTPHeaderSize + (i * 2)):]))
|
||||
if _, ok := srtpProtectionProfiles[supportedProfile]; ok {
|
||||
e.protectionProfiles = append(e.protectionProfiles, supportedProfile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
33
vendor/github.com/pion/dtls/fingerprint.go
generated
Normal file
33
vendor/github.com/pion/dtls/fingerprint.go
generated
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm
|
||||
func Fingerprint(cert *x509.Certificate, algo HashAlgorithm) (string, error) {
|
||||
digest := []byte(fmt.Sprintf("%x", algo.digest(cert.Raw)))
|
||||
|
||||
digestlen := len(digest)
|
||||
if digestlen == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if digestlen%2 != 0 {
|
||||
return "", errors.New("invalid fingerprint length")
|
||||
}
|
||||
res := make([]byte, digestlen>>1+digestlen-1)
|
||||
|
||||
pos := 0
|
||||
for i, c := range digest {
|
||||
res[pos] = c
|
||||
pos++
|
||||
if (i)%2 != 0 && i < digestlen-1 {
|
||||
res[pos] = byte(':')
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return string(res), nil
|
||||
}
|
||||
102
vendor/github.com/pion/dtls/flight.go
generated
Normal file
102
vendor/github.com/pion/dtls/flight.go
generated
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package dtls
|
||||
|
||||
import "sync"
|
||||
|
||||
/*
|
||||
DTLS messages are grouped into a series of message flights, according
|
||||
to the diagrams below. Although each flight of messages may consist
|
||||
of a number of messages, they should be viewed as monolithic for the
|
||||
purpose of timeout and retransmission.
|
||||
https://tools.ietf.org/html/rfc4347#section-4.2.4
|
||||
Client Server
|
||||
------ ------
|
||||
Waiting Flight 0
|
||||
|
||||
ClientHello --------> Flight 1
|
||||
|
||||
<------- HelloVerifyRequest Flight 2
|
||||
|
||||
ClientHello --------> Flight 3
|
||||
|
||||
ServerHello \
|
||||
Certificate* \
|
||||
ServerKeyExchange* Flight 4
|
||||
CertificateRequest* /
|
||||
<-------- ServerHelloDone /
|
||||
|
||||
Certificate* \
|
||||
ClientKeyExchange \
|
||||
CertificateVerify* Flight 5
|
||||
[ChangeCipherSpec] /
|
||||
Finished --------> /
|
||||
|
||||
[ChangeCipherSpec] \ Flight 6
|
||||
<-------- Finished /
|
||||
|
||||
*/
|
||||
|
||||
type flightVal uint8
|
||||
|
||||
const (
|
||||
flight0 flightVal = iota + 1
|
||||
flight1
|
||||
flight2
|
||||
flight3
|
||||
flight4
|
||||
flight5
|
||||
flight6
|
||||
)
|
||||
|
||||
func (f flightVal) String() string {
|
||||
switch f {
|
||||
case flight0:
|
||||
return "Flight 0"
|
||||
case flight1:
|
||||
return "Flight 1"
|
||||
case flight2:
|
||||
return "Flight 2"
|
||||
case flight3:
|
||||
return "Flight 3"
|
||||
case flight4:
|
||||
return "Flight 4"
|
||||
case flight5:
|
||||
return "Flight 5"
|
||||
case flight6:
|
||||
return "Flight 6"
|
||||
default:
|
||||
return "Invalid Flight"
|
||||
}
|
||||
}
|
||||
|
||||
type flight struct {
|
||||
sync.RWMutex
|
||||
val flightVal
|
||||
workerTrigger chan struct{} // Temporary way to trigger next flight
|
||||
}
|
||||
|
||||
func newFlight(isClient bool) *flight {
|
||||
val := flight0
|
||||
if isClient {
|
||||
val = flight1
|
||||
}
|
||||
return &flight{val: val, workerTrigger: make(chan struct{}, 1)}
|
||||
}
|
||||
|
||||
func (f *flight) get() flightVal {
|
||||
f.RLock()
|
||||
defer f.RUnlock()
|
||||
return f.val
|
||||
}
|
||||
|
||||
func (f *flight) set(val flightVal) error {
|
||||
f.Lock()
|
||||
f.val = val // TODO ensure no invalid transitions
|
||||
f.Unlock()
|
||||
|
||||
select {
|
||||
case f.workerTrigger <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
93
vendor/github.com/pion/dtls/fragment_buffer.go
generated
Normal file
93
vendor/github.com/pion/dtls/fragment_buffer.go
generated
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package dtls
|
||||
|
||||
type fragment struct {
|
||||
recordLayerHeader recordLayerHeader
|
||||
handshakeHeader handshakeHeader
|
||||
data []byte
|
||||
}
|
||||
|
||||
type fragmentBuffer struct {
|
||||
// map of MessageSequenceNumbers that hold slices of fragments
|
||||
cache map[uint16][]*fragment
|
||||
|
||||
currentMessageSequenceNumber uint16
|
||||
}
|
||||
|
||||
func newFragmentBuffer() *fragmentBuffer {
|
||||
return &fragmentBuffer{cache: map[uint16][]*fragment{}}
|
||||
}
|
||||
|
||||
// Attempts to push a DTLS packet to the fragmentBuffer
|
||||
// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled
|
||||
// when an error returns it is fatal, and the DTLS connection should be stopped
|
||||
func (f *fragmentBuffer) push(buf []byte) (bool, error) {
|
||||
frag := new(fragment)
|
||||
if err := frag.recordLayerHeader.Unmarshal(buf); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// fragment isn't a handshake, we don't need to handle it
|
||||
if frag.recordLayerHeader.contentType != contentTypeHandshake {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := frag.handshakeHeader.Unmarshal(buf[recordLayerHeaderSize:]); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, ok := f.cache[frag.handshakeHeader.messageSequence]; !ok {
|
||||
f.cache[frag.handshakeHeader.messageSequence] = []*fragment{}
|
||||
}
|
||||
|
||||
// Discard all headers, when rebuilding the packet we will re-build
|
||||
frag.data = append([]byte{}, buf[recordLayerHeaderSize+handshakeHeaderLength:]...)
|
||||
f.cache[frag.handshakeHeader.messageSequence] = append(f.cache[frag.handshakeHeader.messageSequence], frag)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (f *fragmentBuffer) pop() []byte {
|
||||
frags, ok := f.cache[f.currentMessageSequenceNumber]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Go doesn't support recursive lambdas
|
||||
var appendMessage func(targetOffset uint32) bool
|
||||
|
||||
rawMessage := []byte{}
|
||||
appendMessage = func(targetOffset uint32) bool {
|
||||
for _, f := range frags {
|
||||
if f.handshakeHeader.fragmentOffset == targetOffset {
|
||||
fragmentEnd := (f.handshakeHeader.fragmentOffset + f.handshakeHeader.fragmentLength)
|
||||
if fragmentEnd != f.handshakeHeader.length {
|
||||
if !appendMessage(fragmentEnd) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
rawMessage = append(f.data, rawMessage...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Recursively collect up
|
||||
if !appendMessage(0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
firstHeader := frags[0].handshakeHeader
|
||||
firstHeader.fragmentOffset = 0
|
||||
firstHeader.fragmentLength = firstHeader.length
|
||||
|
||||
rawHeader, err := firstHeader.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(f.cache, f.currentMessageSequenceNumber)
|
||||
f.currentMessageSequenceNumber++
|
||||
return append(rawHeader, rawMessage...)
|
||||
}
|
||||
36
vendor/github.com/pion/dtls/fuzz.go
generated
Normal file
36
vendor/github.com/pion/dtls/fuzz.go
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// +build gofuzz
|
||||
|
||||
package dtls
|
||||
|
||||
import "fmt"
|
||||
|
||||
func partialHeaderMismatch(a, b recordLayerHeader) bool {
|
||||
// Ignoring content length for now.
|
||||
a.contentLen = b.contentLen
|
||||
return a != b
|
||||
}
|
||||
|
||||
func FuzzRecordLayer(data []byte) int {
|
||||
var r recordLayer
|
||||
if err := r.Unmarshal(data); err != nil {
|
||||
return 0
|
||||
}
|
||||
buf, err := r.Marshal()
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
panic("zero buff")
|
||||
}
|
||||
var nr recordLayer
|
||||
if err = nr.Unmarshal(data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if partialHeaderMismatch(nr.recordLayerHeader, r.recordLayerHeader) {
|
||||
panic(fmt.Sprintf("header mismatch: %+v != %+v",
|
||||
nr.recordLayerHeader, r.recordLayerHeader,
|
||||
))
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
7
vendor/github.com/pion/dtls/go.mod
generated
vendored
Normal file
7
vendor/github.com/pion/dtls/go.mod
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
module github.com/pion/dtls
|
||||
|
||||
require (
|
||||
github.com/pion/logging v0.2.1
|
||||
github.com/pion/transport v0.6.0
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5
|
||||
)
|
||||
11
vendor/github.com/pion/dtls/go.sum
generated
vendored
Normal file
11
vendor/github.com/pion/dtls/go.sum
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pion/logging v0.2.1 h1:LwASkBKZ+2ysGJ+jLv1E/9H1ge0k1nTfi1X+5zirkDk=
|
||||
github.com/pion/logging v0.2.1/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/transport v0.6.0 h1:WAoyJg/6OI8dhCVFl/0JHTMd1iu2iHgGUXevptMtJ3U=
|
||||
github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
136
vendor/github.com/pion/dtls/handshake.go
generated
Normal file
136
vendor/github.com/pion/dtls/handshake.go
generated
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package dtls
|
||||
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4
|
||||
type handshakeType uint8
|
||||
|
||||
const (
|
||||
handshakeTypeHelloRequest handshakeType = 0
|
||||
handshakeTypeClientHello handshakeType = 1
|
||||
handshakeTypeServerHello handshakeType = 2
|
||||
handshakeTypeHelloVerifyRequest handshakeType = 3
|
||||
handshakeTypeCertificate handshakeType = 11
|
||||
handshakeTypeServerKeyExchange handshakeType = 12
|
||||
handshakeTypeCertificateRequest handshakeType = 13
|
||||
handshakeTypeServerHelloDone handshakeType = 14
|
||||
handshakeTypeCertificateVerify handshakeType = 15
|
||||
handshakeTypeClientKeyExchange handshakeType = 16
|
||||
handshakeTypeFinished handshakeType = 20
|
||||
|
||||
// msg_len for Handshake messages assumes an extra 12 bytes for
|
||||
// sequence, fragment and version information
|
||||
handshakeMessageHeaderLength = 12
|
||||
)
|
||||
|
||||
type handshakeMessage interface {
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal(data []byte) error
|
||||
|
||||
handshakeType() handshakeType
|
||||
}
|
||||
|
||||
func (h handshakeType) String() string {
|
||||
switch h {
|
||||
case handshakeTypeHelloRequest:
|
||||
return "HelloRequest"
|
||||
case handshakeTypeClientHello:
|
||||
return "ClientHello"
|
||||
case handshakeTypeServerHello:
|
||||
return "ServerHello"
|
||||
case handshakeTypeHelloVerifyRequest:
|
||||
return "HelloVerifyRequest"
|
||||
case handshakeTypeCertificate:
|
||||
return "TypeCertificate"
|
||||
case handshakeTypeServerKeyExchange:
|
||||
return "ServerKeyExchange"
|
||||
case handshakeTypeCertificateRequest:
|
||||
return "CertificateRequest"
|
||||
case handshakeTypeServerHelloDone:
|
||||
return "ServerHelloDone"
|
||||
case handshakeTypeCertificateVerify:
|
||||
return "CertificateVerify"
|
||||
case handshakeTypeClientKeyExchange:
|
||||
return "ClientKeyExchange"
|
||||
case handshakeTypeFinished:
|
||||
return "Finished"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The handshake protocol is responsible for selecting a cipher spec and
|
||||
// generating a master secret, which together comprise the primary
|
||||
// cryptographic parameters associated with a secure session. The
|
||||
// handshake protocol can also optionally authenticate parties who have
|
||||
// certificates signed by a trusted certificate authority.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.3
|
||||
type handshake struct {
|
||||
handshakeHeader handshakeHeader
|
||||
handshakeMessage handshakeMessage
|
||||
}
|
||||
|
||||
func (h handshake) contentType() contentType {
|
||||
return contentTypeHandshake
|
||||
}
|
||||
|
||||
func (h *handshake) Marshal() ([]byte, error) {
|
||||
if h.handshakeMessage == nil {
|
||||
return nil, errHandshakeMessageUnset
|
||||
} else if h.handshakeHeader.fragmentOffset != 0 {
|
||||
return nil, errUnableToMarshalFragmented
|
||||
}
|
||||
|
||||
msg, err := h.handshakeMessage.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.handshakeHeader.length = uint32(len(msg))
|
||||
h.handshakeHeader.fragmentLength = h.handshakeHeader.length
|
||||
h.handshakeHeader.handshakeType = h.handshakeMessage.handshakeType()
|
||||
header, err := h.handshakeHeader.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(header, msg...), nil
|
||||
}
|
||||
|
||||
func (h *handshake) Unmarshal(data []byte) error {
|
||||
if err := h.handshakeHeader.Unmarshal(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reportedLen := bigEndianUint24(data[1:])
|
||||
if uint32(len(data)-handshakeMessageHeaderLength) != reportedLen {
|
||||
return errLengthMismatch
|
||||
} else if reportedLen != h.handshakeHeader.fragmentLength {
|
||||
return errLengthMismatch
|
||||
}
|
||||
|
||||
switch handshakeType(data[0]) {
|
||||
case handshakeTypeHelloRequest:
|
||||
return errNotImplemented
|
||||
case handshakeTypeClientHello:
|
||||
h.handshakeMessage = &handshakeMessageClientHello{}
|
||||
case handshakeTypeHelloVerifyRequest:
|
||||
h.handshakeMessage = &handshakeMessageHelloVerifyRequest{}
|
||||
case handshakeTypeServerHello:
|
||||
h.handshakeMessage = &handshakeMessageServerHello{}
|
||||
case handshakeTypeCertificate:
|
||||
h.handshakeMessage = &handshakeMessageCertificate{}
|
||||
case handshakeTypeServerKeyExchange:
|
||||
h.handshakeMessage = &handshakeMessageServerKeyExchange{}
|
||||
case handshakeTypeCertificateRequest:
|
||||
h.handshakeMessage = &handshakeMessageCertificateRequest{}
|
||||
case handshakeTypeServerHelloDone:
|
||||
h.handshakeMessage = &handshakeMessageServerHelloDone{}
|
||||
case handshakeTypeClientKeyExchange:
|
||||
h.handshakeMessage = &handshakeMessageClientKeyExchange{}
|
||||
case handshakeTypeFinished:
|
||||
h.handshakeMessage = &handshakeMessageFinished{}
|
||||
case handshakeTypeCertificateVerify:
|
||||
h.handshakeMessage = &handshakeMessageCertificateVerify{}
|
||||
default:
|
||||
return errNotImplemented
|
||||
}
|
||||
return h.handshakeMessage.Unmarshal(data[handshakeMessageHeaderLength:])
|
||||
}
|
||||
80
vendor/github.com/pion/dtls/handshake_cache.go
generated
Normal file
80
vendor/github.com/pion/dtls/handshake_cache.go
generated
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package dtls
|
||||
|
||||
import "sync"
|
||||
|
||||
type handshakeCacheItem struct {
|
||||
typ handshakeType
|
||||
isClient bool
|
||||
messageSequence uint16
|
||||
data []byte
|
||||
}
|
||||
|
||||
type handshakeCachePullRule struct {
|
||||
typ handshakeType
|
||||
isClient bool
|
||||
}
|
||||
|
||||
type handshakeCache struct {
|
||||
cache []*handshakeCacheItem
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newHandshakeCache() *handshakeCache {
|
||||
return &handshakeCache{}
|
||||
}
|
||||
|
||||
func (h *handshakeCache) push(data []byte, messageSequence uint16, typ handshakeType, isClient bool) bool {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
for _, i := range h.cache {
|
||||
if i.messageSequence == messageSequence &&
|
||||
i.isClient == isClient {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
h.cache = append(h.cache, &handshakeCacheItem{
|
||||
data: append([]byte{}, data...),
|
||||
messageSequence: messageSequence,
|
||||
typ: typ,
|
||||
isClient: isClient,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
// returns a list handshakes that match the requested rules
|
||||
// the list will contain null entries for rules that can't be satisfied
|
||||
// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
|
||||
func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
out := make([]*handshakeCacheItem, len(rules))
|
||||
for i, r := range rules {
|
||||
for _, c := range h.cache {
|
||||
if c.typ == r.typ && c.isClient == r.isClient {
|
||||
switch {
|
||||
case out[i] == nil:
|
||||
out[i] = c
|
||||
case out[i].messageSequence < c.messageSequence:
|
||||
out[i] = c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// pullAndMerge calls pull and then merges the results, ignoring any null entries
|
||||
func (h *handshakeCache) pullAndMerge(rules ...handshakeCachePullRule) []byte {
|
||||
merged := []byte{}
|
||||
|
||||
for _, p := range h.pull(rules...) {
|
||||
if p != nil {
|
||||
merged = append(merged, p.data...)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
41
vendor/github.com/pion/dtls/handshake_header.go
generated
Normal file
41
vendor/github.com/pion/dtls/handshake_header.go
generated
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// msg_len for Handshake messages assumes an extra 12 bytes for
|
||||
// sequence, fragment and version information
|
||||
const handshakeHeaderLength = 12
|
||||
|
||||
type handshakeHeader struct {
|
||||
handshakeType handshakeType
|
||||
length uint32 // uint24 in spec
|
||||
messageSequence uint16
|
||||
fragmentOffset uint32 // uint24 in spec
|
||||
fragmentLength uint32 // uint24 in spec
|
||||
}
|
||||
|
||||
func (h *handshakeHeader) Marshal() ([]byte, error) {
|
||||
out := make([]byte, handshakeMessageHeaderLength)
|
||||
|
||||
out[0] = byte(h.handshakeType)
|
||||
putBigEndianUint24(out[1:], h.length)
|
||||
binary.BigEndian.PutUint16(out[4:], h.messageSequence)
|
||||
putBigEndianUint24(out[6:], h.fragmentOffset)
|
||||
putBigEndianUint24(out[9:], h.fragmentLength)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeHeader) Unmarshal(data []byte) error {
|
||||
if len(data) < handshakeHeaderLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
h.handshakeType = handshakeType(data[0])
|
||||
h.length = bigEndianUint24(data[1:])
|
||||
h.messageSequence = binary.BigEndian.Uint16(data[4:])
|
||||
h.fragmentOffset = bigEndianUint24(data[6:])
|
||||
h.fragmentLength = bigEndianUint24(data[9:])
|
||||
return nil
|
||||
}
|
||||
47
vendor/github.com/pion/dtls/handshake_message_certificate.go
generated
Normal file
47
vendor/github.com/pion/dtls/handshake_message_certificate.go
generated
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
type handshakeMessageCertificate struct {
|
||||
certificate *x509.Certificate
|
||||
}
|
||||
|
||||
func (h handshakeMessageCertificate) handshakeType() handshakeType {
|
||||
return handshakeTypeCertificate
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificate) Marshal() ([]byte, error) {
|
||||
if h.certificate == nil {
|
||||
return nil, errCertificateUnset
|
||||
}
|
||||
|
||||
out := make([]byte, 6)
|
||||
putBigEndianUint24(out, uint32(len(h.certificate.Raw))+3)
|
||||
putBigEndianUint24(out[3:], uint32(len(h.certificate.Raw)))
|
||||
|
||||
return append(out, h.certificate.Raw...), nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificate) Unmarshal(data []byte) error {
|
||||
if len(data) < 6 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
certificateBodyLen := int(bigEndianUint24(data))
|
||||
certificateLen := int(bigEndianUint24(data[3:]))
|
||||
if certificateBodyLen+3 != len(data) {
|
||||
return errLengthMismatch
|
||||
} else if certificateLen+6 != len(data) {
|
||||
return errLengthMismatch
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(data[6:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.certificate = cert
|
||||
|
||||
return nil
|
||||
}
|
||||
91
vendor/github.com/pion/dtls/handshake_message_certificate_request.go
generated
Normal file
91
vendor/github.com/pion/dtls/handshake_message_certificate_request.go
generated
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
/*
|
||||
A non-anonymous server can optionally request a certificate from
|
||||
the client, if appropriate for the selected cipher suite. This
|
||||
message, if sent, will immediately follow the ServerKeyExchange
|
||||
message (if it is sent; otherwise, this message follows the
|
||||
server's Certificate message).
|
||||
*/
|
||||
|
||||
type handshakeMessageCertificateRequest struct {
|
||||
certificateTypes []clientCertificateType
|
||||
signatureHashAlgorithms []signatureHashAlgorithm
|
||||
}
|
||||
|
||||
const (
|
||||
handshakeMessageCertificateRequestMinLength = 5
|
||||
)
|
||||
|
||||
func (h handshakeMessageCertificateRequest) handshakeType() handshakeType {
|
||||
return handshakeTypeCertificateRequest
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificateRequest) Marshal() ([]byte, error) {
|
||||
out := []byte{byte(len(h.certificateTypes))}
|
||||
for _, v := range h.certificateTypes {
|
||||
out = append(out, byte(v))
|
||||
}
|
||||
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(h.signatureHashAlgorithms)*2))
|
||||
for _, v := range h.signatureHashAlgorithms {
|
||||
out = append(out, byte(v.hash))
|
||||
out = append(out, byte(v.signature))
|
||||
}
|
||||
|
||||
out = append(out, []byte{0x00, 0x00}...) // Distinguished Names Length
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificateRequest) Unmarshal(data []byte) error {
|
||||
if len(data) < handshakeMessageCertificateRequestMinLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
offset := 0
|
||||
certificateTypesLength := int(data[0])
|
||||
offset++
|
||||
|
||||
if (offset + certificateTypesLength) > len(data) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
for i := 0; i < certificateTypesLength; i++ {
|
||||
certType := clientCertificateType(data[offset+i])
|
||||
if _, ok := clientCertificateTypes[certType]; ok {
|
||||
h.certificateTypes = append(h.certificateTypes, certType)
|
||||
}
|
||||
}
|
||||
offset += certificateTypesLength
|
||||
if len(data) < offset+2 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
signatureHashAlgorithmsLength := int(binary.BigEndian.Uint16(data[offset:]))
|
||||
offset += 2
|
||||
|
||||
if (offset + signatureHashAlgorithmsLength) > len(data) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
for i := 0; i < signatureHashAlgorithmsLength; i += 2 {
|
||||
if len(data) < (offset + i + 2) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
hash := HashAlgorithm(data[offset+i])
|
||||
signature := signatureAlgorithm(data[offset+i+1])
|
||||
|
||||
if _, ok := hashAlgorithms[hash]; !ok {
|
||||
continue
|
||||
} else if _, ok := signatureAlgorithms[signature]; !ok {
|
||||
continue
|
||||
}
|
||||
h.signatureHashAlgorithms = append(h.signatureHashAlgorithms, signatureHashAlgorithm{signature: signature, hash: hash})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
51
vendor/github.com/pion/dtls/handshake_message_certificate_verify.go
generated
Normal file
51
vendor/github.com/pion/dtls/handshake_message_certificate_verify.go
generated
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type handshakeMessageCertificateVerify struct {
|
||||
hashAlgorithm HashAlgorithm
|
||||
signatureAlgorithm signatureAlgorithm
|
||||
signature []byte
|
||||
}
|
||||
|
||||
const handshakeMessageCertificateVerifyMinLength = 4
|
||||
|
||||
func (h handshakeMessageCertificateVerify) handshakeType() handshakeType {
|
||||
return handshakeTypeCertificateVerify
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificateVerify) Marshal() ([]byte, error) {
|
||||
out := make([]byte, 1+1+2+len(h.signature))
|
||||
|
||||
out[0] = byte(h.hashAlgorithm)
|
||||
out[1] = byte(h.signatureAlgorithm)
|
||||
binary.BigEndian.PutUint16(out[2:], uint16(len(h.signature)))
|
||||
copy(out[4:], h.signature)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageCertificateVerify) Unmarshal(data []byte) error {
|
||||
if len(data) < handshakeMessageCertificateVerifyMinLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
h.hashAlgorithm = HashAlgorithm(data[0])
|
||||
if _, ok := hashAlgorithms[h.hashAlgorithm]; !ok {
|
||||
return errInvalidHashAlgorithm
|
||||
}
|
||||
|
||||
h.signatureAlgorithm = signatureAlgorithm(data[1])
|
||||
if _, ok := signatureAlgorithms[h.signatureAlgorithm]; !ok {
|
||||
return errInvalidSignatureAlgorithm
|
||||
}
|
||||
|
||||
signatureLength := int(binary.BigEndian.Uint16(data[2:]))
|
||||
if (signatureLength + 4) != len(data) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
h.signature = append([]byte{}, data[4:]...)
|
||||
return nil
|
||||
}
|
||||
119
vendor/github.com/pion/dtls/handshake_message_client_hello.go
generated
Normal file
119
vendor/github.com/pion/dtls/handshake_message_client_hello.go
generated
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
/*
|
||||
When a client first connects to a server it is required to send
|
||||
the client hello as its first message. The client can also send a
|
||||
client hello in response to a hello request or on its own
|
||||
initiative in order to renegotiate the security parameters in an
|
||||
existing connection.
|
||||
*/
|
||||
type handshakeMessageClientHello struct {
|
||||
version protocolVersion
|
||||
random handshakeRandom
|
||||
cookie []byte
|
||||
|
||||
cipherSuites []cipherSuite
|
||||
compressionMethods []*compressionMethod
|
||||
extensions []extension
|
||||
}
|
||||
|
||||
const handshakeMessageClientHelloVariableWidthStart = 34
|
||||
|
||||
func (h handshakeMessageClientHello) handshakeType() handshakeType {
|
||||
return handshakeTypeClientHello
|
||||
}
|
||||
|
||||
func (h *handshakeMessageClientHello) Marshal() ([]byte, error) {
|
||||
if len(h.cookie) > 255 {
|
||||
return nil, errCookieTooLong
|
||||
}
|
||||
|
||||
out := make([]byte, handshakeMessageClientHelloVariableWidthStart)
|
||||
out[0] = h.version.major
|
||||
out[1] = h.version.minor
|
||||
|
||||
rand, err := h.random.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(out[2:], rand)
|
||||
|
||||
out = append(out, 0x00) // SessionID
|
||||
|
||||
out = append(out, byte(len(h.cookie)))
|
||||
out = append(out, h.cookie...)
|
||||
out = append(out, encodeCipherSuites(h.cipherSuites)...)
|
||||
out = append(out, encodeCompressionMethods(h.compressionMethods)...)
|
||||
|
||||
extensions, err := encodeExtensions(h.extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(out, extensions...), nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageClientHello) Unmarshal(data []byte) error {
|
||||
if len(data) < 2+handshakeRandomLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
h.version.major = data[0]
|
||||
h.version.minor = data[1]
|
||||
|
||||
if err := h.random.Unmarshal(data[2 : 2+handshakeRandomLength]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rest of packet has variable width sections
|
||||
currOffset := handshakeMessageClientHelloVariableWidthStart
|
||||
currOffset += int(data[currOffset]) + 1 // SessionID
|
||||
|
||||
currOffset++
|
||||
if len(data) < currOffset {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.cookie = append([]byte{}, data[currOffset:currOffset+int(data[currOffset-1])]...)
|
||||
currOffset += len(h.cookie)
|
||||
|
||||
// Cipher Suites
|
||||
if len(data) < currOffset {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
cipherSuites, err := decodeCipherSuites(data[currOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.cipherSuites = cipherSuites
|
||||
if len(data) < currOffset+2 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2
|
||||
|
||||
// Compression Methods
|
||||
if len(data) < currOffset {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
compressionMethods, err := decodeCompressionMethods(data[currOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.compressionMethods = compressionMethods
|
||||
if len(data) < currOffset {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
currOffset += int(data[currOffset]) + 1
|
||||
|
||||
// Extensions
|
||||
extensions, err := decodeExtensions(data[currOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.extensions = extensions
|
||||
|
||||
return nil
|
||||
}
|
||||
25
vendor/github.com/pion/dtls/handshake_message_client_key_exchange.go
generated
Normal file
25
vendor/github.com/pion/dtls/handshake_message_client_key_exchange.go
generated
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package dtls
|
||||
|
||||
type handshakeMessageClientKeyExchange struct {
|
||||
publicKey []byte
|
||||
}
|
||||
|
||||
func (h handshakeMessageClientKeyExchange) handshakeType() handshakeType {
|
||||
return handshakeTypeClientKeyExchange
|
||||
}
|
||||
|
||||
func (h *handshakeMessageClientKeyExchange) Marshal() ([]byte, error) {
|
||||
return append([]byte{byte(len(h.publicKey))}, h.publicKey...), nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageClientKeyExchange) Unmarshal(data []byte) error {
|
||||
if len(data) < 1 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
publicKeyLength := int(data[0])
|
||||
if len(data) <= publicKeyLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.publicKey = append([]byte{}, data[1:]...)
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/pion/dtls/handshake_message_finished.go
generated
Normal file
18
vendor/github.com/pion/dtls/handshake_message_finished.go
generated
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package dtls
|
||||
|
||||
type handshakeMessageFinished struct {
|
||||
verifyData []byte
|
||||
}
|
||||
|
||||
func (h handshakeMessageFinished) handshakeType() handshakeType {
|
||||
return handshakeTypeFinished
|
||||
}
|
||||
|
||||
func (h *handshakeMessageFinished) Marshal() ([]byte, error) {
|
||||
return append([]byte{}, h.verifyData...), nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageFinished) Unmarshal(data []byte) error {
|
||||
h.verifyData = append([]byte{}, data...)
|
||||
return nil
|
||||
}
|
||||
57
vendor/github.com/pion/dtls/handshake_message_hello_verify_request.go
generated
Normal file
57
vendor/github.com/pion/dtls/handshake_message_hello_verify_request.go
generated
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package dtls
|
||||
|
||||
/*
|
||||
The definition of HelloVerifyRequest is as follows:
|
||||
|
||||
struct {
|
||||
ProtocolVersion server_version;
|
||||
opaque cookie<0..2^8-1>;
|
||||
} HelloVerifyRequest;
|
||||
|
||||
The HelloVerifyRequest message type is hello_verify_request(3).
|
||||
|
||||
When the client sends its ClientHello message to the server, the server
|
||||
MAY respond with a HelloVerifyRequest message. This message contains
|
||||
a stateless cookie generated using the technique of [PHOTURIS]. The
|
||||
client MUST retransmit the ClientHello with the cookie added.
|
||||
|
||||
https://tools.ietf.org/html/rfc6347#section-4.2.1
|
||||
*/
|
||||
type handshakeMessageHelloVerifyRequest struct {
|
||||
version protocolVersion
|
||||
cookie []byte
|
||||
}
|
||||
|
||||
func (h handshakeMessageHelloVerifyRequest) handshakeType() handshakeType {
|
||||
return handshakeTypeHelloVerifyRequest
|
||||
}
|
||||
|
||||
func (h *handshakeMessageHelloVerifyRequest) Marshal() ([]byte, error) {
|
||||
if len(h.cookie) > 255 {
|
||||
return nil, errCookieTooLong
|
||||
}
|
||||
|
||||
out := make([]byte, 3+len(h.cookie))
|
||||
out[0] = h.version.major
|
||||
out[1] = h.version.minor
|
||||
out[2] = byte(len(h.cookie))
|
||||
copy(out[3:], h.cookie)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageHelloVerifyRequest) Unmarshal(data []byte) error {
|
||||
if len(data) < 3 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.version.major = data[0]
|
||||
h.version.minor = data[1]
|
||||
cookieLength := data[2]
|
||||
if len(data) < (int(cookieLength) + 3) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.cookie = make([]byte, cookieLength)
|
||||
|
||||
copy(h.cookie, data[3:3+cookieLength])
|
||||
return nil
|
||||
}
|
||||
105
vendor/github.com/pion/dtls/handshake_message_server_hello.go
generated
Normal file
105
vendor/github.com/pion/dtls/handshake_message_server_hello.go
generated
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
/*
|
||||
The server will send this message in response to a ClientHello
|
||||
message when it was able to find an acceptable set of algorithms.
|
||||
If it cannot find such a match, it will respond with a handshake
|
||||
failure alert.
|
||||
https://tools.ietf.org/html/rfc5246#section-7.4.1.3
|
||||
*/
|
||||
type handshakeMessageServerHello struct {
|
||||
version protocolVersion
|
||||
random handshakeRandom
|
||||
|
||||
cipherSuite cipherSuite
|
||||
compressionMethod *compressionMethod
|
||||
extensions []extension
|
||||
}
|
||||
|
||||
const handshakeMessageServerHelloVariableWidthStart = 2 + handshakeRandomLength
|
||||
|
||||
func (h handshakeMessageServerHello) handshakeType() handshakeType {
|
||||
return handshakeTypeServerHello
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerHello) Marshal() ([]byte, error) {
|
||||
if h.cipherSuite == nil {
|
||||
return nil, errCipherSuiteUnset
|
||||
} else if h.compressionMethod == nil {
|
||||
return nil, errCompressionmethodUnset
|
||||
}
|
||||
|
||||
out := make([]byte, handshakeMessageServerHelloVariableWidthStart)
|
||||
out[0] = h.version.major
|
||||
out[1] = h.version.minor
|
||||
|
||||
rand, err := h.random.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(out[2:], rand)
|
||||
|
||||
out = append(out, 0x00) // SessionID
|
||||
|
||||
out = append(out, []byte{0x00, 0x00}...)
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(h.cipherSuite.ID()))
|
||||
|
||||
out = append(out, byte(h.compressionMethod.id))
|
||||
|
||||
extensions, err := encodeExtensions(h.extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(out, extensions...), nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerHello) Unmarshal(data []byte) error {
|
||||
if len(data) < 2+handshakeRandomLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
h.version.major = data[0]
|
||||
h.version.minor = data[1]
|
||||
|
||||
if err := h.random.Unmarshal(data[2 : 2+handshakeRandomLength]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currOffset := handshakeMessageServerHelloVariableWidthStart
|
||||
currOffset += int(data[currOffset]) + 1 // SessionID
|
||||
if len(data) < (currOffset + 2) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
if c := cipherSuiteForID(CipherSuiteID(binary.BigEndian.Uint16(data[currOffset:]))); c != nil {
|
||||
h.cipherSuite = c
|
||||
currOffset += 2
|
||||
} else {
|
||||
return errInvalidCipherSuite
|
||||
}
|
||||
if len(data) < currOffset {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
if compressionMethod, ok := compressionMethods[compressionMethodID(data[currOffset])]; ok {
|
||||
h.compressionMethod = compressionMethod
|
||||
currOffset++
|
||||
} else {
|
||||
return errInvalidCompressionMethod
|
||||
}
|
||||
|
||||
if len(data) <= currOffset {
|
||||
h.extensions = []extension{}
|
||||
return nil
|
||||
}
|
||||
|
||||
extensions, err := decodeExtensions(data[currOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.extensions = extensions
|
||||
return nil
|
||||
}
|
||||
16
vendor/github.com/pion/dtls/handshake_message_server_hello_done.go
generated
Normal file
16
vendor/github.com/pion/dtls/handshake_message_server_hello_done.go
generated
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package dtls
|
||||
|
||||
type handshakeMessageServerHelloDone struct {
|
||||
}
|
||||
|
||||
func (h handshakeMessageServerHelloDone) handshakeType() handshakeType {
|
||||
return handshakeTypeServerHelloDone
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerHelloDone) Marshal() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerHelloDone) Unmarshal(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
77
vendor/github.com/pion/dtls/handshake_message_server_key_exchange.go
generated
Normal file
77
vendor/github.com/pion/dtls/handshake_message_server_key_exchange.go
generated
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Structure only supports ECDH
|
||||
type handshakeMessageServerKeyExchange struct {
|
||||
ellipticCurveType ellipticCurveType
|
||||
namedCurve namedCurve
|
||||
publicKey []byte
|
||||
hashAlgorithm HashAlgorithm
|
||||
signatureAlgorithm signatureAlgorithm
|
||||
signature []byte
|
||||
}
|
||||
|
||||
func (h handshakeMessageServerKeyExchange) handshakeType() handshakeType {
|
||||
return handshakeTypeServerKeyExchange
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerKeyExchange) Marshal() ([]byte, error) {
|
||||
out := []byte{byte(h.ellipticCurveType), 0x00, 0x00}
|
||||
binary.BigEndian.PutUint16(out[1:], uint16(h.namedCurve))
|
||||
|
||||
out = append(out, byte(len(h.publicKey)))
|
||||
out = append(out, h.publicKey...)
|
||||
|
||||
out = append(out, []byte{byte(h.hashAlgorithm), byte(h.signatureAlgorithm), 0x00, 0x00}...)
|
||||
|
||||
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(h.signature)))
|
||||
out = append(out, h.signature...)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeMessageServerKeyExchange) Unmarshal(data []byte) error {
|
||||
if len(data) < 1 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
if _, ok := ellipticCurveTypes[ellipticCurveType(data[0])]; ok {
|
||||
h.ellipticCurveType = ellipticCurveType(data[0])
|
||||
} else {
|
||||
return errInvalidEllipticCurveType
|
||||
}
|
||||
|
||||
h.namedCurve = namedCurve(binary.BigEndian.Uint16(data[1:]))
|
||||
if _, ok := namedCurves[h.namedCurve]; !ok {
|
||||
return errInvalidNamedCurve
|
||||
}
|
||||
if len(data) < 4 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
publicKeyLength := int(data[3])
|
||||
offset := 4 + publicKeyLength
|
||||
if len(data) <= publicKeyLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.publicKey = append([]byte{}, data[4:offset]...)
|
||||
|
||||
h.hashAlgorithm = HashAlgorithm(data[offset])
|
||||
if _, ok := hashAlgorithms[h.hashAlgorithm]; !ok {
|
||||
return errInvalidHashAlgorithm
|
||||
}
|
||||
offset++
|
||||
|
||||
h.signatureAlgorithm = signatureAlgorithm(data[offset])
|
||||
if _, ok := signatureAlgorithms[h.signatureAlgorithm]; !ok {
|
||||
return errInvalidSignatureAlgorithm
|
||||
}
|
||||
offset++
|
||||
|
||||
signatureLength := int(binary.BigEndian.Uint16(data[offset:]))
|
||||
offset += 2
|
||||
h.signature = append([]byte{}, data[offset:offset+signatureLength]...)
|
||||
return nil
|
||||
}
|
||||
47
vendor/github.com/pion/dtls/handshake_random.go
generated
Normal file
47
vendor/github.com/pion/dtls/handshake_random.go
generated
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
const randomBytesLength = 28
|
||||
const handshakeRandomLength = randomBytesLength + 4
|
||||
|
||||
// https://tools.ietf.org/html/rfc4346#section-7.4.1.2
|
||||
type handshakeRandom struct {
|
||||
gmtUnixTime time.Time
|
||||
randomBytes [randomBytesLength]byte
|
||||
}
|
||||
|
||||
func (h *handshakeRandom) Marshal() ([]byte, error) {
|
||||
out := make([]byte, handshakeRandomLength)
|
||||
|
||||
binary.BigEndian.PutUint32(out[0:], uint32(h.gmtUnixTime.Unix()))
|
||||
copy(out[4:], h.randomBytes[:])
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *handshakeRandom) Unmarshal(data []byte) error {
|
||||
if len(data) != handshakeRandomLength {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.gmtUnixTime = time.Unix(int64(binary.BigEndian.Uint32(data[0:])), 0)
|
||||
copy(h.randomBytes[:], data[4:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populate fills the handshakeRandom with random values
|
||||
// may be called multiple times
|
||||
func (h *handshakeRandom) populate() error {
|
||||
h.gmtUnixTime = time.Now()
|
||||
|
||||
tmp := make([]byte, randomBytesLength)
|
||||
_, err := rand.Read(tmp)
|
||||
copy(h.randomBytes[:], tmp)
|
||||
|
||||
return err
|
||||
}
|
||||
117
vendor/github.com/pion/dtls/hash_algorithm.go
generated
Normal file
117
vendor/github.com/pion/dtls/hash_algorithm.go
generated
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/md5" // #nosec
|
||||
"crypto/sha1" // #nosec
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
)
|
||||
|
||||
// HashAlgorithm is used to indicate the hash algorithm used
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
|
||||
type HashAlgorithm uint16
|
||||
|
||||
// Supported hash hash algorithms
|
||||
const (
|
||||
// HashAlgorithmMD2 HashAlgorithm = 0 // Blacklisted
|
||||
HashAlgorithmMD5 HashAlgorithm = 1 // Blacklisted
|
||||
HashAlgorithmSHA1 HashAlgorithm = 2 // Blacklisted
|
||||
HashAlgorithmSHA224 HashAlgorithm = 3
|
||||
HashAlgorithmSHA256 HashAlgorithm = 4
|
||||
HashAlgorithmSHA384 HashAlgorithm = 5
|
||||
HashAlgorithmSHA512 HashAlgorithm = 6
|
||||
)
|
||||
|
||||
// String makes HashAlgorithm printable
|
||||
func (h HashAlgorithm) String() string {
|
||||
switch h {
|
||||
case HashAlgorithmMD5:
|
||||
return "md5" // [RFC3279]
|
||||
case HashAlgorithmSHA1:
|
||||
return "sha-1" // [RFC3279]
|
||||
case HashAlgorithmSHA224:
|
||||
return "sha-224" // [RFC4055]
|
||||
case HashAlgorithmSHA256:
|
||||
return "sha-256" // [RFC4055]
|
||||
case HashAlgorithmSHA384:
|
||||
return "sha-384" // [RFC4055]
|
||||
case HashAlgorithmSHA512:
|
||||
return "sha-512" // [RFC4055]
|
||||
default:
|
||||
return "unknown hash algorithm"
|
||||
}
|
||||
}
|
||||
|
||||
// HashAlgorithmString allows looking up a HashAlgorithm by it's string representation
|
||||
func HashAlgorithmString(s string) (HashAlgorithm, error) {
|
||||
switch s {
|
||||
case "md5":
|
||||
return HashAlgorithmMD5, nil // [RFC3279]
|
||||
case "sha-1":
|
||||
return HashAlgorithmSHA1, nil // [RFC3279]
|
||||
case "sha-224":
|
||||
return HashAlgorithmSHA224, nil // [RFC4055]
|
||||
case "sha-256":
|
||||
return HashAlgorithmSHA256, nil // [RFC4055]
|
||||
case "sha-384":
|
||||
return HashAlgorithmSHA384, nil // [RFC4055]
|
||||
case "sha-512":
|
||||
return HashAlgorithmSHA512, nil // [RFC4055]
|
||||
default:
|
||||
return 0, errInvalidHashAlgorithm
|
||||
}
|
||||
}
|
||||
|
||||
func (h HashAlgorithm) digest(b []byte) []byte {
|
||||
switch h {
|
||||
case HashAlgorithmMD5:
|
||||
hash := md5.Sum(b) // #nosec
|
||||
return hash[:]
|
||||
case HashAlgorithmSHA1:
|
||||
hash := sha1.Sum(b) // #nosec
|
||||
return hash[:]
|
||||
case HashAlgorithmSHA224:
|
||||
hash := sha256.Sum224(b)
|
||||
return hash[:]
|
||||
case HashAlgorithmSHA256:
|
||||
hash := sha256.Sum256(b)
|
||||
return hash[:]
|
||||
case HashAlgorithmSHA384:
|
||||
hash := sha512.Sum384(b)
|
||||
return hash[:]
|
||||
case HashAlgorithmSHA512:
|
||||
hash := sha512.Sum512(b)
|
||||
return hash[:]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h HashAlgorithm) cryptoHash() crypto.Hash {
|
||||
switch h {
|
||||
case HashAlgorithmMD5:
|
||||
return crypto.MD5
|
||||
case HashAlgorithmSHA1:
|
||||
return crypto.SHA1
|
||||
case HashAlgorithmSHA224:
|
||||
return crypto.SHA224
|
||||
case HashAlgorithmSHA256:
|
||||
return crypto.SHA256
|
||||
case HashAlgorithmSHA384:
|
||||
return crypto.SHA384
|
||||
case HashAlgorithmSHA512:
|
||||
return crypto.SHA512
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var hashAlgorithms = map[HashAlgorithm]struct{}{
|
||||
HashAlgorithmMD5: {},
|
||||
HashAlgorithmSHA1: {},
|
||||
HashAlgorithmSHA224: {},
|
||||
HashAlgorithmSHA256: {},
|
||||
HashAlgorithmSHA384: {},
|
||||
HashAlgorithmSHA512: {},
|
||||
}
|
||||
230
vendor/github.com/pion/dtls/internal/udp/conn.go
generated
Normal file
230
vendor/github.com/pion/dtls/internal/udp/conn.go
generated
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package udp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const receiveMTU = 8192
|
||||
|
||||
var errClosedListener = errors.New("udp: listener closed")
|
||||
|
||||
// Listener augments a connection-oriented Listener over a UDP PacketConn
|
||||
type Listener struct {
|
||||
pConn *net.UDPConn
|
||||
|
||||
lock sync.RWMutex
|
||||
accepting bool
|
||||
acceptCh chan *Conn
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
|
||||
conns map[string]*Conn
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
// You have to either close or read on all connection that are created.
|
||||
func (l *Listener) Accept() (*Conn, error) {
|
||||
select {
|
||||
case c := <-l.acceptCh:
|
||||
return c, nil
|
||||
|
||||
case <-l.doneCh:
|
||||
return nil, errClosedListener
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
func (l *Listener) Close() error {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
var err error
|
||||
l.doneOnce.Do(func() {
|
||||
l.accepting = false
|
||||
close(l.doneCh)
|
||||
err = l.cleanup()
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanup closes the packet conn if it is no longer used
|
||||
// The caller should hold the read lock.
|
||||
func (l *Listener) cleanup() error {
|
||||
if !l.accepting && len(l.conns) == 0 {
|
||||
return l.pConn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return l.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// Listen creates a new listener
|
||||
func Listen(network string, laddr *net.UDPAddr) (*Listener, error) {
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := &Listener{
|
||||
pConn: conn,
|
||||
acceptCh: make(chan *Conn),
|
||||
conns: make(map[string]*Conn),
|
||||
accepting: true,
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go l.readLoop()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// readLoop has to tasks:
|
||||
// 1. Dispatching incoming packets to the correct Conn.
|
||||
// It can therefore not be ended until all Conns are closed.
|
||||
// 2. Creating a new Conn when receiving from a new remote.
|
||||
func (l *Listener) readLoop() {
|
||||
buf := make([]byte, receiveMTU)
|
||||
|
||||
readLoop:
|
||||
for {
|
||||
n, raddr, err := l.pConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := l.getConn(raddr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case cBuf := <-conn.readCh:
|
||||
n = copy(cBuf, buf[:n])
|
||||
conn.sizeCh <- n
|
||||
case <-conn.doneCh:
|
||||
continue readLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
conn, ok := l.conns[raddr.String()]
|
||||
if !ok {
|
||||
if !l.accepting {
|
||||
return nil, errClosedListener
|
||||
}
|
||||
conn = l.newConn(raddr)
|
||||
l.conns[raddr.String()] = conn
|
||||
l.acceptCh <- conn
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Conn augments a connection-oriented connection over a UDP PacketConn
|
||||
type Conn struct {
|
||||
listener *Listener
|
||||
|
||||
rAddr net.Addr
|
||||
|
||||
readCh chan []byte
|
||||
sizeCh chan int
|
||||
|
||||
lock sync.RWMutex
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
}
|
||||
|
||||
func (l *Listener) newConn(rAddr net.Addr) *Conn {
|
||||
return &Conn{
|
||||
listener: l,
|
||||
rAddr: rAddr,
|
||||
readCh: make(chan []byte),
|
||||
sizeCh: make(chan int),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Read
|
||||
func (c *Conn) Read(p []byte) (int, error) {
|
||||
select {
|
||||
case c.readCh <- p:
|
||||
n := <-c.sizeCh
|
||||
return n, nil
|
||||
case <-c.doneCh:
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the DTLS connection
|
||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||
c.lock.Lock()
|
||||
l := c.listener
|
||||
c.lock.Unlock()
|
||||
|
||||
if l == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return l.pConn.WriteTo(p, c.rAddr)
|
||||
}
|
||||
|
||||
// Close closes the conn and releases any Read calls
|
||||
func (c *Conn) Close() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
var err error
|
||||
c.doneOnce.Do(func() {
|
||||
close(c.doneCh)
|
||||
c.listener.lock.Lock()
|
||||
delete(c.listener.conns, c.rAddr.String())
|
||||
err = c.listener.cleanup()
|
||||
c.listener.lock.Unlock()
|
||||
c.listener = nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// LocalAddr is a stub
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
c.lock.Lock()
|
||||
l := c.listener
|
||||
c.lock.Unlock()
|
||||
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return l.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr is a stub
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
// SetDeadline is a stub
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline is a stub
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline is a stub
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
51
vendor/github.com/pion/dtls/listener.go
generated
Normal file
51
vendor/github.com/pion/dtls/listener.go
generated
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/pion/dtls/internal/udp"
|
||||
)
|
||||
|
||||
// Listen creates a DTLS listener
|
||||
func Listen(network string, laddr *net.UDPAddr, config *Config) (*Listener, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("no config provided")
|
||||
}
|
||||
parent, err := udp.Listen(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Listener{
|
||||
config: config,
|
||||
parent: parent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Listener represents a DTLS listener
|
||||
type Listener struct {
|
||||
config *Config
|
||||
parent *udp.Listener
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
// You have to either close or read on all connection that are created.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
c, err := l.parent.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Server(c, l.config)
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
// Already Accepted connections are not closed.
|
||||
func (l *Listener) Close() error {
|
||||
return l.parent.Close()
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return l.parent.Addr()
|
||||
}
|
||||
51
vendor/github.com/pion/dtls/named_curve.go
generated
Normal file
51
vendor/github.com/pion/dtls/named_curve.go
generated
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||
type namedCurve uint16
|
||||
|
||||
type namedCurveKeypair struct {
|
||||
curve namedCurve
|
||||
publicKey []byte
|
||||
privateKey []byte
|
||||
}
|
||||
|
||||
const (
|
||||
namedCurveP256 namedCurve = 0x0017
|
||||
namedCurveX25519 namedCurve = 0x001d
|
||||
)
|
||||
|
||||
var namedCurves = map[namedCurve]bool{
|
||||
namedCurveX25519: true,
|
||||
namedCurveP256: true,
|
||||
}
|
||||
|
||||
func generateKeypair(c namedCurve) (*namedCurveKeypair, error) {
|
||||
switch c {
|
||||
case namedCurveX25519:
|
||||
tmp := make([]byte, 32)
|
||||
if _, err := rand.Read(tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var public, private [32]byte
|
||||
copy(private[:], tmp)
|
||||
|
||||
curve25519.ScalarBaseMult(&public, &private)
|
||||
return &namedCurveKeypair{namedCurveX25519, public[:], private[:]}, nil
|
||||
case namedCurveP256:
|
||||
privateKey, x, y, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &namedCurveKeypair{namedCurveP256, elliptic.Marshal(elliptic.P256(), x, y), privateKey}, nil
|
||||
}
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
209
vendor/github.com/pion/dtls/prf.go
generated
Normal file
209
vendor/github.com/pion/dtls/prf.go
generated
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1" // #nosec
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const (
|
||||
prfMasterSecretLabel = "master secret"
|
||||
prfKeyExpansionLabel = "key expansion"
|
||||
prfVerifyDataClientLabel = "client finished"
|
||||
prfVerifyDataServerLabel = "server finished"
|
||||
)
|
||||
|
||||
type hashFunc func() hash.Hash
|
||||
|
||||
type encryptionKeys struct {
|
||||
masterSecret []byte
|
||||
clientMACKey []byte
|
||||
serverMACKey []byte
|
||||
clientWriteKey []byte
|
||||
serverWriteKey []byte
|
||||
clientWriteIV []byte
|
||||
serverWriteIV []byte
|
||||
}
|
||||
|
||||
func (e *encryptionKeys) String() string {
|
||||
return fmt.Sprintf(`encryptionKeys:
|
||||
- masterSecret: %#v
|
||||
- clientMACKey: %#v
|
||||
- serverMACKey: %#v
|
||||
- clientWriteKey: %#v
|
||||
- serverWriteKey: %#v
|
||||
- clientWriteIV: %#v
|
||||
- serverWriteIV: %#v
|
||||
`,
|
||||
e.masterSecret,
|
||||
e.clientMACKey,
|
||||
e.serverMACKey,
|
||||
e.clientWriteKey,
|
||||
e.serverWriteKey,
|
||||
e.clientWriteIV,
|
||||
e.serverWriteIV)
|
||||
}
|
||||
|
||||
func prfPreMasterSecret(publicKey, privateKey []byte, curve namedCurve) ([]byte, error) {
|
||||
switch curve {
|
||||
case namedCurveX25519:
|
||||
var preMasterSecret, fixedWidthPrivateKey, fixedWidthPublicKey [32]byte
|
||||
copy(fixedWidthPrivateKey[:], privateKey)
|
||||
copy(fixedWidthPublicKey[:], publicKey)
|
||||
|
||||
curve25519.ScalarMult(&preMasterSecret, &fixedWidthPrivateKey, &fixedWidthPublicKey)
|
||||
return preMasterSecret[:], nil
|
||||
case namedCurveP256:
|
||||
x, y := elliptic.Unmarshal(elliptic.P256(), publicKey)
|
||||
if x == nil || y == nil {
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
|
||||
curve := elliptic.P256()
|
||||
result, _ := curve.ScalarMult(x, y, privateKey)
|
||||
preMasterSecret := make([]byte, (curve.Params().BitSize+7)>>3)
|
||||
resultBytes := result.Bytes()
|
||||
copy(preMasterSecret[len(preMasterSecret)-len(resultBytes):], resultBytes)
|
||||
return preMasterSecret, nil
|
||||
}
|
||||
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
|
||||
// This PRF with the SHA-256 hash function is used for all cipher suites
|
||||
// defined in this document and in TLS documents published prior to this
|
||||
// document when TLS 1.2 is negotiated. New cipher suites MUST explicitly
|
||||
// specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
|
||||
// stronger standard hash function.
|
||||
//
|
||||
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
|
||||
// HMAC_hash(secret, A(2) + seed) +
|
||||
// HMAC_hash(secret, A(3) + seed) + ...
|
||||
//
|
||||
// A() is defined as:
|
||||
//
|
||||
// A(0) = seed
|
||||
// A(i) = HMAC_hash(secret, A(i-1))
|
||||
//
|
||||
// P_hash can be iterated as many times as necessary to produce the
|
||||
// required quantity of data. For example, if P_SHA256 is being used to
|
||||
// create 80 bytes of data, it will have to be iterated three times
|
||||
// (through A(3)), creating 96 bytes of output data; the last 16 bytes
|
||||
// of the final iteration will then be discarded, leaving 80 bytes of
|
||||
// output data.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4346w
|
||||
func prfPHash(secret, seed []byte, requestedLength int, h hashFunc) ([]byte, error) {
|
||||
hmacSHA256 := func(key, data []byte) ([]byte, error) {
|
||||
mac := hmac.New(h, key)
|
||||
if _, err := mac.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mac.Sum(nil), nil
|
||||
}
|
||||
|
||||
var err error
|
||||
lastRound := seed
|
||||
out := []byte{}
|
||||
|
||||
iterations := int(math.Ceil(float64(requestedLength) / float64(h().Size())))
|
||||
for i := 0; i < iterations; i++ {
|
||||
lastRound, err = hmacSHA256(secret, lastRound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withSecret, err := hmacSHA256(secret, append(lastRound, seed...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, withSecret...)
|
||||
}
|
||||
|
||||
return out[:requestedLength], nil
|
||||
}
|
||||
|
||||
func prfMasterSecret(preMasterSecret, clientRandom, serverRandom []byte, h hashFunc) ([]byte, error) {
|
||||
seed := append(append([]byte(prfMasterSecretLabel), clientRandom...), serverRandom...)
|
||||
return prfPHash(preMasterSecret, seed, 48, h)
|
||||
}
|
||||
|
||||
func prfEncryptionKeys(masterSecret, clientRandom, serverRandom []byte, prfMacLen, prfKeyLen, prfIvLen int, h hashFunc) (*encryptionKeys, error) {
|
||||
seed := append(append([]byte(prfKeyExpansionLabel), serverRandom...), clientRandom...)
|
||||
keyMaterial, err := prfPHash(masterSecret, seed, (2*prfMacLen)+(2*prfKeyLen)+(2*prfIvLen), h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientMACKey := keyMaterial[:prfMacLen]
|
||||
keyMaterial = keyMaterial[prfMacLen:]
|
||||
|
||||
serverMACKey := keyMaterial[:prfMacLen]
|
||||
keyMaterial = keyMaterial[prfMacLen:]
|
||||
|
||||
clientWriteKey := keyMaterial[:prfKeyLen]
|
||||
keyMaterial = keyMaterial[prfKeyLen:]
|
||||
|
||||
serverWriteKey := keyMaterial[:prfKeyLen]
|
||||
keyMaterial = keyMaterial[prfKeyLen:]
|
||||
|
||||
clientWriteIV := keyMaterial[:prfIvLen]
|
||||
keyMaterial = keyMaterial[prfIvLen:]
|
||||
|
||||
serverWriteIV := keyMaterial[:prfIvLen]
|
||||
|
||||
return &encryptionKeys{
|
||||
masterSecret: masterSecret,
|
||||
clientMACKey: clientMACKey,
|
||||
serverMACKey: serverMACKey,
|
||||
clientWriteKey: clientWriteKey,
|
||||
serverWriteKey: serverWriteKey,
|
||||
clientWriteIV: clientWriteIV,
|
||||
serverWriteIV: serverWriteIV,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func prfVerifyData(masterSecret, handshakeBodies []byte, label string, hashFunc hashFunc) ([]byte, error) {
|
||||
h := hashFunc()
|
||||
if _, err := h.Write(handshakeBodies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seed := append([]byte(label), h.Sum(nil)...)
|
||||
return prfPHash(masterSecret, seed, 12, hashFunc)
|
||||
}
|
||||
|
||||
func prfVerifyDataClient(masterSecret, handshakeBodies []byte, h hashFunc) ([]byte, error) {
|
||||
return prfVerifyData(masterSecret, handshakeBodies, prfVerifyDataClientLabel, h)
|
||||
}
|
||||
|
||||
func prfVerifyDataServer(masterSecret, handshakeBodies []byte, h hashFunc) ([]byte, error) {
|
||||
return prfVerifyData(masterSecret, handshakeBodies, prfVerifyDataServerLabel, h)
|
||||
}
|
||||
|
||||
// compute the MAC using HMAC-SHA1
|
||||
func prfMac(epoch uint16, sequenceNumber uint64, contentType contentType, protocolVersion protocolVersion, payload []byte, key []byte) ([]byte, error) {
|
||||
h := hmac.New(sha1.New, key)
|
||||
|
||||
msg := make([]byte, 13)
|
||||
|
||||
binary.BigEndian.PutUint16(msg, epoch)
|
||||
putBigEndianUint48(msg[2:], sequenceNumber)
|
||||
msg[8] = byte(contentType)
|
||||
msg[9] = protocolVersion.major
|
||||
msg[10] = protocolVersion.minor
|
||||
binary.BigEndian.PutUint16(msg[11:], uint16(len(payload)))
|
||||
|
||||
if _, err := h.Write(msg); err != nil {
|
||||
return nil, err
|
||||
} else if _, err := h.Write(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
89
vendor/github.com/pion/dtls/record_layer.go
generated
Normal file
89
vendor/github.com/pion/dtls/record_layer.go
generated
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
/*
|
||||
The TLS Record Layer which handles all data transport.
|
||||
The record layer is assumed to sit directly on top of some
|
||||
reliable transport such as TCP. The record layer can carry four types of content:
|
||||
|
||||
1. Handshake messages—used for algorithm negotiation and key establishment.
|
||||
2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
|
||||
3. Alert messages—used to signal that errors have occurred
|
||||
4. Application layer data
|
||||
|
||||
The DTLS record layer is extremely similar to that of TLS 1.1. The
|
||||
only change is the inclusion of an explicit sequence number in the
|
||||
record. This sequence number allows the recipient to correctly
|
||||
verify the TLS MAC.
|
||||
https://tools.ietf.org/html/rfc4347#section-4.1
|
||||
*/
|
||||
type recordLayer struct {
|
||||
recordLayerHeader recordLayerHeader
|
||||
content content
|
||||
}
|
||||
|
||||
func (r *recordLayer) Marshal() ([]byte, error) {
|
||||
contentRaw, err := r.content.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.recordLayerHeader.contentLen = uint16(len(contentRaw))
|
||||
r.recordLayerHeader.contentType = r.content.contentType()
|
||||
|
||||
headerRaw, err := r.recordLayerHeader.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(headerRaw, contentRaw...), nil
|
||||
}
|
||||
|
||||
func (r *recordLayer) Unmarshal(data []byte) error {
|
||||
if len(data) < recordLayerHeaderSize {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
if err := r.recordLayerHeader.Unmarshal(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch contentType(data[0]) {
|
||||
case contentTypeChangeCipherSpec:
|
||||
r.content = &changeCipherSpec{}
|
||||
case contentTypeAlert:
|
||||
r.content = &alert{}
|
||||
case contentTypeHandshake:
|
||||
r.content = &handshake{}
|
||||
case contentTypeApplicationData:
|
||||
r.content = &applicationData{}
|
||||
default:
|
||||
return errInvalidContentType
|
||||
}
|
||||
|
||||
return r.content.Unmarshal(data[recordLayerHeaderSize:])
|
||||
}
|
||||
|
||||
// Note that as with TLS, multiple handshake messages may be placed in
|
||||
// the same DTLS record, provided that there is room and that they are
|
||||
// part of the same flight. Thus, there are two acceptable ways to pack
|
||||
// two DTLS messages into the same datagram: in the same record or in
|
||||
// separate records.
|
||||
// https://tools.ietf.org/html/rfc6347#section-4.2.3
|
||||
func unpackDatagram(buf []byte) ([][]byte, error) {
|
||||
out := [][]byte{}
|
||||
|
||||
for offset := 0; len(buf) != offset; {
|
||||
if len(buf)-offset <= recordLayerHeaderSize {
|
||||
return nil, errDTLSPacketInvalidLength
|
||||
}
|
||||
|
||||
pktLen := (recordLayerHeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
|
||||
out = append(out, buf[offset:offset+pktLen])
|
||||
offset += pktLen
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
59
vendor/github.com/pion/dtls/record_layer_header.go
generated
Normal file
59
vendor/github.com/pion/dtls/record_layer_header.go
generated
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package dtls
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type recordLayerHeader struct {
|
||||
contentType contentType
|
||||
contentLen uint16
|
||||
protocolVersion protocolVersion
|
||||
epoch uint16
|
||||
sequenceNumber uint64 // uint48 in spec
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
recordLayerHeaderSize = 13
|
||||
maxSequenceNumber = 0x0000FFFFFFFFFFFF
|
||||
|
||||
dtls1_2Major = 0xfe
|
||||
dtls1_2Minor = 0xfd
|
||||
)
|
||||
|
||||
var protocolVersion1_2 = protocolVersion{dtls1_2Major, dtls1_2Minor}
|
||||
|
||||
// https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||
type protocolVersion struct {
|
||||
major, minor uint8
|
||||
}
|
||||
|
||||
func (r *recordLayerHeader) Marshal() ([]byte, error) {
|
||||
if r.sequenceNumber > maxSequenceNumber {
|
||||
return nil, errSequenceNumberOverflow
|
||||
}
|
||||
|
||||
out := make([]byte, recordLayerHeaderSize)
|
||||
out[0] = byte(r.contentType)
|
||||
out[1] = r.protocolVersion.major
|
||||
out[2] = r.protocolVersion.minor
|
||||
binary.BigEndian.PutUint16(out[3:], r.epoch)
|
||||
putBigEndianUint48(out[5:], r.sequenceNumber)
|
||||
binary.BigEndian.PutUint16(out[recordLayerHeaderSize-2:], r.contentLen)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *recordLayerHeader) Unmarshal(data []byte) error {
|
||||
if len(data) < recordLayerHeaderSize {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
r.contentType = contentType(data[0])
|
||||
r.protocolVersion.major = data[1]
|
||||
r.protocolVersion.minor = data[2]
|
||||
r.epoch = binary.BigEndian.Uint16(data[3:])
|
||||
|
||||
// SequenceNumber is stored as uint48, make into uint64
|
||||
seqCopy := make([]byte, 8)
|
||||
copy(seqCopy[2:], data[5:11])
|
||||
r.sequenceNumber = binary.BigEndian.Uint64(seqCopy)
|
||||
|
||||
return nil
|
||||
}
|
||||
36
vendor/github.com/pion/dtls/resume.go
generated
Normal file
36
vendor/github.com/pion/dtls/resume.go
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Export extracts dtls state and inner connection from an already handshaked dtls conn
|
||||
func (c *Conn) Export() (*State, net.Conn, error) {
|
||||
state, err := c.state.clone()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return state, c.nextConn, nil
|
||||
}
|
||||
|
||||
// Resume imports an already stablished dtls connection using a specific dtls state
|
||||
func Resume(state *State, conn net.Conn, config *Config) (*Conn, error) {
|
||||
// Custom flight handler that sets imported data and signals as handshaked
|
||||
flightHandler := func(c *Conn) (bool, error) {
|
||||
c.state = *state
|
||||
c.signalHandshakeComplete()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Empty handshake handler, since handshake was already done
|
||||
handshakeHandler := func(c *Conn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := createConn(conn, flightHandler, handshakeHandler, config, state.isClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, c.getConnErr()
|
||||
}
|
||||
425
vendor/github.com/pion/dtls/server_handlers.go
generated
Normal file
425
vendor/github.com/pion/dtls/server_handlers.go
generated
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func serverHandshakeHandler(c *Conn) error {
|
||||
handleSingleHandshake := func(buf []byte) error {
|
||||
rawHandshake := &handshake{}
|
||||
if err := rawHandshake.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch h := rawHandshake.handshakeMessage.(type) {
|
||||
case *handshakeMessageClientHello:
|
||||
if c.currFlight.get() == flight2 {
|
||||
if !bytes.Equal(c.cookie, h.cookie) {
|
||||
return errCookieMismatch
|
||||
}
|
||||
c.state.localSequenceNumber = 1
|
||||
if err := c.currFlight.set(flight4); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
c.state.remoteRandom = h.random
|
||||
|
||||
if _, ok := findMatchingCipherSuite(h.cipherSuites, c.localCipherSuites); !ok {
|
||||
return errCipherSuiteNoIntersection
|
||||
}
|
||||
c.state.cipherSuite = h.cipherSuites[0]
|
||||
|
||||
for _, extension := range h.extensions {
|
||||
switch e := extension.(type) {
|
||||
case *extensionSupportedEllipticCurves:
|
||||
c.namedCurve = e.ellipticCurves[0]
|
||||
case *extensionUseSRTP:
|
||||
profile, ok := findMatchingSRTPProfile(e.protectionProfiles, c.localSRTPProtectionProfiles)
|
||||
if !ok {
|
||||
return fmt.Errorf("Client requested SRTP but we have no matching profiles")
|
||||
}
|
||||
c.state.srtpProtectionProfile = profile
|
||||
}
|
||||
}
|
||||
|
||||
if c.localKeypair == nil {
|
||||
var err error
|
||||
c.localKeypair, err = generateKeypair(c.namedCurve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.currFlight.set(flight2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *handshakeMessageCertificateVerify:
|
||||
if c.state.remoteCertificate == nil {
|
||||
return errCertificateVerifyNoCertificate
|
||||
}
|
||||
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
)
|
||||
|
||||
if err := verifyCertificateVerify(plainText, h.hashAlgorithm, h.signature, c.state.remoteCertificate); err != nil {
|
||||
return err
|
||||
}
|
||||
c.remoteCertificateVerified = true
|
||||
|
||||
case *handshakeMessageCertificate:
|
||||
c.state.remoteCertificate = h.certificate
|
||||
|
||||
case *handshakeMessageClientKeyExchange:
|
||||
c.remoteKeypair = &namedCurveKeypair{c.namedCurve, h.publicKey, nil}
|
||||
|
||||
serverRandom, err := c.state.localRandom.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientRandom, err := c.state.remoteRandom.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preMasterSecret, err := prfPreMasterSecret(c.remoteKeypair.publicKey, c.localKeypair.privateKey, c.localKeypair.curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.state.masterSecret, err = prfMasterSecret(preMasterSecret, clientRandom, serverRandom, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.state.cipherSuite.init(c.state.masterSecret, clientRandom, serverRandom /* isClient */, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *handshakeMessageFinished:
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
handshakeCachePullRule{handshakeTypeCertificateVerify, true},
|
||||
)
|
||||
expectedVerifyData, err := prfVerifyDataClient(c.state.masterSecret, plainText, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !bytes.Equal(expectedVerifyData, h.verifyData) {
|
||||
return errVerifyDataMismatch
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unhandled handshake %d", h.handshakeType())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
switch c.currFlight.get() {
|
||||
case flight0:
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
)
|
||||
if expectedMessages[0] != nil && expectedMessages[0].messageSequence == 0 {
|
||||
return handleSingleHandshake(expectedMessages[0].data)
|
||||
}
|
||||
case flight2:
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
)
|
||||
if expectedMessages[0] != nil && expectedMessages[0].messageSequence == 1 {
|
||||
return handleSingleHandshake(expectedMessages[0].data)
|
||||
}
|
||||
case flight4:
|
||||
expectedMessages := c.handshakeCache.pull(
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
handshakeCachePullRule{handshakeTypeCertificateVerify, true},
|
||||
)
|
||||
|
||||
var expectedSeqnum uint16
|
||||
switch {
|
||||
case expectedMessages[0] != nil:
|
||||
expectedSeqnum = expectedMessages[0].messageSequence
|
||||
case expectedMessages[1] != nil:
|
||||
expectedSeqnum = expectedMessages[1].messageSequence
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, msg := range expectedMessages {
|
||||
// handshakeTypeCertificate and handshakeTypeCertificateVerify can be nil, just make sure we have no gaps
|
||||
switch {
|
||||
case (i == 0 || i == 2) && msg == nil:
|
||||
continue
|
||||
case msg == nil:
|
||||
return nil // We don't have all messages yet, try again later
|
||||
case msg.messageSequence != expectedSeqnum:
|
||||
return nil // We have a gap, still waiting on messages
|
||||
}
|
||||
expectedSeqnum++
|
||||
}
|
||||
|
||||
for _, msg := range expectedMessages {
|
||||
if msg != nil {
|
||||
if err := handleSingleHandshake(msg.data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finishedMsg := c.handshakeCache.pull(handshakeCachePullRule{handshakeTypeFinished, true})
|
||||
if finishedMsg[0] == nil {
|
||||
return nil
|
||||
} else if err := handleSingleHandshake(finishedMsg[0].data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.clientAuth == RequireAnyClientCert && c.state.remoteCertificate == nil {
|
||||
return errClientCertificateRequired
|
||||
} else if c.state.remoteCertificate != nil && !c.remoteCertificateVerified {
|
||||
return errClientCertificateNotVerified
|
||||
}
|
||||
|
||||
if c.clientAuth > NoClientCert {
|
||||
c.state.localSequenceNumber = 6
|
||||
} else {
|
||||
c.state.localSequenceNumber = 5
|
||||
}
|
||||
c.setLocalEpoch(1)
|
||||
|
||||
if err := c.currFlight.set(flight6); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func serverFlightHandler(c *Conn) (bool, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
switch c.currFlight.get() {
|
||||
case flight0:
|
||||
// Waiting for ClientHello
|
||||
case flight2:
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(c.state.localSequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageHelloVerifyRequest{
|
||||
version: protocolVersion1_2,
|
||||
cookie: c.cookie,
|
||||
},
|
||||
},
|
||||
}, false)
|
||||
|
||||
case flight4:
|
||||
extensions := []extension{
|
||||
&extensionSupportedEllipticCurves{
|
||||
ellipticCurves: []namedCurve{namedCurveX25519, namedCurveP256},
|
||||
},
|
||||
&extensionSupportedPointFormats{
|
||||
pointFormats: []ellipticCurvePointFormat{ellipticCurvePointFormatUncompressed},
|
||||
},
|
||||
}
|
||||
if c.state.srtpProtectionProfile != 0 {
|
||||
extensions = append(extensions, &extensionUseSRTP{
|
||||
protectionProfiles: []SRTPProtectionProfile{c.state.srtpProtectionProfile},
|
||||
})
|
||||
}
|
||||
|
||||
sequenceNumber := c.state.localSequenceNumber
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageServerHello{
|
||||
version: protocolVersion1_2,
|
||||
random: c.state.localRandom,
|
||||
cipherSuite: c.state.cipherSuite,
|
||||
compressionMethod: defaultCompressionMethods[0],
|
||||
extensions: extensions,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageCertificate{
|
||||
certificate: c.localCertificate,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
|
||||
if len(c.localKeySignature) == 0 {
|
||||
serverRandom, err := c.state.localRandom.Marshal()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
clientRandom, err := c.state.remoteRandom.Marshal()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
signature, err := generateKeySignature(clientRandom, serverRandom, c.localKeypair.publicKey, c.namedCurve, c.localPrivateKey, HashAlgorithmSHA256)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
c.localKeySignature = signature
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageServerKeyExchange{
|
||||
ellipticCurveType: ellipticCurveTypeNamedCurve,
|
||||
namedCurve: c.namedCurve,
|
||||
publicKey: c.localKeypair.publicKey,
|
||||
hashAlgorithm: HashAlgorithmSHA256,
|
||||
signatureAlgorithm: signatureAlgorithmECDSA,
|
||||
signature: c.localKeySignature,
|
||||
}},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
|
||||
if c.clientAuth > NoClientCert {
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageCertificateRequest{
|
||||
certificateTypes: []clientCertificateType{clientCertificateTypeRSASign, clientCertificateTypeECDSASign},
|
||||
signatureHashAlgorithms: []signatureHashAlgorithm{
|
||||
{HashAlgorithmSHA256, signatureAlgorithmRSA},
|
||||
{HashAlgorithmSHA384, signatureAlgorithmRSA},
|
||||
{HashAlgorithmSHA512, signatureAlgorithmRSA},
|
||||
{HashAlgorithmSHA256, signatureAlgorithmECDSA},
|
||||
{HashAlgorithmSHA384, signatureAlgorithmECDSA},
|
||||
{HashAlgorithmSHA512, signatureAlgorithmECDSA},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, false)
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: sequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(sequenceNumber),
|
||||
},
|
||||
handshakeMessage: &handshakeMessageServerHelloDone{},
|
||||
},
|
||||
}, false)
|
||||
case flight6:
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
sequenceNumber: c.state.localSequenceNumber,
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &changeCipherSpec{},
|
||||
}, false)
|
||||
|
||||
if len(c.localVerifyData) == 0 {
|
||||
plainText := c.handshakeCache.pullAndMerge(
|
||||
handshakeCachePullRule{handshakeTypeClientHello, true},
|
||||
handshakeCachePullRule{handshakeTypeServerHello, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, false},
|
||||
handshakeCachePullRule{handshakeTypeServerKeyExchange, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificateRequest, false},
|
||||
handshakeCachePullRule{handshakeTypeServerHelloDone, false},
|
||||
handshakeCachePullRule{handshakeTypeCertificate, true},
|
||||
handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
|
||||
handshakeCachePullRule{handshakeTypeCertificateVerify, true},
|
||||
handshakeCachePullRule{handshakeTypeFinished, true},
|
||||
)
|
||||
|
||||
var err error
|
||||
c.localVerifyData, err = prfVerifyDataServer(c.state.masterSecret, plainText, c.state.cipherSuite.hashFunc())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
c.internalSend(&recordLayer{
|
||||
recordLayerHeader: recordLayerHeader{
|
||||
epoch: 1,
|
||||
sequenceNumber: 0, // sequenceNumber restarts per epoch
|
||||
protocolVersion: protocolVersion1_2,
|
||||
},
|
||||
content: &handshake{
|
||||
// sequenceNumber and messageSequence line up, may need to be re-evaluated
|
||||
handshakeHeader: handshakeHeader{
|
||||
messageSequence: uint16(c.state.localSequenceNumber), // KeyExchange + 1
|
||||
},
|
||||
|
||||
handshakeMessage: &handshakeMessageFinished{
|
||||
verifyData: c.localVerifyData,
|
||||
}},
|
||||
}, true)
|
||||
|
||||
// TODO: Better way to end handshake
|
||||
c.signalHandshakeComplete()
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unhandled flight %s", c.currFlight.get())
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
14
vendor/github.com/pion/dtls/signature_algorithm.go
generated
Normal file
14
vendor/github.com/pion/dtls/signature_algorithm.go
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package dtls
|
||||
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
|
||||
type signatureAlgorithm uint16
|
||||
|
||||
const (
|
||||
signatureAlgorithmRSA signatureAlgorithm = 1
|
||||
signatureAlgorithmECDSA signatureAlgorithm = 3
|
||||
)
|
||||
|
||||
var signatureAlgorithms = map[signatureAlgorithm]bool{
|
||||
signatureAlgorithmRSA: true,
|
||||
signatureAlgorithmECDSA: true,
|
||||
}
|
||||
6
vendor/github.com/pion/dtls/signature_hash_algorithm.go
generated
Normal file
6
vendor/github.com/pion/dtls/signature_hash_algorithm.go
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package dtls
|
||||
|
||||
type signatureHashAlgorithm struct {
|
||||
hash HashAlgorithm
|
||||
signature signatureAlgorithm
|
||||
}
|
||||
13
vendor/github.com/pion/dtls/srtp_protection_profile.go
generated
Normal file
13
vendor/github.com/pion/dtls/srtp_protection_profile.go
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package dtls
|
||||
|
||||
// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
|
||||
// https://tools.ietf.org/html/rfc5764#section-4.1.2
|
||||
type SRTPProtectionProfile uint16
|
||||
|
||||
const (
|
||||
SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = 0x0001 // nolint
|
||||
)
|
||||
|
||||
var srtpProtectionProfiles = map[SRTPProtectionProfile]bool{
|
||||
SRTP_AES128_CM_HMAC_SHA1_80: true,
|
||||
}
|
||||
159
vendor/github.com/pion/dtls/state.go
generated
Normal file
159
vendor/github.com/pion/dtls/state.go
generated
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/gob"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
|
||||
type State struct {
|
||||
localEpoch, remoteEpoch atomic.Value
|
||||
localSequenceNumber uint64 // uint48
|
||||
localRandom, remoteRandom handshakeRandom
|
||||
masterSecret []byte
|
||||
cipherSuite cipherSuite // nil if a cipherSuite hasn't been chosen
|
||||
|
||||
srtpProtectionProfile SRTPProtectionProfile // Negotiated SRTPProtectionProfile
|
||||
remoteCertificate *x509.Certificate
|
||||
|
||||
isClient bool
|
||||
}
|
||||
|
||||
type serializedState struct {
|
||||
LocalEpoch uint16
|
||||
RemoteEpoch uint16
|
||||
LocalRandom []byte
|
||||
RemoteRandom []byte
|
||||
CipherSuiteID uint16
|
||||
MasterSecret []byte
|
||||
SequenceNumber uint64
|
||||
SRTPProtectionProfile uint16
|
||||
RemoteCertificate []byte
|
||||
IsClient bool
|
||||
}
|
||||
|
||||
func (s *State) clone() (*State, error) {
|
||||
serialized, err := s.serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state := &State{}
|
||||
if err := state.deserialize(*serialized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (s *State) serialize() (*serializedState, error) {
|
||||
// Marshal random values
|
||||
localRnd, err := s.localRandom.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteRnd, err := s.remoteRandom.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Marshal remote certificate
|
||||
var cert []byte
|
||||
if s.remoteCertificate != nil {
|
||||
h := &handshakeMessageCertificate{s.remoteCertificate}
|
||||
cert, err = h.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
serialized := serializedState{
|
||||
LocalEpoch: s.localEpoch.Load().(uint16),
|
||||
RemoteEpoch: s.remoteEpoch.Load().(uint16),
|
||||
CipherSuiteID: uint16(s.cipherSuite.ID()),
|
||||
MasterSecret: s.masterSecret,
|
||||
SequenceNumber: s.localSequenceNumber,
|
||||
LocalRandom: localRnd,
|
||||
RemoteRandom: remoteRnd,
|
||||
SRTPProtectionProfile: uint16(s.srtpProtectionProfile),
|
||||
RemoteCertificate: cert,
|
||||
IsClient: s.isClient,
|
||||
}
|
||||
|
||||
return &serialized, nil
|
||||
}
|
||||
|
||||
func (s *State) deserialize(serialized serializedState) error {
|
||||
// Set epoch values
|
||||
s.localEpoch.Store(serialized.LocalEpoch)
|
||||
s.remoteEpoch.Store(serialized.RemoteEpoch)
|
||||
|
||||
// Set random values
|
||||
localRandom := &handshakeRandom{}
|
||||
if err := localRandom.Unmarshal(serialized.LocalRandom); err != nil {
|
||||
return err
|
||||
}
|
||||
s.localRandom = *localRandom
|
||||
remoteRandom := &handshakeRandom{}
|
||||
if err := remoteRandom.Unmarshal(serialized.RemoteRandom); err != nil {
|
||||
return err
|
||||
}
|
||||
s.remoteRandom = *remoteRandom
|
||||
|
||||
s.isClient = serialized.IsClient
|
||||
|
||||
// Set cipher suite
|
||||
s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID))
|
||||
var err error
|
||||
if serialized.IsClient {
|
||||
err = s.cipherSuite.init(serialized.MasterSecret, serialized.LocalRandom, serialized.RemoteRandom, true)
|
||||
} else {
|
||||
err = s.cipherSuite.init(serialized.MasterSecret, serialized.RemoteRandom, serialized.LocalRandom, false)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.localSequenceNumber = serialized.SequenceNumber
|
||||
s.srtpProtectionProfile = SRTPProtectionProfile(serialized.SRTPProtectionProfile)
|
||||
|
||||
// Set remote certificate
|
||||
if serialized.RemoteCertificate != nil {
|
||||
h := &handshakeMessageCertificate{}
|
||||
if err := h.Unmarshal(serialized.RemoteCertificate); err != nil {
|
||||
return err
|
||||
}
|
||||
s.remoteCertificate = h.certificate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation
|
||||
func (s *State) MarshalBinary() ([]byte, error) {
|
||||
serialized, err := s.serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
if err := enc.Encode(*serialized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation
|
||||
func (s *State) UnmarshalBinary(data []byte) error {
|
||||
enc := gob.NewDecoder(bytes.NewBuffer(data))
|
||||
var serialized serializedState
|
||||
if err := enc.Decode(&serialized); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.deserialize(serialized); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
154
vendor/github.com/pion/dtls/util.go
generated
Normal file
154
vendor/github.com/pion/dtls/util.go
generated
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package dtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Parse a big endian uint24
|
||||
func bigEndianUint24(raw []byte) uint32 {
|
||||
if len(raw) < 3 {
|
||||
return 0
|
||||
}
|
||||
|
||||
rawCopy := make([]byte, 4)
|
||||
copy(rawCopy[1:], raw)
|
||||
return binary.BigEndian.Uint32(rawCopy)
|
||||
}
|
||||
|
||||
func putBigEndianUint24(out []byte, in uint32) {
|
||||
tmp := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(tmp, in)
|
||||
copy(out, tmp[1:])
|
||||
}
|
||||
|
||||
func putBigEndianUint48(out []byte, in uint64) {
|
||||
tmp := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(tmp, in)
|
||||
copy(out, tmp[2:])
|
||||
}
|
||||
|
||||
// GenerateSelfSigned creates a self-signed certificate
|
||||
func GenerateSelfSigned() (*x509.Certificate, crypto.PrivateKey, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
origin := make([]byte, 16)
|
||||
|
||||
// Max random value, a 130-bits integer, i.e 2^130 - 1
|
||||
maxBigInt := new(big.Int)
|
||||
/* #nosec */
|
||||
maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
|
||||
serialNumber, err := rand.Int(rand.Reader, maxBigInt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
NotBefore: time.Now(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
NotAfter: time.Now().AddDate(0, 1, 0),
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
Subject: pkix.Name{CommonName: hex.EncodeToString(origin)},
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
raw, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(raw)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return cert, priv, nil
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// examinePadding returns, in constant time, the length of the padding to remove
|
||||
// from the end of payload. It also returns a byte which is equal to 255 if the
|
||||
// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
|
||||
//
|
||||
// https://github.com/golang/go/blob/039c2081d1178f90a8fa2f4e6958693129f8de33/src/crypto/tls/conn.go#L245
|
||||
func examinePadding(payload []byte) (toRemove int, good byte) {
|
||||
if len(payload) < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
paddingLen := payload[len(payload)-1]
|
||||
t := uint(len(payload)-1) - uint(paddingLen)
|
||||
// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
|
||||
good = byte(int32(^t) >> 31)
|
||||
|
||||
// The maximum possible padding length plus the actual length field
|
||||
toCheck := 256
|
||||
// The length of the padded data is public, so we can use an if here
|
||||
if toCheck > len(payload) {
|
||||
toCheck = len(payload)
|
||||
}
|
||||
|
||||
for i := 0; i < toCheck; i++ {
|
||||
t := uint(paddingLen) - uint(i)
|
||||
// if i <= paddingLen then the MSB of t is zero
|
||||
mask := byte(int32(^t) >> 31)
|
||||
b := payload[len(payload)-1-i]
|
||||
good &^= mask&paddingLen ^ mask&b
|
||||
}
|
||||
|
||||
// We AND together the bits of good and replicate the result across
|
||||
// all the bits.
|
||||
good &= good << 4
|
||||
good &= good << 2
|
||||
good &= good << 1
|
||||
good = uint8(int8(good) >> 7)
|
||||
|
||||
toRemove = int(paddingLen) + 1
|
||||
|
||||
return toRemove, good
|
||||
}
|
||||
|
||||
func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfile, bool) {
|
||||
for _, aProfile := range a {
|
||||
for _, bProfile := range b {
|
||||
if aProfile == bProfile {
|
||||
return aProfile, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func findMatchingCipherSuite(a, b []cipherSuite) (cipherSuite, bool) {
|
||||
for _, aSuite := range a {
|
||||
for _, bSuite := range b {
|
||||
if aSuite.ID() == bSuite.ID() {
|
||||
return aSuite, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
20
vendor/github.com/pion/ice/.gitignore
generated
vendored
Normal file
20
vendor/github.com/pion/ice/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
### JetBrains IDE ###
|
||||
#####################
|
||||
.idea/
|
||||
|
||||
### Emacs Temporary Files ###
|
||||
#############################
|
||||
*~
|
||||
|
||||
### Folders ###
|
||||
###############
|
||||
bin/
|
||||
vendor/
|
||||
node_modules/
|
||||
|
||||
### Files ###
|
||||
#############
|
||||
tags
|
||||
cover.out
|
||||
*.sw[poe]
|
||||
*.wasm
|
||||
23
vendor/github.com/pion/ice/.golangci.yml
generated
vendored
Normal file
23
vendor/github.com/pion/ice/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- lll
|
||||
- maligned
|
||||
- gochecknoglobals
|
||||
- interfacer
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-per-linter: 0
|
||||
max-same-issues: 50
|
||||
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- dupl
|
||||
19
vendor/github.com/pion/ice/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/pion/ice/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.x" # use the latest Go release
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
before_script:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
|
||||
|
||||
script:
|
||||
- golangci-lint run ./...
|
||||
# - rm -rf examples # Remove examples, no test coverage for them
|
||||
- go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash .github/assert-contributors.sh
|
||||
- bash .github/lint-disallowed-functions-in-library.sh
|
||||
- bash .github/lint-commit-message.sh
|
||||
21
vendor/github.com/pion/ice/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pion/ice/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
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.
|
||||
44
vendor/github.com/pion/ice/README.md
generated
vendored
Normal file
44
vendor/github.com/pion/ice/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<h1 align="center">
|
||||
<br>
|
||||
Pion ICE
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of ICE</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-ice-gray.svg?longCache=true&colorB=brightgreen" alt="Pion transport"></a>
|
||||
<a href="http://gophers.slack.com/messages/pion"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/pion/ice"><img src="https://travis-ci.org/pion/ice.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://godoc.org/github.com/pion/ice"><img src="https://godoc.org/github.com/pion/ice?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://codecov.io/gh/pion/ice"><img src="https://codecov.io/gh/pion/ice/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/ice"><img src="https://goreportcard.com/badge/github.com/pion/ice" alt="Go Report Card"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
### Roadmap
|
||||
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||
|
||||
### Community
|
||||
Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
|
||||
|
||||
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||
|
||||
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||
|
||||
### Contributing
|
||||
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
|
||||
|
||||
* [John Bradley](https://github.com/kc5nra) - *Original Author*
|
||||
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
|
||||
* [Michael MacDonald](https://github.com/mjmac) - *Original Author*
|
||||
* [Michiel De Backker](https://github.com/backkem) - *Original Author*
|
||||
* [Konstantin Itskov](https://github.com/trivigy) - *Original Author*
|
||||
* [Luke Curley](https://github.com/kixelated)
|
||||
* [Hugo Arregui](https://github.com/hugoArregui)
|
||||
* [Adam Kiss](https://github.com/masterada)
|
||||
* [Aleksandr Razumov](https://github.com/ernado)
|
||||
* [Yutaka Takeda](https://github.com/enobufs)
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
1009
vendor/github.com/pion/ice/agent.go
generated
Normal file
1009
vendor/github.com/pion/ice/agent.go
generated
Normal file
File diff suppressed because it is too large
Load diff
40
vendor/github.com/pion/ice/candidate.go
generated
Normal file
40
vendor/github.com/pion/ice/candidate.go
generated
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
receiveMTU = 8192
|
||||
defaultLocalPreference = 65535
|
||||
|
||||
// ComponentRTP indicates that the candidate is used for RTP
|
||||
ComponentRTP uint16 = 1
|
||||
// ComponentRTCP indicates that the candidate is used for RTCP
|
||||
ComponentRTCP
|
||||
)
|
||||
|
||||
// Candidate represents an ICE candidate
|
||||
type Candidate interface {
|
||||
Component() uint16
|
||||
Address() string
|
||||
LastReceived() time.Time
|
||||
LastSent() time.Time
|
||||
NetworkType() NetworkType
|
||||
Port() int
|
||||
Priority() uint32
|
||||
RelatedAddress() *CandidateRelatedAddress
|
||||
String() string
|
||||
Type() CandidateType
|
||||
|
||||
Equal(other Candidate) bool
|
||||
|
||||
addr() *net.UDPAddr
|
||||
agent() *Agent
|
||||
|
||||
close() error
|
||||
seen(outbound bool)
|
||||
start(a *Agent, conn net.PacketConn)
|
||||
writeTo(raw []byte, dst Candidate) (int, error)
|
||||
}
|
||||
235
vendor/github.com/pion/ice/candidate_base.go
generated
Normal file
235
vendor/github.com/pion/ice/candidate_base.go
generated
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
type candidateBase struct {
|
||||
networkType NetworkType
|
||||
candidateType CandidateType
|
||||
|
||||
component uint16
|
||||
address string
|
||||
port int
|
||||
relatedAddress *CandidateRelatedAddress
|
||||
|
||||
resolvedAddr *net.UDPAddr
|
||||
|
||||
lock sync.RWMutex
|
||||
lastSent time.Time
|
||||
lastReceived time.Time
|
||||
|
||||
currAgent *Agent
|
||||
conn net.PacketConn
|
||||
closeCh chan struct{}
|
||||
closedCh chan struct{}
|
||||
}
|
||||
|
||||
// Address returns Candidate Address
|
||||
func (c *candidateBase) Address() string {
|
||||
return c.address
|
||||
}
|
||||
|
||||
// Port returns Candidate Port
|
||||
func (c *candidateBase) Port() int {
|
||||
return c.port
|
||||
}
|
||||
|
||||
// Type returns candidate type
|
||||
func (c *candidateBase) Type() CandidateType {
|
||||
return c.candidateType
|
||||
}
|
||||
|
||||
// NetworkType returns candidate NetworkType
|
||||
func (c *candidateBase) NetworkType() NetworkType {
|
||||
return c.networkType
|
||||
}
|
||||
|
||||
// Component returns candidate component
|
||||
func (c *candidateBase) Component() uint16 {
|
||||
return c.component
|
||||
}
|
||||
|
||||
// LocalPreference returns the local preference for this candidate
|
||||
func (c *candidateBase) LocalPreference() uint16 {
|
||||
return defaultLocalPreference
|
||||
}
|
||||
|
||||
// RelatedAddress returns *CandidateRelatedAddress
|
||||
func (c *candidateBase) RelatedAddress() *CandidateRelatedAddress {
|
||||
return c.relatedAddress
|
||||
}
|
||||
|
||||
// start runs the candidate using the provided connection
|
||||
func (c *candidateBase) start(a *Agent, conn net.PacketConn) {
|
||||
c.currAgent = a
|
||||
c.conn = conn
|
||||
c.closeCh = make(chan struct{})
|
||||
c.closedCh = make(chan struct{})
|
||||
|
||||
go c.recvLoop()
|
||||
}
|
||||
|
||||
func (c *candidateBase) recvLoop() {
|
||||
defer func() {
|
||||
close(c.closedCh)
|
||||
}()
|
||||
|
||||
log := c.agent().log
|
||||
buffer := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, srcAddr, err := c.conn.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
handleInboundCandidateMsg(c, buffer[:n], srcAddr, log)
|
||||
}
|
||||
}
|
||||
|
||||
func handleInboundCandidateMsg(c Candidate, buffer []byte, srcAddr net.Addr, log logging.LeveledLogger) {
|
||||
if stun.IsMessage(buffer) {
|
||||
m := &stun.Message{
|
||||
Raw: make([]byte, len(buffer)),
|
||||
}
|
||||
// Explicitly copy raw buffer so Message can own the memory.
|
||||
copy(m.Raw, buffer)
|
||||
if err := m.Decode(); err != nil {
|
||||
log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
|
||||
return
|
||||
}
|
||||
err := c.agent().run(func(agent *Agent) {
|
||||
agent.handleInbound(m, c, srcAddr)
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("Failed to handle message: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
isValidRemoteCandidate := make(chan bool, 1)
|
||||
err := c.agent().run(func(agent *Agent) {
|
||||
isValidRemoteCandidate <- agent.noSTUNSeen(c, srcAddr)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to handle message: %v", err)
|
||||
} else if !<-isValidRemoteCandidate {
|
||||
log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr())
|
||||
}
|
||||
|
||||
// NOTE This will return packetio.ErrFull if the buffer ever manages to fill up.
|
||||
if _, err := c.agent().buffer.Write(buffer); err != nil {
|
||||
log.Warnf("failed to write packet")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// close stops the recvLoop
|
||||
func (c *candidateBase) close() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.conn != nil {
|
||||
// Unblock recvLoop
|
||||
close(c.closeCh)
|
||||
// Close the conn
|
||||
err := c.conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait until the recvLoop is closed
|
||||
<-c.closedCh
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) {
|
||||
n, err := c.conn.WriteTo(raw, dst.addr())
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to send packet: %v", err)
|
||||
}
|
||||
c.seen(true)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Priority computes the priority for this ICE Candidate
|
||||
func (c *candidateBase) Priority() uint32 {
|
||||
// The local preference MUST be an integer from 0 (lowest preference) to
|
||||
// 65535 (highest preference) inclusive. When there is only a single IP
|
||||
// address, this value SHOULD be set to 65535. If there are multiple
|
||||
// candidates for a particular component for a particular data stream
|
||||
// that have the same type, the local preference MUST be unique for each
|
||||
// one.
|
||||
return (1<<24)*uint32(c.Type().Preference()) +
|
||||
(1<<8)*uint32(c.LocalPreference()) +
|
||||
uint32(256-c.Component())
|
||||
}
|
||||
|
||||
// Equal is used to compare two candidateBases
|
||||
func (c *candidateBase) Equal(other Candidate) bool {
|
||||
return c.NetworkType() == other.NetworkType() &&
|
||||
c.Type() == other.Type() &&
|
||||
c.Address() == other.Address() &&
|
||||
c.Port() == other.Port() &&
|
||||
c.RelatedAddress().Equal(other.RelatedAddress())
|
||||
}
|
||||
|
||||
// String makes the candidateBase printable
|
||||
func (c *candidateBase) String() string {
|
||||
return fmt.Sprintf("%s %s:%d%s", c.Type(), c.Address(), c.Port(), c.relatedAddress)
|
||||
}
|
||||
|
||||
// LastReceived returns a time.Time indicating the last time
|
||||
// this candidate was received
|
||||
func (c *candidateBase) LastReceived() time.Time {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.lastReceived
|
||||
}
|
||||
|
||||
func (c *candidateBase) setLastReceived(t time.Time) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.lastReceived = t
|
||||
}
|
||||
|
||||
// LastSent returns a time.Time indicating the last time
|
||||
// this candidate was sent
|
||||
func (c *candidateBase) LastSent() time.Time {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.lastSent
|
||||
}
|
||||
|
||||
func (c *candidateBase) setLastSent(t time.Time) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.lastSent = t
|
||||
}
|
||||
|
||||
func (c *candidateBase) seen(outbound bool) {
|
||||
if outbound {
|
||||
c.setLastSent(time.Now())
|
||||
} else {
|
||||
c.setLastReceived(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *candidateBase) addr() *net.UDPAddr {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.resolvedAddr
|
||||
}
|
||||
|
||||
func (c *candidateBase) agent() *Agent {
|
||||
return c.currAgent
|
||||
}
|
||||
49
vendor/github.com/pion/ice/candidate_host.go
generated
Normal file
49
vendor/github.com/pion/ice/candidate_host.go
generated
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CandidateHost is a candidate of type host
|
||||
type CandidateHost struct {
|
||||
candidateBase
|
||||
|
||||
network string
|
||||
}
|
||||
|
||||
// NewCandidateHost creates a new host candidate
|
||||
func NewCandidateHost(network string, address string, port int, component uint16) (*CandidateHost, error) {
|
||||
c := &CandidateHost{
|
||||
candidateBase: candidateBase{
|
||||
address: address,
|
||||
candidateType: CandidateTypeHost,
|
||||
component: component,
|
||||
port: port,
|
||||
},
|
||||
network: network,
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(address, ".local") {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
if err := c.setIP(ip); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *CandidateHost) setIP(ip net.IP) error {
|
||||
networkType, err := determineNetworkType(c.network, ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.candidateBase.networkType = networkType
|
||||
c.candidateBase.resolvedAddr = &net.UDPAddr{IP: ip, Port: c.port}
|
||||
return nil
|
||||
}
|
||||
36
vendor/github.com/pion/ice/candidate_peer_reflexive.go
generated
Normal file
36
vendor/github.com/pion/ice/candidate_peer_reflexive.go
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package ice
|
||||
|
||||
import "net"
|
||||
|
||||
// CandidatePeerReflexive ...
|
||||
type CandidatePeerReflexive struct {
|
||||
candidateBase
|
||||
}
|
||||
|
||||
// NewCandidatePeerReflexive creates a new peer reflective candidate
|
||||
func NewCandidatePeerReflexive(network string, address string, port int, component uint16, relAddr string, relPort int) (*CandidatePeerReflexive, error) {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidatePeerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypePeerReflexive,
|
||||
address: address,
|
||||
port: port,
|
||||
resolvedAddr: &net.UDPAddr{IP: ip, Port: port},
|
||||
component: component,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: relAddr,
|
||||
Port: relPort,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
107
vendor/github.com/pion/ice/candidate_relay.go
generated
Normal file
107
vendor/github.com/pion/ice/candidate_relay.go
generated
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/pion/turnc"
|
||||
)
|
||||
|
||||
// CandidateRelay ...
|
||||
type CandidateRelay struct {
|
||||
candidateBase
|
||||
|
||||
allocation *turnc.Allocation
|
||||
client io.Closer
|
||||
permissions map[string]*turnc.Permission
|
||||
}
|
||||
|
||||
// NewCandidateRelay creates a new relay candidate
|
||||
func NewCandidateRelay(network string, address string, port int, component uint16, relAddr string, relPort int) (*CandidateRelay, error) {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidateRelay{
|
||||
candidateBase: candidateBase{
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeRelay,
|
||||
address: address,
|
||||
port: port,
|
||||
resolvedAddr: &net.UDPAddr{IP: ip, Port: port},
|
||||
component: component,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: relAddr,
|
||||
Port: relPort,
|
||||
},
|
||||
},
|
||||
permissions: map[string]*turnc.Permission{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) setAllocation(client io.Closer, a *turnc.Allocation) {
|
||||
c.allocation = a
|
||||
c.client = client
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) start(a *Agent, conn net.PacketConn) {
|
||||
c.currAgent = a
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) close() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
for _, p := range c.permissions {
|
||||
if err := p.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.client == nil {
|
||||
return nil
|
||||
}
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) addPermission(dst Candidate) error {
|
||||
permission, err := c.allocation.Create(dst.addr())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
c.permissions[dst.String()] = permission
|
||||
if err = c.permissions[dst.String()].Bind(); err != nil {
|
||||
c.agent().log.Warnf("Failed to Create ChannelBind for %v: %v", dst.String, err)
|
||||
}
|
||||
c.lock.Unlock()
|
||||
|
||||
go func(remoteAddr net.Addr) {
|
||||
log := c.agent().log
|
||||
buffer := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, err := permission.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
handleInboundCandidateMsg(c, buffer[:n], remoteAddr, log)
|
||||
}
|
||||
}(dst.addr())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) writeTo(raw []byte, dst Candidate) (int, error) {
|
||||
permission, ok := c.permissions[dst.String()]
|
||||
if !ok {
|
||||
return 0, errors.New("no permission created for remote candidate")
|
||||
}
|
||||
|
||||
return permission.Write(raw)
|
||||
}
|
||||
36
vendor/github.com/pion/ice/candidate_server_reflexive.go
generated
Normal file
36
vendor/github.com/pion/ice/candidate_server_reflexive.go
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package ice
|
||||
|
||||
import "net"
|
||||
|
||||
// CandidateServerReflexive ...
|
||||
type CandidateServerReflexive struct {
|
||||
candidateBase
|
||||
}
|
||||
|
||||
// NewCandidateServerReflexive creates a new server reflective candidate
|
||||
func NewCandidateServerReflexive(network string, address string, port int, component uint16, relAddr string, relPort int) (*CandidateServerReflexive, error) {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidateServerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeServerReflexive,
|
||||
address: address,
|
||||
port: port,
|
||||
resolvedAddr: &net.UDPAddr{IP: ip, Port: port},
|
||||
component: component,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: relAddr,
|
||||
Port: relPort,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
113
vendor/github.com/pion/ice/candidatepair.go
generated
Normal file
113
vendor/github.com/pion/ice/candidatepair.go
generated
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
type candidatePairState int
|
||||
|
||||
const (
|
||||
candidatePairStateChecking candidatePairState = iota + 1
|
||||
candidatePairStateFailed
|
||||
candidatePairStateValid
|
||||
)
|
||||
|
||||
func (c candidatePairState) String() string {
|
||||
switch c {
|
||||
case candidatePairStateChecking:
|
||||
return "checking"
|
||||
case candidatePairStateFailed:
|
||||
return "failed"
|
||||
case candidatePairStateValid:
|
||||
return "valid"
|
||||
}
|
||||
return "Unknown candidate pair state"
|
||||
}
|
||||
|
||||
func newCandidatePair(local, remote Candidate, controlling bool) *candidatePair {
|
||||
return &candidatePair{
|
||||
iceRoleControlling: controlling,
|
||||
remote: remote,
|
||||
local: local,
|
||||
state: candidatePairStateChecking,
|
||||
}
|
||||
}
|
||||
|
||||
// candidatePair represents a combination of a local and remote candidate
|
||||
type candidatePair struct {
|
||||
iceRoleControlling bool
|
||||
remote Candidate
|
||||
local Candidate
|
||||
bindingRequestCount uint16
|
||||
state candidatePairState
|
||||
}
|
||||
|
||||
func (p *candidatePair) String() string {
|
||||
return fmt.Sprintf("prio %d (local, prio %d) %s <-> %s (remote, prio %d)",
|
||||
p.Priority(), p.local.Priority(), p.local, p.remote, p.remote.Priority())
|
||||
}
|
||||
|
||||
func (p *candidatePair) Equal(other *candidatePair) bool {
|
||||
if p == nil && other == nil {
|
||||
return true
|
||||
}
|
||||
if p == nil || other == nil {
|
||||
return false
|
||||
}
|
||||
return p.local.Equal(other.local) && p.remote.Equal(other.remote)
|
||||
}
|
||||
|
||||
// RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
|
||||
// Let G be the priority for the candidate provided by the controlling
|
||||
// agent. Let D be the priority for the candidate provided by the
|
||||
// controlled agent.
|
||||
// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
|
||||
func (p *candidatePair) Priority() uint64 {
|
||||
var g uint32
|
||||
var d uint32
|
||||
if p.iceRoleControlling {
|
||||
g = p.local.Priority()
|
||||
d = p.remote.Priority()
|
||||
} else {
|
||||
g = p.remote.Priority()
|
||||
d = p.local.Priority()
|
||||
}
|
||||
|
||||
// Just implement these here rather
|
||||
// than fooling around with the math package
|
||||
min := func(x, y uint32) uint64 {
|
||||
if x < y {
|
||||
return uint64(x)
|
||||
}
|
||||
return uint64(y)
|
||||
}
|
||||
max := func(x, y uint32) uint64 {
|
||||
if x > y {
|
||||
return uint64(x)
|
||||
}
|
||||
return uint64(y)
|
||||
}
|
||||
cmp := func(x, y uint32) uint64 {
|
||||
if x > y {
|
||||
return uint64(1)
|
||||
}
|
||||
return uint64(0)
|
||||
}
|
||||
|
||||
// 1<<32 overflows uint32; and if both g && d are
|
||||
// maxUint32, this result would overflow uint64
|
||||
return (1<<32-1)*min(g, d) + 2*max(g, d) + cmp(g, d)
|
||||
}
|
||||
|
||||
func (p *candidatePair) Write(b []byte) (int, error) {
|
||||
return p.local.writeTo(b, p.remote)
|
||||
}
|
||||
|
||||
func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) {
|
||||
_, err := local.writeTo(msg.Raw, remote)
|
||||
if err != nil {
|
||||
a.log.Tracef("failed to send STUN message: %s", err)
|
||||
}
|
||||
}
|
||||
30
vendor/github.com/pion/ice/candidaterelatedaddress.go
generated
Normal file
30
vendor/github.com/pion/ice/candidaterelatedaddress.go
generated
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package ice
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CandidateRelatedAddress convey transport addresses related to the
|
||||
// candidate, useful for diagnostics and other purposes.
|
||||
type CandidateRelatedAddress struct {
|
||||
Address string
|
||||
Port int
|
||||
}
|
||||
|
||||
// String makes CandidateRelatedAddress printable
|
||||
func (c *CandidateRelatedAddress) String() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" related %s:%d", c.Address, c.Port)
|
||||
}
|
||||
|
||||
// Equal allows comparing two CandidateRelatedAddresses.
|
||||
// The CandidateRelatedAddress are allowed to be nil.
|
||||
func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool {
|
||||
if c == nil && other == nil {
|
||||
return true
|
||||
}
|
||||
return c != nil && other != nil &&
|
||||
c.Address == other.Address &&
|
||||
c.Port == other.Port
|
||||
}
|
||||
45
vendor/github.com/pion/ice/candidatetype.go
generated
Normal file
45
vendor/github.com/pion/ice/candidatetype.go
generated
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package ice
|
||||
|
||||
// CandidateType represents the type of candidate
|
||||
type CandidateType byte
|
||||
|
||||
// CandidateType enum
|
||||
const (
|
||||
CandidateTypeHost CandidateType = iota + 1
|
||||
CandidateTypeServerReflexive
|
||||
CandidateTypePeerReflexive
|
||||
CandidateTypeRelay
|
||||
)
|
||||
|
||||
// String makes CandidateType printable
|
||||
func (c CandidateType) String() string {
|
||||
switch c {
|
||||
case CandidateTypeHost:
|
||||
return "host"
|
||||
case CandidateTypeServerReflexive:
|
||||
return "srflx"
|
||||
case CandidateTypePeerReflexive:
|
||||
return "prflx"
|
||||
case CandidateTypeRelay:
|
||||
return "relay"
|
||||
}
|
||||
return "Unknown candidate type"
|
||||
}
|
||||
|
||||
// Preference returns the preference weight of a CandidateType
|
||||
//
|
||||
// 4.1.2.2. Guidelines for Choosing Type and Local Preferences
|
||||
// The RECOMMENDED values are 126 for host candidates, 100
|
||||
// for server reflexive candidates, 110 for peer reflexive candidates,
|
||||
// and 0 for relayed candidates.
|
||||
func (c CandidateType) Preference() uint16 {
|
||||
switch c {
|
||||
case CandidateTypeHost:
|
||||
return 126
|
||||
case CandidateTypePeerReflexive:
|
||||
return 110
|
||||
case CandidateTypeServerReflexive:
|
||||
return 100
|
||||
}
|
||||
return 0
|
||||
}
|
||||
60
vendor/github.com/pion/ice/errors.go
generated
Normal file
60
vendor/github.com/pion/ice/errors.go
generated
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package ice
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrUnknownType indicates an error with Unknown info.
|
||||
ErrUnknownType = errors.New("Unknown")
|
||||
|
||||
// ErrSchemeType indicates the scheme type could not be parsed.
|
||||
ErrSchemeType = errors.New("unknown scheme type")
|
||||
|
||||
// ErrSTUNQuery indicates query arguments are provided in a STUN URL.
|
||||
ErrSTUNQuery = errors.New("queries not supported in stun address")
|
||||
|
||||
// ErrInvalidQuery indicates an malformed query is provided.
|
||||
ErrInvalidQuery = errors.New("invalid query")
|
||||
|
||||
// ErrHost indicates malformed hostname is provided.
|
||||
ErrHost = errors.New("invalid hostname")
|
||||
|
||||
// ErrPort indicates malformed port is provided.
|
||||
ErrPort = errors.New("invalid port")
|
||||
|
||||
// ErrProtoType indicates an unsupported transport type was provided.
|
||||
ErrProtoType = errors.New("invalid transport protocol type")
|
||||
|
||||
// ErrClosed indicates the agent is closed
|
||||
ErrClosed = errors.New("the agent is closed")
|
||||
|
||||
// ErrNoCandidatePairs indicates agent does not have a valid candidate pair
|
||||
ErrNoCandidatePairs = errors.New("no candidate pairs available")
|
||||
|
||||
// ErrCanceledByCaller indicates agent connection was canceled by the caller
|
||||
ErrCanceledByCaller = errors.New("connecting canceled by caller")
|
||||
|
||||
// ErrMultipleStart indicates agent was started twice
|
||||
ErrMultipleStart = errors.New("attempted to start agent twice")
|
||||
|
||||
// ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag
|
||||
ErrRemoteUfragEmpty = errors.New("remote ufrag is empty")
|
||||
|
||||
// ErrRemotePwdEmpty indicates agent was started with an empty remote pwd
|
||||
ErrRemotePwdEmpty = errors.New("remote pwd is empty")
|
||||
|
||||
// ErrNoOnCandidateHandler indicates agent was started without OnCandidate
|
||||
// while running in trickle mode.
|
||||
ErrNoOnCandidateHandler = errors.New("no OnCandidate provided")
|
||||
|
||||
// ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times
|
||||
ErrMultipleGatherAttempted = errors.New("attempting to gather candidates during gathering state")
|
||||
|
||||
// ErrUsernameEmpty indicates agent was give TURN URL with an empty Username
|
||||
ErrUsernameEmpty = errors.New("username is empty")
|
||||
|
||||
// ErrPasswordEmpty indicates agent was give TURN URL with an empty Password
|
||||
ErrPasswordEmpty = errors.New("password is empty")
|
||||
|
||||
// ErrAddressParseFailed indicates we were unable to parse a candidate address
|
||||
ErrAddressParseFailed = errors.New("failed to parse address")
|
||||
)
|
||||
381
vendor/github.com/pion/ice/gather.go
generated
Normal file
381
vendor/github.com/pion/ice/gather.go
generated
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/turnc"
|
||||
)
|
||||
|
||||
const (
|
||||
stunGatherTimeout = time.Second * 5
|
||||
)
|
||||
|
||||
func localInterfaces(networkTypes []NetworkType) (ips []net.IP) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return ips
|
||||
}
|
||||
|
||||
var IPv4Requested, IPv6Requested bool
|
||||
for _, typ := range networkTypes {
|
||||
if typ.IsIPv4() {
|
||||
IPv4Requested = true
|
||||
}
|
||||
|
||||
if typ.IsIPv6() {
|
||||
IPv6Requested = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // interface down
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // loopback interface
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return ips
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipv4 := ip.To4(); ipv4 == nil {
|
||||
if !IPv6Requested {
|
||||
continue
|
||||
} else if !isSupportedIPv6(ip) {
|
||||
continue
|
||||
}
|
||||
} else if !IPv4Requested {
|
||||
continue
|
||||
}
|
||||
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
func listenUDP(portMax, portMin int, network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
if (laddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
|
||||
return net.ListenUDP(network, laddr)
|
||||
}
|
||||
var i, j int
|
||||
i = portMin
|
||||
if i == 0 {
|
||||
i = 1
|
||||
}
|
||||
j = portMax
|
||||
if j == 0 {
|
||||
j = 0xFFFF
|
||||
}
|
||||
for i <= j {
|
||||
c, e := net.ListenUDP(network, &net.UDPAddr{IP: laddr.IP, Port: i})
|
||||
if e == nil {
|
||||
return c, e
|
||||
}
|
||||
i++
|
||||
}
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
// GatherCandidates initiates the trickle based gathering process.
|
||||
func (a *Agent) GatherCandidates() error {
|
||||
gatherErrChan := make(chan error, 1)
|
||||
|
||||
runErr := a.run(func(agent *Agent) {
|
||||
if a.gatheringState != GatheringStateNew {
|
||||
gatherErrChan <- ErrMultipleGatherAttempted
|
||||
return
|
||||
} else if a.onCandidateHdlr == nil {
|
||||
gatherErrChan <- ErrNoOnCandidateHandler
|
||||
return
|
||||
}
|
||||
|
||||
go a.gatherCandidates()
|
||||
|
||||
gatherErrChan <- nil
|
||||
})
|
||||
if runErr != nil {
|
||||
return runErr
|
||||
}
|
||||
return <-gatherErrChan
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidates() {
|
||||
gatherStateUpdated := make(chan bool)
|
||||
if err := a.run(func(agent *Agent) {
|
||||
a.gatheringState = GatheringStateGathering
|
||||
close(gatherStateUpdated)
|
||||
}); err != nil {
|
||||
a.log.Warnf("failed to set gatheringState to GatheringStateGathering for gatherCandidates: %v", err)
|
||||
return
|
||||
}
|
||||
<-gatherStateUpdated
|
||||
|
||||
for _, t := range a.candidateTypes {
|
||||
switch t {
|
||||
case CandidateTypeHost:
|
||||
a.gatherCandidatesLocal(a.networkTypes)
|
||||
case CandidateTypeServerReflexive:
|
||||
a.gatherCandidatesSrflx(a.urls, a.networkTypes)
|
||||
case CandidateTypeRelay:
|
||||
if err := a.gatherCandidatesRelay(a.urls); err != nil {
|
||||
a.log.Errorf("Failed to gather relay candidates: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
if a.onCandidateHdlr != nil {
|
||||
go a.onCandidateHdlr(nil)
|
||||
}
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to run onCandidateHdlr task: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
a.gatheringState = GatheringStateComplete
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to update gatheringState: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesLocal(networkTypes []NetworkType) {
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
localIPs := localInterfaces(networkTypes)
|
||||
wg.Add(len(localIPs) * len(supportedNetworks))
|
||||
for _, ip := range localIPs {
|
||||
for _, network := range supportedNetworks {
|
||||
go func(network string, ip net.IP) {
|
||||
defer wg.Done()
|
||||
conn, err := listenUDP(int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("could not listen %s %s\n", network, ip)
|
||||
return
|
||||
}
|
||||
|
||||
address := ip.String()
|
||||
if a.mDNSMode == MulticastDNSModeQueryAndGather {
|
||||
address = a.mDNSName
|
||||
}
|
||||
|
||||
port := conn.LocalAddr().(*net.UDPAddr).Port
|
||||
c, err := NewCandidateHost(network, address, port, ComponentRTP)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to create host candidate: %s %s %d: %v\n", network, ip, port, err)
|
||||
return
|
||||
}
|
||||
|
||||
if a.mDNSMode == MulticastDNSModeQueryAndGather {
|
||||
if err = c.setIP(ip); err != nil {
|
||||
a.log.Warnf("Failed to create host candidate: %s %s %d: %v\n", network, ip, port, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
a.addCandidate(c)
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to append to localCandidates: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.start(a, conn)
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
if a.onCandidateHdlr != nil {
|
||||
go a.onCandidateHdlr(c)
|
||||
}
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to run onCandidateHdlr task: %v\n", err)
|
||||
return
|
||||
}
|
||||
}(network, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesSrflx(urls []*URL, networkTypes []NetworkType) {
|
||||
for _, networkType := range networkTypes {
|
||||
network := networkType.String()
|
||||
for _, url := range urls {
|
||||
if url.Scheme != SchemeTypeSTUN {
|
||||
continue
|
||||
}
|
||||
|
||||
hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
|
||||
serverAddr, err := net.ResolveUDPAddr(network, hostPort)
|
||||
if err != nil {
|
||||
a.log.Warnf("failed to resolve stun host: %s: %v", hostPort, err)
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err := listenUDP(int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to listen on %s for %s: %v\n", conn.LocalAddr().String(), serverAddr.String(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
xoraddr, err := getXORMappedAddr(conn, serverAddr, stunGatherTimeout)
|
||||
if err != nil {
|
||||
a.log.Warnf("could not get server reflexive address %s %s: %v\n", network, url, err)
|
||||
continue
|
||||
}
|
||||
|
||||
laddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
ip := xoraddr.IP
|
||||
port := xoraddr.Port
|
||||
relIP := laddr.IP.String()
|
||||
relPort := laddr.Port
|
||||
c, err := NewCandidateServerReflexive(network, ip.String(), port, ComponentRTP, relIP, relPort)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to create server reflexive candidate: %s %s %d: %v\n", network, ip, port, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
a.addCandidate(c)
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to append to localCandidates: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
c.start(a, conn)
|
||||
|
||||
if err := a.run(func(agent *Agent) {
|
||||
if a.onCandidateHdlr != nil {
|
||||
go a.onCandidateHdlr(c)
|
||||
}
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to run onCandidateHdlr task: %v\n", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesRelay(urls []*URL) error {
|
||||
network := NetworkTypeUDP4.String() // TODO IPv6
|
||||
for _, url := range urls {
|
||||
switch {
|
||||
case url.Scheme != SchemeTypeTURN:
|
||||
continue
|
||||
case url.Username == "":
|
||||
return ErrUsernameEmpty
|
||||
case url.Password == "":
|
||||
return ErrPasswordEmpty
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr(network, fmt.Sprintf("%s:%d", url.Host, url.Port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := net.DialUDP(network, nil, raddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, clientErr := turnc.New(turnc.Options{
|
||||
Conn: c,
|
||||
Username: url.Username,
|
||||
Password: url.Password,
|
||||
})
|
||||
if clientErr != nil {
|
||||
return clientErr
|
||||
}
|
||||
allocation, allocErr := client.Allocate()
|
||||
if allocErr != nil {
|
||||
return allocErr
|
||||
}
|
||||
|
||||
laddr := c.LocalAddr().(*net.UDPAddr)
|
||||
ip := allocation.Relayed().IP
|
||||
port := allocation.Relayed().Port
|
||||
|
||||
candidate, err := NewCandidateRelay(network, ip.String(), port, ComponentRTP, laddr.IP.String(), laddr.Port)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to create server reflexive candidate: %s %s %d: %v\n", network, ip, port, err)
|
||||
continue
|
||||
}
|
||||
candidate.setAllocation(client, allocation)
|
||||
|
||||
a.addCandidate(candidate)
|
||||
candidate.start(a, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getXORMappedAddr initiates a stun requests to serverAddr using conn, reads the response and returns
|
||||
// the XORMappedAddress returned by the stun server.
|
||||
//
|
||||
// Adapted from stun v0.2.
|
||||
func getXORMappedAddr(conn *net.UDPConn, serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) {
|
||||
if deadline > 0 {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(deadline)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if deadline > 0 {
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
}
|
||||
}()
|
||||
resp, err := stunRequest(
|
||||
conn.Read,
|
||||
func(b []byte) (int, error) {
|
||||
return conn.WriteTo(b, serverAddr)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var addr stun.XORMappedAddress
|
||||
if err = addr.GetFrom(resp); err != nil {
|
||||
return nil, fmt.Errorf("failed to get XOR-MAPPED-ADDRESS response: %v", err)
|
||||
}
|
||||
return &addr, nil
|
||||
}
|
||||
|
||||
func stunRequest(read func([]byte) (int, error), write func([]byte) (int, error)) (*stun.Message, error) {
|
||||
req, err := stun.Build(stun.BindingRequest, stun.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = write(req.Raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
const maxMessageSize = 1280
|
||||
bs := make([]byte, maxMessageSize)
|
||||
n, err := read(bs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &stun.Message{Raw: bs[:n]}
|
||||
if err := res.Decode(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
14
vendor/github.com/pion/ice/go.mod
generated
vendored
Normal file
14
vendor/github.com/pion/ice/go.mod
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
module github.com/pion/ice
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/pion/logging v0.2.1
|
||||
github.com/pion/mdns v0.0.2
|
||||
github.com/pion/stun v0.3.1
|
||||
github.com/pion/transport v0.7.0
|
||||
github.com/pion/turn v1.1.4
|
||||
github.com/pion/turnc v0.0.6
|
||||
github.com/stretchr/testify v1.3.0
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b
|
||||
)
|
||||
33
vendor/github.com/pion/ice/go.sum
generated
vendored
Normal file
33
vendor/github.com/pion/ice/go.sum
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gortc/turn v0.7.1/go.mod h1:3FZ+LvCZKCKu6YYgwuYPqEi3FqCtdjfSFnFqVQNwfjk=
|
||||
github.com/gortc/turn v0.7.3 h1:CE72C79erbcsfa6L/QDhKztcl2kDq1UK20ImrJWDt/w=
|
||||
github.com/gortc/turn v0.7.3/go.mod h1:gvguwaGAFyv5/9KrcW9MkCgHALYD+e99mSM7pSCYYho=
|
||||
github.com/pion/logging v0.2.1 h1:LwASkBKZ+2ysGJ+jLv1E/9H1ge0k1nTfi1X+5zirkDk=
|
||||
github.com/pion/logging v0.2.1/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.2 h1:T22Gg4dSuYVYsZ21oRFh9z7twzAm27+5PEKiABbjCvM=
|
||||
github.com/pion/mdns v0.0.2/go.mod h1:VrN3wefVgtfL8QgpEblPUC46ag1reLIfpqekCnKunLE=
|
||||
github.com/pion/stun v0.3.0/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M=
|
||||
github.com/pion/stun v0.3.1 h1:d09JJzOmOS8ZzIp8NppCMgrxGZpJ4Ix8qirfNYyI3BA=
|
||||
github.com/pion/stun v0.3.1/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M=
|
||||
github.com/pion/transport v0.7.0 h1:EsXN8TglHMlKZMo4ZGqwK6QgXBu0WYg7wfGMWIXsS+w=
|
||||
github.com/pion/transport v0.7.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||
github.com/pion/turn v1.1.4 h1:yGxcasBvge4idNjxjowePn8oW43C4v70bXroBBKLyKY=
|
||||
github.com/pion/turn v1.1.4/go.mod h1:2O2GFDGO6+hJ5gsyExDhoNHtVcacPB1NOyc81gkq0WA=
|
||||
github.com/pion/turnc v0.0.6 h1:FHsmwYvdJ8mhT1/ZtWWer9L0unEb7AyRgrymfWy6mEY=
|
||||
github.com/pion/turnc v0.0.6/go.mod h1:4MSFv5i0v3MRkDLdo5eF9cD/xJtj1pxSphHNnxKL2W8=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b h1:/zjbcJPEGAyu6Is/VBOALsgdi4z9+kz/Vtdm6S+beD0=
|
||||
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
76
vendor/github.com/pion/ice/ice.go
generated
Normal file
76
vendor/github.com/pion/ice/ice.go
generated
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package ice
|
||||
|
||||
// ConnectionState is an enum showing the state of a ICE Connection
|
||||
type ConnectionState int
|
||||
|
||||
// List of supported States
|
||||
const (
|
||||
// ConnectionStateNew ICE agent is gathering addresses
|
||||
ConnectionStateNew = iota + 1
|
||||
|
||||
// ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match
|
||||
ConnectionStateChecking
|
||||
|
||||
// ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs
|
||||
ConnectionStateConnected
|
||||
|
||||
// ConnectionStateCompleted ICE agent has finished
|
||||
ConnectionStateCompleted
|
||||
|
||||
// ConnectionStateFailed ICE agent never could successfully connect
|
||||
ConnectionStateFailed
|
||||
|
||||
// ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state
|
||||
ConnectionStateDisconnected
|
||||
|
||||
// ConnectionStateClosed ICE agent has finished and is no longer handling requests
|
||||
ConnectionStateClosed
|
||||
)
|
||||
|
||||
func (c ConnectionState) String() string {
|
||||
switch c {
|
||||
case ConnectionStateNew:
|
||||
return "New"
|
||||
case ConnectionStateChecking:
|
||||
return "Checking"
|
||||
case ConnectionStateConnected:
|
||||
return "Connected"
|
||||
case ConnectionStateCompleted:
|
||||
return "Completed"
|
||||
case ConnectionStateFailed:
|
||||
return "Failed"
|
||||
case ConnectionStateDisconnected:
|
||||
return "Disconnected"
|
||||
case ConnectionStateClosed:
|
||||
return "Closed"
|
||||
default:
|
||||
return "Invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// GatheringState describes the state of the candidate gathering process
|
||||
type GatheringState int
|
||||
|
||||
const (
|
||||
// GatheringStateNew indicates candidate gatering is not yet started
|
||||
GatheringStateNew GatheringState = iota + 1
|
||||
|
||||
// GatheringStateGathering indicates candidate gatering is ongoing
|
||||
GatheringStateGathering
|
||||
|
||||
// GatheringStateComplete indicates candidate gatering has been completed
|
||||
GatheringStateComplete
|
||||
)
|
||||
|
||||
func (t GatheringState) String() string {
|
||||
switch t {
|
||||
case GatheringStateNew:
|
||||
return "new"
|
||||
case GatheringStateGathering:
|
||||
return "gathering"
|
||||
case GatheringStateComplete:
|
||||
return "complete"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
83
vendor/github.com/pion/ice/icecontrol.go
generated
Normal file
83
vendor/github.com/pion/ice/icecontrol.go
generated
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package ice
|
||||
|
||||
import "github.com/pion/stun"
|
||||
|
||||
// tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING}
|
||||
// and represents the so-called tiebreaker number.
|
||||
type tiebreaker uint64
|
||||
|
||||
const tiebreakerSize = 8 // 64 bit
|
||||
|
||||
// AddToAs adds tiebreaker value to m as t attribute.
|
||||
func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error {
|
||||
v := make([]byte, tiebreakerSize)
|
||||
bin.PutUint64(v, uint64(a))
|
||||
m.Add(t, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFromAs decodes tiebreaker value in message getting it as for t type.
|
||||
func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error {
|
||||
v, err := m.Get(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = stun.CheckSize(t, len(v), tiebreakerSize); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = tiebreaker(bin.Uint64(v))
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttrControlled represents ICE-CONTROLLED attribute.
|
||||
type AttrControlled uint64
|
||||
|
||||
// AddTo adds ICE-CONTROLLED to message.
|
||||
func (c AttrControlled) AddTo(m *stun.Message) error {
|
||||
return tiebreaker(c).AddToAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// GetFrom decodes ICE-CONTROLLED from message.
|
||||
func (c *AttrControlled) GetFrom(m *stun.Message) error {
|
||||
return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// AttrControlling represents ICE-CONTROLLING attribute.
|
||||
type AttrControlling uint64
|
||||
|
||||
// AddTo adds ICE-CONTROLLING to message.
|
||||
func (c AttrControlling) AddTo(m *stun.Message) error {
|
||||
return tiebreaker(c).AddToAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
|
||||
// GetFrom decodes ICE-CONTROLLING from message.
|
||||
func (c *AttrControlling) GetFrom(m *stun.Message) error {
|
||||
return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
|
||||
// AttrControl is helper that wraps ICE-{CONTROLLED,CONTROLLING}.
|
||||
type AttrControl struct {
|
||||
Role Role
|
||||
Tiebreaker uint64
|
||||
}
|
||||
|
||||
// AddTo adds ICE-CONTROLLED or ICE-CONTROLLING attribute depending on Role.
|
||||
func (c AttrControl) AddTo(m *stun.Message) error {
|
||||
if c.Role == Controlling {
|
||||
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// GetFrom decodes Role and Tiebreaker value from message.
|
||||
func (c *AttrControl) GetFrom(m *stun.Message) error {
|
||||
if m.Contains(stun.AttrICEControlling) {
|
||||
c.Role = Controlling
|
||||
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
if m.Contains(stun.AttrICEControlled) {
|
||||
c.Role = Controlled
|
||||
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
return stun.ErrAttributeNotFound
|
||||
}
|
||||
32
vendor/github.com/pion/ice/mdns.go
generated
Normal file
32
vendor/github.com/pion/ice/mdns.go
generated
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MulticastDNSMode represents the different Multicast modes ICE can run in
|
||||
type MulticastDNSMode byte
|
||||
|
||||
// MulticastDNSMode enum
|
||||
const (
|
||||
// MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs
|
||||
MulticastDNSModeDisabled MulticastDNSMode = iota + 1
|
||||
|
||||
// MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs
|
||||
MulticastDNSModeQueryOnly
|
||||
|
||||
// MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted, and local host candidates will use mDNS
|
||||
MulticastDNSModeQueryAndGather
|
||||
)
|
||||
|
||||
func generateMulticastDNSName() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b) //nolint
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%X-%X-%X-%X-%X.local", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil
|
||||
}
|
||||
124
vendor/github.com/pion/ice/networktype.go
generated
Normal file
124
vendor/github.com/pion/ice/networktype.go
generated
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
udp = "udp"
|
||||
tcp = "tcp"
|
||||
)
|
||||
|
||||
var supportedNetworks = []string{
|
||||
udp,
|
||||
// tcp, // Not supported yet
|
||||
}
|
||||
|
||||
var supportedNetworkTypes = []NetworkType{
|
||||
NetworkTypeUDP4,
|
||||
NetworkTypeUDP6,
|
||||
// NetworkTypeTCP4, // Not supported yet
|
||||
// NetworkTypeTCP6, // Not supported yet
|
||||
}
|
||||
|
||||
// NetworkType represents the type of network
|
||||
type NetworkType int
|
||||
|
||||
const (
|
||||
// NetworkTypeUDP4 indicates UDP over IPv4.
|
||||
NetworkTypeUDP4 NetworkType = iota + 1
|
||||
|
||||
// NetworkTypeUDP6 indicates UDP over IPv6.
|
||||
NetworkTypeUDP6
|
||||
|
||||
// NetworkTypeTCP4 indicates TCP over IPv4.
|
||||
NetworkTypeTCP4
|
||||
|
||||
// NetworkTypeTCP6 indicates TCP over IPv6.
|
||||
NetworkTypeTCP6
|
||||
)
|
||||
|
||||
func (t NetworkType) String() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4:
|
||||
return "udp4"
|
||||
case NetworkTypeUDP6:
|
||||
return "udp6"
|
||||
case NetworkTypeTCP4:
|
||||
return "tcp4"
|
||||
case NetworkTypeTCP6:
|
||||
return "tcp6"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkShort returns the short network description
|
||||
func (t NetworkType) NetworkShort() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeUDP6:
|
||||
return udp
|
||||
case NetworkTypeTCP4, NetworkTypeTCP6:
|
||||
return tcp
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// IsReliable returns true if the network is reliable
|
||||
func (t NetworkType) IsReliable() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeUDP6:
|
||||
return false
|
||||
case NetworkTypeTCP4, NetworkTypeTCP6:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIPv4 returns whether the network type is IPv4 or not.
|
||||
func (t NetworkType) IsIPv4() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeTCP4:
|
||||
return true
|
||||
case NetworkTypeUDP6, NetworkTypeTCP6:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIPv6 returns whether the network type is IPv6 or not.
|
||||
func (t NetworkType) IsIPv6() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeTCP4:
|
||||
return false
|
||||
case NetworkTypeUDP6, NetworkTypeTCP6:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// determineNetworkType determines the type of network based on
|
||||
// the short network string and an IP address.
|
||||
func determineNetworkType(network string, ip net.IP) (NetworkType, error) {
|
||||
ipv4 := ip.To4() != nil
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(strings.ToLower(network), udp):
|
||||
if ipv4 {
|
||||
return NetworkTypeUDP4, nil
|
||||
}
|
||||
return NetworkTypeUDP6, nil
|
||||
|
||||
case strings.HasPrefix(strings.ToLower(network), tcp):
|
||||
if ipv4 {
|
||||
return NetworkTypeTCP4, nil
|
||||
}
|
||||
return NetworkTypeTCP6, nil
|
||||
|
||||
}
|
||||
|
||||
return NetworkType(0), fmt.Errorf("unable to determine networkType from %s %s", network, ip)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue