Introduce Makefile and go vendor directory for faster build (#67)

This commit is contained in:
Sadlil Rhythom 2019-08-16 18:13:36 +02:00 committed by giongto35
parent b4970f37c4
commit 42e281234c
1709 changed files with 521045 additions and 14 deletions

2
vendor/github.com/pion/datachannel/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
# vim temporary files
*.sw[poe]

11
vendor/github.com/pion/datachannel/.golangci.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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=

View 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
// }

View 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
}

View 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
View 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
View file

@ -0,0 +1,2 @@
vendor
*-fuzz.zip

29
vendor/github.com/pion/dtls/.golangci.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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)
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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)
}

View 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"
}

View 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"
}

View 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,
}

View 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
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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
View 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
View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
View 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
}

View 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
View 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
View 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
View 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
View 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:])
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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: {},
}

View 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
View 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()
}

View 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
View 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
}

View 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 messagesused for algorithm negotiation and key establishment.
2. ChangeCipherSpec messagesreally part of the handshake but technically a separate kind of message.
3. Alert messagesused 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
}

View 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
View 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()
}

View 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
}

View 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,
}

View file

@ -0,0 +1,6 @@
package dtls
type signatureHashAlgorithm struct {
hash HashAlgorithm
signature signatureAlgorithm
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

40
vendor/github.com/pion/ice/candidate.go generated Normal file
View 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)
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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
}

View 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)
}
}

View 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
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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