mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-05 21:27:35 +03:00
move stuff from example server to new Session and SessionConfig classes
This commit is contained in:
parent
49ccd0bb01
commit
09879ed4f7
5 changed files with 225 additions and 122 deletions
157
example/main.go
157
example/main.go
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// QuicVersion32Bytes is the QUIC protocol version
|
||||
// QuicVersionNumber32 is the QUIC protocol version
|
||||
QuicVersionNumber32 = 32
|
||||
)
|
||||
|
||||
|
@ -25,6 +25,11 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
serverConfig := quic.NewServerConfig(crypto.NewCurve25519KEX(), keyData)
|
||||
|
||||
// TODO: When should a session be created?
|
||||
sessions := map[uint64]*quic.Session{}
|
||||
|
||||
addr, err := net.ResolveUDPAddr("udp", "localhost:6121")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -35,121 +40,45 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
data := make([]byte, 0x10000)
|
||||
n, remoteAddr, err := conn.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = data[:n]
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
fmt.Printf("Remote addr: %v\n", remoteAddr)
|
||||
|
||||
publicHeader, err := quic.ParsePublicHeader(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// send Version Negotiation Packet if the client is speaking a different protocol version
|
||||
if publicHeader.VersionFlag && publicHeader.QuicVersion != QuicVersion32 {
|
||||
fmt.Println("Sending VersionNegotiationPacket")
|
||||
fullReply := &bytes.Buffer{}
|
||||
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1, VersionFlag: true}
|
||||
err = responsePublicHeader.WritePublicHeader(fullReply)
|
||||
for {
|
||||
data := make([]byte, 0x10000)
|
||||
n, remoteAddr, err := conn.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
utils.WriteUint32BigEndian(fullReply, QuicVersion32)
|
||||
conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
|
||||
data = data[:n]
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
fmt.Printf("Received %d bytes from %v\n", n, remoteAddr)
|
||||
|
||||
publicHeader, err := quic.ParsePublicHeader(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%#v\n", publicHeader)
|
||||
|
||||
// Send Version Negotiation Packet if the client is speaking a different protocol version
|
||||
if publicHeader.VersionFlag && publicHeader.QuicVersion != QuicVersion32 {
|
||||
fmt.Println("Sending VersionNegotiationPacket")
|
||||
fullReply := &bytes.Buffer{}
|
||||
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1, VersionFlag: true}
|
||||
err = responsePublicHeader.WritePublicHeader(fullReply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
utils.WriteUint32BigEndian(fullReply, QuicVersion32)
|
||||
_, err := conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
session, ok := sessions[publicHeader.ConnectionID]
|
||||
if !ok {
|
||||
session = quic.NewSession(conn, publicHeader.ConnectionID, serverConfig)
|
||||
sessions[publicHeader.ConnectionID] = session
|
||||
}
|
||||
session.HandlePacket(remoteAddr, data[0:n-r.Len()], publicHeader, r)
|
||||
}
|
||||
|
||||
nullAEAD := &crypto.NullAEAD{}
|
||||
r, err = nullAEAD.Open(0, data[0:int(r.Size())-r.Len()], r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privateFlag, err := r.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var entropyAcc quic.EntropyAccumulator
|
||||
entropyAcc.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)
|
||||
|
||||
frame, err := quic.ParseStreamFrame(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
messageTag, cryptoData, err := quic.ParseCryptoMessage(frame.Data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if messageTag != quic.TagCHLO {
|
||||
panic("expected CHLO")
|
||||
}
|
||||
|
||||
fmt.Printf("Talking to: %q\n", cryptoData[quic.TagUAID])
|
||||
|
||||
kex := crypto.NewCurve25519KEX()
|
||||
|
||||
serverConfig := &bytes.Buffer{}
|
||||
quic.WriteCryptoMessage(serverConfig, quic.TagSCFG, map[quic.Tag][]byte{
|
||||
quic.TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF},
|
||||
quic.TagKEXS: []byte("C255"),
|
||||
quic.TagAEAD: []byte("AESG"),
|
||||
quic.TagPUBS: append([]byte{0x20, 0x00, 0x00}, kex.PublicKey()...),
|
||||
quic.TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
|
||||
quic.TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
quic.TagVER: []byte("Q032"),
|
||||
})
|
||||
|
||||
proof, err := keyData.SignServerProof(frame.Data, serverConfig.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
serverReply := &bytes.Buffer{}
|
||||
quic.WriteCryptoMessage(serverReply, quic.TagREJ, map[quic.Tag][]byte{
|
||||
quic.TagSCFG: serverConfig.Bytes(),
|
||||
quic.TagCERT: keyData.GetCERTdata(),
|
||||
quic.TagPROF: proof,
|
||||
})
|
||||
|
||||
replyFrame := &bytes.Buffer{}
|
||||
replyFrame.WriteByte(0) // Private header
|
||||
quic.WriteAckFrame(replyFrame, &quic.AckFrame{
|
||||
Entropy: entropyAcc.Get(),
|
||||
LargestObserved: 1,
|
||||
})
|
||||
quic.WriteStreamFrame(replyFrame, &quic.StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: serverReply.Bytes(),
|
||||
})
|
||||
|
||||
fullReply := &bytes.Buffer{}
|
||||
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1}
|
||||
fmt.Println(responsePublicHeader)
|
||||
err = responsePublicHeader.WritePublicHeader(fullReply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
nullAEAD.Seal(0, fullReply, fullReply.Bytes(), replyFrame.Bytes())
|
||||
|
||||
conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
|
||||
|
||||
n, _, err = conn.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = data[:n]
|
||||
r = bytes.NewReader(data)
|
||||
|
||||
publicHeader, err = quic.ParsePublicHeader(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%#v\n", publicHeader)
|
||||
}
|
||||
|
|
10
frame.go
10
frame.go
|
@ -7,6 +7,10 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
type Frame interface {
|
||||
Write(b *bytes.Buffer) error
|
||||
}
|
||||
|
||||
// A StreamFrame of QUIC
|
||||
type StreamFrame struct {
|
||||
FinBit bool
|
||||
|
@ -67,7 +71,7 @@ func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
|
|||
}
|
||||
|
||||
// WriteStreamFrame writes a stream frame.
|
||||
func WriteStreamFrame(b *bytes.Buffer, f *StreamFrame) {
|
||||
func (f *StreamFrame) Write(b *bytes.Buffer) error {
|
||||
typeByte := uint8(0x80)
|
||||
if f.FinBit {
|
||||
typeByte ^= 0x40
|
||||
|
@ -84,6 +88,7 @@ func WriteStreamFrame(b *bytes.Buffer, f *StreamFrame) {
|
|||
}
|
||||
utils.WriteUint16(b, uint16(len(f.Data)))
|
||||
b.Write(f.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An AckFrame in QUIC
|
||||
|
@ -93,7 +98,7 @@ type AckFrame struct {
|
|||
}
|
||||
|
||||
// WriteAckFrame writes an ack frame.
|
||||
func WriteAckFrame(b *bytes.Buffer, f *AckFrame) {
|
||||
func (f *AckFrame) Write(b *bytes.Buffer) error {
|
||||
typeByte := uint8(0x48)
|
||||
b.WriteByte(typeByte)
|
||||
b.WriteByte(f.Entropy)
|
||||
|
@ -102,4 +107,5 @@ func WriteAckFrame(b *bytes.Buffer, f *AckFrame) {
|
|||
b.WriteByte(0x01) // Just one timestamp
|
||||
b.WriteByte(0x00) // Largest observed
|
||||
utils.WriteUint32(b, 0) // First timestamp
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -34,20 +34,20 @@ var _ = Describe("Frame", func() {
|
|||
Context("when writing", func() {
|
||||
It("writes sample frame", func() {
|
||||
b := &bytes.Buffer{}
|
||||
WriteStreamFrame(b, &StreamFrame{
|
||||
(&StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: []byte("foobar"),
|
||||
})
|
||||
}).Write(b)
|
||||
Expect(b.Bytes()).To(Equal([]byte{0xa3, 0x1, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}))
|
||||
})
|
||||
|
||||
It("writes offsets", func() {
|
||||
b := &bytes.Buffer{}
|
||||
WriteStreamFrame(b, &StreamFrame{
|
||||
(&StreamFrame{
|
||||
StreamID: 1,
|
||||
Offset: 16,
|
||||
Data: []byte("foobar"),
|
||||
})
|
||||
}).Write(b)
|
||||
Expect(b.Bytes()).To(Equal([]byte{0xbf, 0x1, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}))
|
||||
})
|
||||
})
|
||||
|
@ -57,10 +57,10 @@ var _ = Describe("Frame", func() {
|
|||
Context("when writing", func() {
|
||||
It("writes simple frames", func() {
|
||||
b := &bytes.Buffer{}
|
||||
WriteAckFrame(b, &AckFrame{
|
||||
(&AckFrame{
|
||||
Entropy: 2,
|
||||
LargestObserved: 1,
|
||||
})
|
||||
}).Write(b)
|
||||
Expect(b.Bytes()).To(Equal([]byte{0x48, 0x02, 0x01, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}))
|
||||
})
|
||||
})
|
||||
|
|
46
server_config.go
Normal file
46
server_config.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/crypto"
|
||||
)
|
||||
|
||||
// ServerConfig is a server config
|
||||
type ServerConfig struct {
|
||||
kex crypto.KeyExchange
|
||||
kd *crypto.KeyData
|
||||
}
|
||||
|
||||
// NewServerConfig creates a new server config
|
||||
func NewServerConfig(kex crypto.KeyExchange, kd *crypto.KeyData) *ServerConfig {
|
||||
return &ServerConfig{
|
||||
kex: kex,
|
||||
kd: kd,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the server config binary representation
|
||||
func (s *ServerConfig) Get() []byte {
|
||||
var serverConfig bytes.Buffer
|
||||
WriteCryptoMessage(&serverConfig, TagSCFG, map[Tag][]byte{
|
||||
TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF},
|
||||
TagKEXS: []byte("C255"),
|
||||
TagAEAD: []byte("AESG"),
|
||||
TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...),
|
||||
TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
|
||||
TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
TagVER: []byte("Q032"),
|
||||
})
|
||||
return serverConfig.Bytes()
|
||||
}
|
||||
|
||||
// Sign the server config and CHLO with the server's keyData
|
||||
func (s *ServerConfig) Sign(chlo []byte) ([]byte, error) {
|
||||
return s.kd.SignServerProof(chlo, s.Get())
|
||||
}
|
||||
|
||||
// GetCertData returns the certificate data
|
||||
func (s *ServerConfig) GetCertData() []byte {
|
||||
return s.kd.GetCERTdata()
|
||||
}
|
122
session.go
Normal file
122
session.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/crypto"
|
||||
)
|
||||
|
||||
// A Session is a QUIC session
|
||||
type Session struct {
|
||||
ConnectionID uint64
|
||||
ServerConfig *ServerConfig
|
||||
|
||||
Connection *net.UDPConn
|
||||
CurrentRemoteAddr *net.UDPAddr
|
||||
|
||||
Entropy EntropyAccumulator
|
||||
}
|
||||
|
||||
// NewSession makes a new session
|
||||
func NewSession(conn *net.UDPConn, connectionID uint64, sCfg *ServerConfig) *Session {
|
||||
return &Session{
|
||||
Connection: conn,
|
||||
ConnectionID: connectionID,
|
||||
ServerConfig: sCfg,
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePacket handles a packet
|
||||
func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, publicHeader *PublicHeader, r *bytes.Reader) error {
|
||||
// TODO: Only do this after authenticating
|
||||
if addr != s.CurrentRemoteAddr {
|
||||
s.CurrentRemoteAddr = addr
|
||||
}
|
||||
|
||||
nullAEAD := &crypto.NullAEAD{}
|
||||
r, err := nullAEAD.Open(0, publicHeaderBinary, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateFlag, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Entropy.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)
|
||||
|
||||
// TODO: Switch frame type here
|
||||
|
||||
frame, err := ParseStreamFrame(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Switch stream here
|
||||
if frame.StreamID != 1 {
|
||||
panic("streamid not 1")
|
||||
}
|
||||
|
||||
messageTag, cryptoData, err := ParseCryptoMessage(frame.Data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: Switch client messages here
|
||||
if messageTag != TagCHLO {
|
||||
panic("expected CHLO")
|
||||
}
|
||||
|
||||
if _, ok := cryptoData[TagPUBS]; ok {
|
||||
panic("received CHLO with PUBS")
|
||||
}
|
||||
|
||||
proof, err := s.ServerConfig.Sign(frame.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var serverReply bytes.Buffer
|
||||
WriteCryptoMessage(&serverReply, TagREJ, map[Tag][]byte{
|
||||
TagSCFG: s.ServerConfig.Get(),
|
||||
TagCERT: s.ServerConfig.GetCertData(),
|
||||
TagPROF: proof,
|
||||
})
|
||||
|
||||
s.SendFrames([]Frame{
|
||||
&AckFrame{
|
||||
Entropy: s.Entropy.Get(),
|
||||
LargestObserved: 1,
|
||||
},
|
||||
&StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: serverReply.Bytes(),
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendFrames sends a number of frames to the client
|
||||
func (s *Session) SendFrames(frames []Frame) error {
|
||||
var framesData bytes.Buffer
|
||||
framesData.WriteByte(0) // TODO: entropy
|
||||
for _, f := range frames {
|
||||
if err := f.Write(&framesData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var fullReply bytes.Buffer
|
||||
responsePublicHeader := PublicHeader{ConnectionID: s.ConnectionID, PacketNumber: 1}
|
||||
fmt.Printf("%#v\n", responsePublicHeader)
|
||||
if err := responsePublicHeader.WritePublicHeader(&fullReply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(&crypto.NullAEAD{}).Seal(0, &fullReply, fullReply.Bytes(), framesData.Bytes())
|
||||
|
||||
_, err := s.Connection.WriteToUDP(fullReply.Bytes(), s.CurrentRemoteAddr)
|
||||
return err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue