Browse Source

Add dependencies (fixes #1364)

Audrius Butkevicius 10 years ago
parent
commit
2c0f8dc546

+ 8 - 0
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"

+ 280 - 0
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
+	}
+}

+ 15 - 0
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
+)

+ 117 - 0
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,
+		},
+	}
+}

+ 65 - 0
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)
+}

+ 567 - 0
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()
+}

+ 114 - 0
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")
+}