From afa71d52f1bfa54a90583e1337b7182f68e6010d Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 7 Nov 2016 17:51:39 +0700 Subject: [PATCH] create Client in main package --- client.go | 102 +++++++++++++++++++++++++++++++++++++++ client_test.go | 29 +++++++++++ example/client/client.go | 81 +++---------------------------- 3 files changed, 137 insertions(+), 75 deletions(-) create mode 100644 client.go create mode 100644 client_test.go diff --git a/client.go b/client.go new file mode 100644 index 00000000..a4cffce4 --- /dev/null +++ b/client.go @@ -0,0 +1,102 @@ +package quic + +import ( + "bytes" + "math/rand" + "net" + "time" + + "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/qerr" + "github.com/lucas-clemente/quic-go/utils" +) + +// A Client of QUIC +type Client struct { + addr *net.UDPAddr + conn *net.UDPConn + + connectionID protocol.ConnectionID + version protocol.VersionNumber + + session *Session +} + +// NewClient makes a new client +func NewClient(addr *net.UDPAddr) (*Client, error) { + conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + return nil, err + } + + // TODO: generate cryptographically secure random ConnectionID + rand.Seed(time.Now().UTC().UnixNano()) + connectionID := protocol.ConnectionID(rand.Int63()) + + client := &Client{ + addr: addr, + conn: conn, + version: protocol.Version36, + connectionID: connectionID, + } + + streamCallback := func(session *Session, stream utils.Stream) {} + + client.session, err = newClientSession(conn, addr, client.version, client.connectionID, streamCallback, client.closeCallback) + if err != nil { + return nil, err + } + + return client, nil +} + +// Listen listens +func (c *Client) Listen() { + go c.session.run() + + for { + data := getPacketBuffer() + data = data[:protocol.MaxPacketSize] + + n, _, err := c.conn.ReadFromUDP(data) + utils.Debugf("%d", n) + if err != nil { + panic(err) + } + data = data[:n] + + err = c.handlePacket(data) + if err != nil { + utils.Errorf("error handling packet: %s", err.Error()) + } + } +} + +func (c *Client) handlePacket(packet []byte) error { + if protocol.ByteCount(len(packet)) > protocol.MaxPacketSize { + return qerr.PacketTooLarge + } + + rcvTime := time.Now() + + r := bytes.NewReader(packet) + + hdr, err := ParsePublicHeader(r) + if err != nil { + return qerr.Error(qerr.InvalidPacketHeader, err.Error()) + } + hdr.Raw = packet[:len(packet)-r.Len()] + + c.session.handlePacket(&receivedPacket{ + remoteAddr: c.addr, + publicHeader: hdr, + data: packet[len(packet)-r.Len():], + rcvTime: rcvTime, + }) + return nil +} + +func (c *Client) closeCallback(id protocol.ConnectionID) { + utils.Infof("Connection %x closed.", id) + c.conn.Close() +} diff --git a/client_test.go b/client_test.go new file mode 100644 index 00000000..bff1a809 --- /dev/null +++ b/client_test.go @@ -0,0 +1,29 @@ +package quic + +import ( + "bytes" + + "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/qerr" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Client", func() { + var client *Client + + BeforeEach(func() { + client = &Client{} + }) + + It("errors on invalid public header", func() { + err := client.handlePacket(nil) + Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(qerr.InvalidPacketHeader)) + }) + + It("errors on large packets", func() { + err := client.handlePacket(bytes.Repeat([]byte{'a'}, int(protocol.MaxPacketSize)+1)) + Expect(err).To(MatchError(qerr.PacketTooLarge)) + }) +}) diff --git a/example/client/client.go b/example/client/client.go index 126f5b63..4a466b83 100644 --- a/example/client/client.go +++ b/example/client/client.go @@ -1,95 +1,26 @@ package main import ( - "bytes" - "fmt" - "math/rand" "net" - "time" quic "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/crypto" - "github.com/lucas-clemente/quic-go/frames" - "github.com/lucas-clemente/quic-go/handshake" - "github.com/lucas-clemente/quic-go/protocol" + "github.com/lucas-clemente/quic-go/utils" ) func main() { addr := "quic.clemente.io:6121" - conn, err := connect(addr) - defer conn.Close() - if err != nil { - panic(err) - } + utils.SetLogLevel(utils.LogLevelDebug) - rand.Seed(time.Now().UTC().UnixNano()) - connectionID := protocol.ConnectionID(0x1337 + rand.Int63()) - packetNumber := protocol.PacketNumber(1) - version := protocol.Version34 - - ph := quic.PublicHeader{ - ConnectionID: connectionID, - PacketNumber: packetNumber, - PacketNumberLen: protocol.PacketNumberLen6, - VersionFlag: true, - VersionNumber: version, - } - - raw := make([]byte, 0, protocol.MaxPacketSize) - buffer := bytes.NewBuffer(raw) - - err = ph.Write(buffer, protocol.Version34, protocol.PerspectiveClient) - if err != nil { - panic(err) - } - payloadStartIndex := buffer.Len() - - b := &bytes.Buffer{} - - tags := make(map[handshake.Tag][]byte) - tags[handshake.TagSNI] = []byte("quic.clemente.io") - tags[handshake.TagPDMD] = []byte("X509") - tags[handshake.TagPAD] = bytes.Repeat([]byte("F"), 1000) - handshake.WriteHandshakeMessage(b, handshake.TagCHLO, tags) - - frame := frames.StreamFrame{ - StreamID: 1, - DataLenPresent: true, - Data: b.Bytes(), - } - - frame.Write(buffer, version) - - raw = raw[0:buffer.Len()] - aead := crypto.NullAEAD{} - aead.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], packetNumber, raw[:payloadStartIndex]) - raw = raw[0 : buffer.Len()+12] - - conn.Write(raw) - - for { - data := make([]byte, 1500) - n, _, err := conn.ReadFromUDP(data) - if err != nil { - panic(err) - } - data = data[:n] - fmt.Printf("Response length: %d\n", n) - fmt.Println(data) - } -} - -func connect(addr string) (*net.UDPConn, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { - return nil, err + panic(err) } - conn, err := net.DialUDP("udp", nil, udpAddr) + client, err := quic.NewClient(udpAddr) if err != nil { - return nil, err + panic(err) } - return conn, nil + client.Listen() }