Skip to content

Commit

Permalink
add incoming records sorting buffer, handshake encoder/decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelbender committed Sep 12, 2016
1 parent 3bd8b2d commit a56125f
Show file tree
Hide file tree
Showing 10 changed files with 3,031 additions and 0 deletions.
82 changes: 82 additions & 0 deletions dtls/alert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package dtls

import "strconv"

type alert uint8

// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6
const (
alertCloseNotify alert = 0
alertUnexpectedMessage alert = 10
alertBadRecordMAC alert = 20
alertDecryptionFailed alert = 21
alertRecordOverflow alert = 22
alertDecompressionFailure alert = 30
alertHandshakeFailure alert = 40
alertBadCertificate alert = 42
alertUnsupportedCertificate alert = 43
alertCertificateRevoked alert = 44
alertCertificateExpired alert = 45
alertCertificateUnknown alert = 46
alertIllegalParameter alert = 47
alertUnknownCA alert = 48
alertAccessDenied alert = 49
alertDecodeError alert = 50
alertDecryptError alert = 51
alertProtocolVersion alert = 70
alertInsufficientSecurity alert = 71
alertInternalError alert = 80
alertInappropriateFallback alert = 86
alertUserCanceled alert = 90
alertNoRenegotiation alert = 100
alertUnsupportedExtension alert = 110
alertCertificateUnobtainable alert = 111
alertUnrecognizedName alert = 112
alertBadCertificateStatusResponse alert = 113
alertBadCertificateHashValue alert = 114
alertUnknownPSKIdentity alert = 115
)

var alertText = map[alert]string{
alertCloseNotify: "close notify",
alertUnexpectedMessage: "unexpected message",
alertBadRecordMAC: "bad record MAC",
alertDecryptionFailed: "decryption failed",
alertRecordOverflow: "record overflow",
alertDecompressionFailure: "decompression failure",
alertHandshakeFailure: "handshake failure",
alertBadCertificate: "bad certificate",
alertUnsupportedCertificate: "unsupported certificate",
alertCertificateRevoked: "revoked certificate",
alertCertificateExpired: "expired certificate",
alertCertificateUnknown: "unknown certificate",
alertIllegalParameter: "illegal parameter",
alertUnknownCA: "unknown certificate authority",
alertAccessDenied: "access denied",
alertDecodeError: "error decoding message",
alertDecryptError: "error decrypting message",
alertProtocolVersion: "protocol version not supported",
alertInsufficientSecurity: "insufficient security level",
alertInternalError: "internal error",
alertInappropriateFallback: "inappropriate fallback",
alertUserCanceled: "user canceled",
alertNoRenegotiation: "no renegotiation",
alertUnsupportedExtension: "unsupported extension",
alertCertificateUnobtainable: "certificate unobtainable",
alertUnrecognizedName: "unrecognized name",
alertBadCertificateStatusResponse: "bad certificate status response",
alertBadCertificateHashValue: "bad certificate hash value",
alertUnknownPSKIdentity: "unknown PSK identity",
}

func (e alert) String() string {
s, ok := alertText[e]
if ok {
return "dtls: " + s
}
return "dtls: alert(" + strconv.Itoa(int(e)) + ")"
}

func (e alert) Error() string {
return e.String()
}
180 changes: 180 additions & 0 deletions dtls/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package dtls

import (
"encoding/hex"
"errors"
"io"
"log"
"sort"
"strconv"
)

var ErrFormat = errors.New("dtls: incorrect format")

type reader interface {
Next(n int) ([]byte, error)
Len() int
}

const (
maxPacketSize = 4096
maxUnreadRecords = 64
)

type bufferReader struct {
buf []byte
pos int
}

func (r *bufferReader) Next(n int) ([]byte, error) {
p := r.pos + n
if len(r.buf) < r.pos+n {
return nil, io.EOF
}
off := r.pos
r.pos = p
return r.buf[off:p], nil
}

func (r *bufferReader) Len() int {
return len(r.buf) - r.pos
}

type receiver struct {
inner io.Reader
buf [maxPacketSize]byte
records recordSeq
epoch uint16
seq uint64
}

func (r *receiver) Read() (*record, error) {
for {
if len(r.records) > 0 {
if rec := r.records[0]; rec.seq == r.seq {
r.records = r.records[1:]
r.seq++
return rec, nil
}
}
n, err := r.inner.Read(r.buf[:])
if err != nil {
return nil, err
}
r.fill(r.buf[:n])
}
}

func (r *receiver) fill(b []byte) (err error) {
log.Printf("RECV %s", hex.Dump(b))
rec := new(record)
if err = rec.read(b); err != nil {
return err
}
if rec.seq < r.seq {
return nil
}
r.records = append(r.records, rec)
n := 0
for _, it := range r.records {
it.age++
if it.seq < r.seq || it.seq > r.seq+maxUnreadRecords || it.age > maxUnreadRecords {
continue
}
r.records[n] = it
n++
}
r.records = r.records[:n]
sort.Sort(r.records)
return
}

type handshakeDecoder struct {
*receiver
ver uint16
mseq uint16
}

func (r *handshakeDecoder) ReadMessage() (msg handshakeMsg, err error) {
var rec *record
var buf []byte
var off int
for {
if rec, err = r.Read(); err != nil {
return
}
f := new(fragment)
if err = f.read(rec.buf); err != nil {
return
}
if f.off != off {
return nil, ErrFormat
}
if buf == nil {
buf = make([]byte, f.sum)
}
if off += copy(buf[off:], f.buf); off < len(buf) {
continue
}
msg = newMessage(f.typ)
if msg == nil {
return nil, errors.New("dtls: unsupported handshake message: " + strconv.Itoa(int(f.typ)))
}
if err = msg.Unmarshal(&bufferReader{buf: buf}); err != nil {
return nil, err
}
return
}
}

func getInt24(b []byte) int {
return int(b[0])<<16 | int(b[1])<<8 | int(b[2])
}

func getSlice16(r reader) (v []uint16, err error) {
var b []byte
if b, err = r.Next(2); err != nil {
return
}
n := int(be.Uint16(b))
if b, err = r.Next(n); err != nil {
return
}
for i, s := 0, n-1; i < s; i += 2 {
v = append(v, be.Uint16(b[i:]))
}
return
}

func getSlice8(r reader) (v []uint8, err error) {
var b []byte
if b, err = r.Next(1); err != nil {
return
}
n := int(b[0])
if n > 0 {
v, err = r.Next(n)
}
return
}

func getExtensions(r reader) (v map[uint16]Extension, err error) {
var b []byte
if b, err = r.Next(2); err != nil {
return
}
n := int(be.Uint16(b))
v = make(map[uint16]Extension)
for n > 0 {
if b, err = r.Next(4); err != nil {
return
}
typ, s := be.Uint16(b), int(be.Uint16(b[2:]))
if b, err = r.Next(s); err != nil {
return
}
v[typ] = rawExtension(b)
n -= 4 + s
}
return
}
40 changes: 40 additions & 0 deletions dtls/decoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dtls

import (
"bytes"
"encoding/hex"
"log"
"testing"
)

func TestDecoder(t *testing.T) {
t.Parallel()
data, _ := hex.DecodeString("16feff000000000000000000980100008c000000000000008cfefded6b5094c6c428fc41606dc37b295f51392b1033fc9549775e457ffbeac3ea6e00000022c02bc02f009ecca9cca8cc14cc13c009c0130033c00ac0140039009c002f0035000a01000040ff010001000017000000230000000d0012001006010603050105030401040302010203000e000700040002000100000b00020100000a00080006001d00170018")
dec := &handshakeDecoder{
receiver: &receiver{
inner: bytes.NewReader(data),
},
}
msg, err := dec.ReadMessage()
if err != nil {
t.Fatal("read", err)
}
hello := msg.(*clientHello)
if hello == nil {
t.Fatal("message type error")
}
if hello.Version != VersionDTLS12 {
t.Fatal("handshake version")
}
random, _ := hex.DecodeString("ed6b5094c6c428fc41606dc37b295f51392b1033fc9549775e457ffbeac3ea6e")
if !bytes.Equal(hello.RandomBytes, random) {
t.Fatal("random")
}
if hello.Session != nil {
t.Fatal("session")
}
if hello.Cookie != nil {
t.Fatal("cookie")
}
log.Printf("%#v", msg)
}

0 comments on commit a56125f

Please sign in to comment.