From 2c0f8dc546e92892db53e5568a0424955286ce9d Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Fri, 17 Jul 2015 20:23:00 +0100 Subject: [PATCH] Add dependencies (fixes #1364) --- Godeps/Godeps.json | 8 + .../syncthing/relaysrv/client/client.go | 280 +++++++++ .../syncthing/relaysrv/client/debug.go | 15 + .../syncthing/relaysrv/client/methods.go | 117 ++++ .../syncthing/relaysrv/protocol/packets.go | 65 ++ .../relaysrv/protocol/packets_xdr.go | 567 ++++++++++++++++++ .../syncthing/relaysrv/protocol/protocol.go | 114 ++++ 7 files changed, 1166 insertions(+) create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 65aa24ad..b22318ee 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -41,6 +41,14 @@ "ImportPath": "github.com/syncthing/protocol", "Rev": "ebcdea63c07327a342f65415bbadc497462b8f1f" }, + { + "ImportPath": "github.com/syncthing/relaysrv/client", + "Rev": "7c6a31017968e7c1a69148db1ca3dea71eba8236" + }, + { + "ImportPath": "github.com/syncthing/relaysrv/protocol", + "Rev": "7c6a31017968e7c1a69148db1ca3dea71eba8236" + }, { "ImportPath": "github.com/syndtr/goleveldb/leveldb", "Rev": "b743d92d3215f11c9b5ce8830fafe1f16786adf4" diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go new file mode 100644 index 00000000..94e4eedd --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go @@ -0,0 +1,280 @@ +// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file). + +package client + +import ( + "crypto/tls" + "fmt" + "log" + "net" + "net/url" + "time" + + syncthingprotocol "github.com/syncthing/protocol" + "github.com/syncthing/relaysrv/protocol" + "github.com/syncthing/syncthing/lib/sync" +) + +type ProtocolClient struct { + URI *url.URL + Invitations chan protocol.SessionInvitation + + closeInvitationsOnFinish bool + + config *tls.Config + + timeout time.Duration + + stop chan struct{} + stopped chan struct{} + + conn *tls.Conn + + mut sync.RWMutex + connected bool +} + +func NewProtocolClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation) *ProtocolClient { + closeInvitationsOnFinish := false + if invitations == nil { + closeInvitationsOnFinish = true + invitations = make(chan protocol.SessionInvitation) + } + + return &ProtocolClient{ + URI: uri, + Invitations: invitations, + + closeInvitationsOnFinish: closeInvitationsOnFinish, + + config: configForCerts(certs), + + timeout: time.Minute * 2, + + stop: make(chan struct{}), + stopped: make(chan struct{}), + + mut: sync.NewRWMutex(), + connected: false, + } +} + +func (c *ProtocolClient) Serve() { + c.stop = make(chan struct{}) + c.stopped = make(chan struct{}) + defer close(c.stopped) + + if err := c.connect(); err != nil { + if debug { + l.Debugln("Relay connect:", err) + } + return + } + + if debug { + l.Debugln(c, "connected", c.conn.RemoteAddr()) + } + + if err := c.join(); err != nil { + c.conn.Close() + l.Infoln("Relay join:", err) + return + } + + if err := c.conn.SetDeadline(time.Time{}); err != nil { + l.Infoln("Relay set deadline:", err) + return + } + + if debug { + l.Debugln(c, "joined", c.conn.RemoteAddr(), "via", c.conn.LocalAddr()) + } + + defer c.cleanup() + c.mut.Lock() + c.connected = true + c.mut.Unlock() + + messages := make(chan interface{}) + errors := make(chan error, 1) + + go messageReader(c.conn, messages, errors) + + timeout := time.NewTimer(c.timeout) + + for { + select { + case message := <-messages: + timeout.Reset(c.timeout) + if debug { + log.Printf("%s received message %T", c, message) + } + + switch msg := message.(type) { + case protocol.Ping: + if err := protocol.WriteMessage(c.conn, protocol.Pong{}); err != nil { + l.Infoln("Relay write:", err) + return + + } + if debug { + l.Debugln(c, "sent pong") + } + + case protocol.SessionInvitation: + ip := net.IP(msg.Address) + if len(ip) == 0 || ip.IsUnspecified() { + msg.Address = c.conn.RemoteAddr().(*net.TCPAddr).IP[:] + } + c.Invitations <- msg + + default: + l.Infoln("Relay: protocol error: unexpected message %v", msg) + return + } + + case <-c.stop: + if debug { + l.Debugln(c, "stopping") + } + return + + case err := <-errors: + l.Infoln("Relay received:", err) + return + + case <-timeout.C: + if debug { + l.Debugln(c, "timed out") + } + return + } + } +} + +func (c *ProtocolClient) Stop() { + if c.stop == nil { + return + } + + close(c.stop) + <-c.stopped +} + +func (c *ProtocolClient) StatusOK() bool { + c.mut.RLock() + con := c.connected + c.mut.RUnlock() + return con +} + +func (c *ProtocolClient) String() string { + return fmt.Sprintf("ProtocolClient@%p", c) +} + +func (c *ProtocolClient) connect() error { + if c.URI.Scheme != "relay" { + return fmt.Errorf("Unsupported relay schema:", c.URI.Scheme) + } + + conn, err := tls.Dial("tcp", c.URI.Host, c.config) + if err != nil { + return err + } + + if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil { + conn.Close() + return err + } + + if err := performHandshakeAndValidation(conn, c.URI); err != nil { + conn.Close() + return err + } + + c.conn = conn + return nil +} + +func (c *ProtocolClient) cleanup() { + if c.closeInvitationsOnFinish { + close(c.Invitations) + c.Invitations = make(chan protocol.SessionInvitation) + } + + if debug { + l.Debugln(c, "cleaning up") + } + + c.mut.Lock() + c.connected = false + c.mut.Unlock() + + c.conn.Close() +} + +func (c *ProtocolClient) join() error { + if err := protocol.WriteMessage(c.conn, protocol.JoinRelayRequest{}); err != nil { + return err + } + + message, err := protocol.ReadMessage(c.conn) + if err != nil { + return err + } + + switch msg := message.(type) { + case protocol.Response: + if msg.Code != 0 { + return fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) + } + + default: + return fmt.Errorf("protocol error: expecting response got %v", msg) + } + + return nil +} + +func performHandshakeAndValidation(conn *tls.Conn, uri *url.URL) error { + if err := conn.Handshake(); err != nil { + return err + } + + cs := conn.ConnectionState() + if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != protocol.ProtocolName { + return fmt.Errorf("protocol negotiation error") + } + + q := uri.Query() + relayIDs := q.Get("id") + if relayIDs != "" { + relayID, err := syncthingprotocol.DeviceIDFromString(relayIDs) + if err != nil { + return fmt.Errorf("relay address contains invalid verification id: %s", err) + } + + certs := cs.PeerCertificates + if cl := len(certs); cl != 1 { + return fmt.Errorf("unexpected certificate count: %d", cl) + } + + remoteID := syncthingprotocol.NewDeviceID(certs[0].Raw) + if remoteID != relayID { + return fmt.Errorf("relay id does not match. Expected %v got %v", relayID, remoteID) + } + } + + return nil +} + +func messageReader(conn net.Conn, messages chan<- interface{}, errors chan<- error) { + for { + msg, err := protocol.ReadMessage(conn) + if err != nil { + errors <- err + return + } + messages <- msg + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go new file mode 100644 index 00000000..935e9fe6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go @@ -0,0 +1,15 @@ +// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file). + +package client + +import ( + "os" + "strings" + + "github.com/calmh/logger" +) + +var ( + debug = strings.Contains(os.Getenv("STTRACE"), "relay") || os.Getenv("STTRACE") == "all" + l = logger.DefaultLogger +) diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go new file mode 100644 index 00000000..ef6145e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go @@ -0,0 +1,117 @@ +// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file). + +package client + +import ( + "crypto/tls" + "fmt" + "net" + "net/url" + "strconv" + "time" + + syncthingprotocol "github.com/syncthing/protocol" + "github.com/syncthing/relaysrv/protocol" +) + +func GetInvitationFromRelay(uri *url.URL, id syncthingprotocol.DeviceID, certs []tls.Certificate) (protocol.SessionInvitation, error) { + if uri.Scheme != "relay" { + return protocol.SessionInvitation{}, fmt.Errorf("Unsupported relay scheme:", uri.Scheme) + } + + conn, err := tls.Dial("tcp", uri.Host, configForCerts(certs)) + conn.SetDeadline(time.Now().Add(10 * time.Second)) + if err != nil { + return protocol.SessionInvitation{}, err + } + + if err := performHandshakeAndValidation(conn, uri); err != nil { + return protocol.SessionInvitation{}, err + } + + defer conn.Close() + + request := protocol.ConnectRequest{ + ID: id[:], + } + + if err := protocol.WriteMessage(conn, request); err != nil { + return protocol.SessionInvitation{}, err + } + + message, err := protocol.ReadMessage(conn) + if err != nil { + return protocol.SessionInvitation{}, err + } + + switch msg := message.(type) { + case protocol.Response: + return protocol.SessionInvitation{}, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) + case protocol.SessionInvitation: + if debug { + l.Debugln("Received invitation", msg, "via", conn.LocalAddr()) + } + ip := net.IP(msg.Address) + if len(ip) == 0 || ip.IsUnspecified() { + msg.Address = conn.RemoteAddr().(*net.TCPAddr).IP[:] + } + return msg, nil + default: + return protocol.SessionInvitation{}, fmt.Errorf("protocol error: unexpected message %v", msg) + } +} + +func JoinSession(invitation protocol.SessionInvitation) (net.Conn, error) { + addr := net.JoinHostPort(net.IP(invitation.Address).String(), strconv.Itoa(int(invitation.Port))) + + conn, err := net.Dial("tcp", addr) + if err != nil { + return nil, err + } + + request := protocol.JoinSessionRequest{ + Key: invitation.Key, + } + + conn.SetDeadline(time.Now().Add(10 * time.Second)) + err = protocol.WriteMessage(conn, request) + if err != nil { + return nil, err + } + + message, err := protocol.ReadMessage(conn) + if err != nil { + return nil, err + } + + conn.SetDeadline(time.Time{}) + + switch msg := message.(type) { + case protocol.Response: + if msg.Code != 0 { + return nil, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) + } + return conn, nil + default: + return nil, fmt.Errorf("protocol error: expecting response got %v", msg) + } +} + +func configForCerts(certs []tls.Certificate) *tls.Config { + return &tls.Config{ + Certificates: certs, + NextProtos: []string{protocol.ProtocolName}, + ClientAuth: tls.RequestClientCert, + SessionTicketsDisabled: true, + InsecureSkipVerify: true, + MinVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + }, + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go new file mode 100644 index 00000000..7ff02011 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go @@ -0,0 +1,65 @@ +// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file). + +//go:generate -command genxdr go run ../../syncthing/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go +//go:generate genxdr -o packets_xdr.go packets.go + +package protocol + +import ( + "fmt" + syncthingprotocol "github.com/syncthing/protocol" + "net" +) + +const ( + messageTypePing int32 = iota + messageTypePong + messageTypeJoinRelayRequest + messageTypeJoinSessionRequest + messageTypeResponse + messageTypeConnectRequest + messageTypeSessionInvitation +) + +type header struct { + magic uint32 + messageType int32 + messageLength int32 +} + +type Ping struct{} +type Pong struct{} +type JoinRelayRequest struct{} + +type JoinSessionRequest struct { + Key []byte // max:32 +} + +type Response struct { + Code int32 + Message string +} + +type ConnectRequest struct { + ID []byte // max:32 +} + +type SessionInvitation struct { + From []byte // max:32 + Key []byte // max:32 + Address []byte // max:32 + Port uint16 + ServerSocket bool +} + +func (i SessionInvitation) String() string { + return fmt.Sprintf("%s@%s", syncthingprotocol.DeviceIDFromBytes(i.From), i.AddressString()) +} + +func (i SessionInvitation) GoString() string { + return i.String() +} + +func (i SessionInvitation) AddressString() string { + return fmt.Sprintf("%s:%d", net.IP(i.Address), i.Port) +} diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go new file mode 100644 index 00000000..f18e18c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go @@ -0,0 +1,567 @@ +// ************************************************************ +// This file is automatically generated by genxdr. Do not edit. +// ************************************************************ + +package protocol + +import ( + "bytes" + "io" + + "github.com/calmh/xdr" +) + +/* + +header Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| magic | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| message Type | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| message Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct header { + unsigned int magic; + int messageType; + int messageLength; +} + +*/ + +func (o header) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o header) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o header) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o header) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o header) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteUint32(o.magic) + xw.WriteUint32(uint32(o.messageType)) + xw.WriteUint32(uint32(o.messageLength)) + return xw.Tot(), xw.Error() +} + +func (o *header) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *header) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *header) DecodeXDRFrom(xr *xdr.Reader) error { + o.magic = xr.ReadUint32() + o.messageType = int32(xr.ReadUint32()) + o.messageLength = int32(xr.ReadUint32()) + return xr.Error() +} + +/* + +Ping Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Ping { +} + +*/ + +func (o Ping) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Ping) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Ping) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Ping) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Ping) EncodeXDRInto(xw *xdr.Writer) (int, error) { + return xw.Tot(), xw.Error() +} + +func (o *Ping) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Ping) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Ping) DecodeXDRFrom(xr *xdr.Reader) error { + return xr.Error() +} + +/* + +Pong Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Pong { +} + +*/ + +func (o Pong) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Pong) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Pong) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Pong) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Pong) EncodeXDRInto(xw *xdr.Writer) (int, error) { + return xw.Tot(), xw.Error() +} + +func (o *Pong) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Pong) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Pong) DecodeXDRFrom(xr *xdr.Reader) error { + return xr.Error() +} + +/* + +JoinRelayRequest Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct JoinRelayRequest { +} + +*/ + +func (o JoinRelayRequest) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o JoinRelayRequest) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o JoinRelayRequest) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o JoinRelayRequest) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o JoinRelayRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) { + return xw.Tot(), xw.Error() +} + +func (o *JoinRelayRequest) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *JoinRelayRequest) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *JoinRelayRequest) DecodeXDRFrom(xr *xdr.Reader) error { + return xr.Error() +} + +/* + +JoinSessionRequest Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Key | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Key (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct JoinSessionRequest { + opaque Key<32>; +} + +*/ + +func (o JoinSessionRequest) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o JoinSessionRequest) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o JoinSessionRequest) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o JoinSessionRequest) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o JoinSessionRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.Key); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 32) + } + xw.WriteBytes(o.Key) + return xw.Tot(), xw.Error() +} + +func (o *JoinSessionRequest) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *JoinSessionRequest) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *JoinSessionRequest) DecodeXDRFrom(xr *xdr.Reader) error { + o.Key = xr.ReadBytesMax(32) + return xr.Error() +} + +/* + +Response Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Code | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Message | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Message (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Response { + int Code; + string Message<>; +} + +*/ + +func (o Response) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Response) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Response) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Response) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Response) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteUint32(uint32(o.Code)) + xw.WriteString(o.Message) + return xw.Tot(), xw.Error() +} + +func (o *Response) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Response) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Response) DecodeXDRFrom(xr *xdr.Reader) error { + o.Code = int32(xr.ReadUint32()) + o.Message = xr.ReadString() + return xr.Error() +} + +/* + +ConnectRequest Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ ID (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct ConnectRequest { + opaque ID<32>; +} + +*/ + +func (o ConnectRequest) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o ConnectRequest) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o ConnectRequest) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o ConnectRequest) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o ConnectRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.ID); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32) + } + xw.WriteBytes(o.ID) + return xw.Tot(), xw.Error() +} + +func (o *ConnectRequest) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *ConnectRequest) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *ConnectRequest) DecodeXDRFrom(xr *xdr.Reader) error { + o.ID = xr.ReadBytesMax(32) + return xr.Error() +} + +/* + +SessionInvitation Structure: + + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of From | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ From (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Key | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Key (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Address (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 0x0000 | Port | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Server Socket (V=0 or 1) |V| ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct SessionInvitation { + opaque From<32>; + opaque Key<32>; + opaque Address<32>; + unsigned int Port; + bool ServerSocket; +} + +*/ + +func (o SessionInvitation) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o SessionInvitation) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o SessionInvitation) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o SessionInvitation) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o SessionInvitation) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.From); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("From", l, 32) + } + xw.WriteBytes(o.From) + if l := len(o.Key); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 32) + } + xw.WriteBytes(o.Key) + if l := len(o.Address); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("Address", l, 32) + } + xw.WriteBytes(o.Address) + xw.WriteUint16(o.Port) + xw.WriteBool(o.ServerSocket) + return xw.Tot(), xw.Error() +} + +func (o *SessionInvitation) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *SessionInvitation) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *SessionInvitation) DecodeXDRFrom(xr *xdr.Reader) error { + o.From = xr.ReadBytesMax(32) + o.Key = xr.ReadBytesMax(32) + o.Address = xr.ReadBytesMax(32) + o.Port = xr.ReadUint16() + o.ServerSocket = xr.ReadBool() + return xr.Error() +} diff --git a/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go new file mode 100644 index 00000000..57a967ac --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go @@ -0,0 +1,114 @@ +// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file). + +package protocol + +import ( + "fmt" + "io" +) + +const ( + magic = 0x9E79BC40 + ProtocolName = "bep-relay" +) + +var ( + ResponseSuccess = Response{0, "success"} + ResponseNotFound = Response{1, "not found"} + ResponseAlreadyConnected = Response{2, "already connected"} + ResponseInternalError = Response{99, "internal error"} + ResponseUnexpectedMessage = Response{100, "unexpected message"} +) + +func WriteMessage(w io.Writer, message interface{}) error { + header := header{ + magic: magic, + } + + var payload []byte + var err error + + switch msg := message.(type) { + case Ping: + payload, err = msg.MarshalXDR() + header.messageType = messageTypePing + case Pong: + payload, err = msg.MarshalXDR() + header.messageType = messageTypePong + case JoinRelayRequest: + payload, err = msg.MarshalXDR() + header.messageType = messageTypeJoinRelayRequest + case JoinSessionRequest: + payload, err = msg.MarshalXDR() + header.messageType = messageTypeJoinSessionRequest + case Response: + payload, err = msg.MarshalXDR() + header.messageType = messageTypeResponse + case ConnectRequest: + payload, err = msg.MarshalXDR() + header.messageType = messageTypeConnectRequest + case SessionInvitation: + payload, err = msg.MarshalXDR() + header.messageType = messageTypeSessionInvitation + default: + err = fmt.Errorf("Unknown message type") + } + + if err != nil { + return err + } + + header.messageLength = int32(len(payload)) + + headerpayload, err := header.MarshalXDR() + if err != nil { + return err + } + + _, err = w.Write(append(headerpayload, payload...)) + return err +} + +func ReadMessage(r io.Reader) (interface{}, error) { + var header header + if err := header.DecodeXDR(r); err != nil { + return nil, err + } + + if header.magic != magic { + return nil, fmt.Errorf("magic mismatch") + } + + switch header.messageType { + case messageTypePing: + var msg Ping + err := msg.DecodeXDR(r) + return msg, err + case messageTypePong: + var msg Pong + err := msg.DecodeXDR(r) + return msg, err + case messageTypeJoinRelayRequest: + var msg JoinRelayRequest + err := msg.DecodeXDR(r) + return msg, err + case messageTypeJoinSessionRequest: + var msg JoinSessionRequest + err := msg.DecodeXDR(r) + return msg, err + case messageTypeResponse: + var msg Response + err := msg.DecodeXDR(r) + return msg, err + case messageTypeConnectRequest: + var msg ConnectRequest + err := msg.DecodeXDR(r) + return msg, err + case messageTypeSessionInvitation: + var msg SessionInvitation + err := msg.DecodeXDR(r) + return msg, err + } + + return nil, fmt.Errorf("Unknown message type") +}