From 9b541a28e6629d92b71eabd72bd6c49c1ab074f0 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 13 Sep 2015 11:44:33 +0200 Subject: [PATCH] New discovery protocol over HTTPS --- cmd/discosrv/main.go | 19 +- cmd/discosrv/psql.go | 6 +- cmd/discosrv/ql.go | 2 +- cmd/discosrv/querysrv.go | 401 ++++++++++++++++++++++++--------------- 4 files changed, 262 insertions(+), 166 deletions(-) diff --git a/cmd/discosrv/main.go b/cmd/discosrv/main.go index 91f16c10c..067f3f57f 100644 --- a/cmd/discosrv/main.go +++ b/cmd/discosrv/main.go @@ -3,10 +3,10 @@ package main import ( + "crypto/tls" "database/sql" "flag" "log" - "net" "os" "time" @@ -21,6 +21,9 @@ var ( statsFile string backend = "ql" dsn = getEnvDefault("DISCOSRV_DB_DSN", "memory://discosrv") + certFile = "cert.pem" + keyFile = "key.pem" + debug = false ) func main() { @@ -34,18 +37,23 @@ func main() { log.SetOutput(os.Stdout) log.SetFlags(0) - flag.StringVar(&listen, "listen", ":22027", "Listen address") + flag.StringVar(&listen, "listen", ":8443", "Listen address") flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries") flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s") flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets") flag.StringVar(&statsFile, "stats-file", statsFile, "File to write periodic operation stats to") flag.StringVar(&backend, "db-backend", backend, "Database backend to use") flag.StringVar(&dsn, "db-dsn", dsn, "Database DSN") + flag.StringVar(&certFile, "cert", certFile, "Certificate file") + flag.StringVar(&keyFile, "key", keyFile, "Key file") + flag.BoolVar(&debug, "debug", debug, "Debug") flag.Parse() - addr, _ := net.ResolveUDPAddr("udp", listen) + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + log.Fatalln("Failed to load X509 key pair:", err) + } - var err error db, err := sql.Open(backend, dsn) if err != nil { log.Fatalln("sql.Open:", err) @@ -58,7 +66,8 @@ func main() { main := suture.NewSimple("main") main.Add(&querysrv{ - addr: addr, + addr: listen, + cert: cert, db: db, prep: prep, }) diff --git a/cmd/discosrv/psql.go b/cmd/discosrv/psql.go index 068547812..2f76f48f0 100644 --- a/cmd/discosrv/psql.go +++ b/cmd/discosrv/psql.go @@ -104,9 +104,9 @@ func postgresCompile(db *sql.DB) (map[string]*sql.Stmt, error) { "insertAddress": "INSERT INTO Addresses (DeviceID, Seen, Address) VALUES ($1, now(), $2)", "insertRelay": "INSERT INTO Relays (DeviceID, Seen, Address, Latency) VALUES ($1, now(), $2, $3)", "insertDevice": "INSERT INTO Devices (DeviceID, Seen) VALUES ($1, now())", - "selectAddress": "SELECT Address from Addresses WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", - "selectRelay": "SELECT Address, Latency from Relays WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", - "updateRelay": "UPDATE Relays SET Seen=now(), Latency=$3 WHERE DeviceID=$1 AND Address=$2", + "selectAddress": "SELECT Address FROM Addresses WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", + "selectRelay": "SELECT Address, Latency FROM Relays WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", + "updateAddress": "UPDATE Addresses SET Seen=now() WHERE DeviceID=$1 AND Address=$2", "updateDevice": "UPDATE Devices SET Seen=now() WHERE DeviceID=$1", "deleteRelay": "DELETE FROM Relays WHERE DeviceID=$1", } diff --git a/cmd/discosrv/ql.go b/cmd/discosrv/ql.go index 850eac74f..3a0559df0 100644 --- a/cmd/discosrv/ql.go +++ b/cmd/discosrv/ql.go @@ -80,7 +80,7 @@ func qlCompile(db *sql.DB) (map[string]*sql.Stmt, error) { "insertDevice": "INSERT INTO Devices (DeviceID, Seen) VALUES ($1, now())", "selectAddress": `SELECT Address from Addresses WHERE DeviceID==$1 AND Seen > now() - duration("1h") LIMIT 16`, "selectRelay": `SELECT Address, Latency from Relays WHERE DeviceID==$1 AND Seen > now() - duration("1h") LIMIT 16`, - "updateAddress": "UPDATE Addresses Seen=now()WHERE DeviceID==$1 AND Address==$2", + "updateAddress": "UPDATE Addresses Seen=now() WHERE DeviceID==$1 AND Address==$2", "updateDevice": "UPDATE Devices Seen=now() WHERE DeviceID==$1", "deleteRelay": "DELETE FROM Relays WHERE DeviceID==$1", } diff --git a/cmd/discosrv/querysrv.go b/cmd/discosrv/querysrv.go index 9af15ef69..57cdba00d 100644 --- a/cmd/discosrv/querysrv.go +++ b/cmd/discosrv/querysrv.go @@ -3,213 +3,305 @@ package main import ( + "crypto/tls" "database/sql" - "encoding/binary" - "fmt" - "io" + "encoding/json" "log" "net" + "net/http" "net/url" "time" "github.com/golang/groupcache/lru" "github.com/juju/ratelimit" "github.com/syncthing/protocol" - "github.com/syncthing/syncthing/lib/discover" ) type querysrv struct { - addr *net.UDPAddr - db *sql.DB - prep map[string]*sql.Stmt - limiter *lru.Cache + addr string + db *sql.DB + prep map[string]*sql.Stmt + limiter *lru.Cache + cert tls.Certificate + listener net.Listener +} + +type announcement struct { + Direct []string `json:"direct"` + Relays []annRelay `json:"relays"` +} + +type annRelay struct { + URL string `json:"url"` + Latency int `json:"latency"` } func (s *querysrv) Serve() { s.limiter = lru.New(lruSize) - conn, err := net.ListenUDP("udp", s.addr) + tlsCfg := &tls.Config{ + Certificates: []tls.Certificate{s.cert}, + ClientAuth: tls.RequestClientCert, + SessionTicketsDisabled: 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, + }, + } + + http.HandleFunc("/", s.handler) + + tlsListener, err := tls.Listen("tcp", s.addr, tlsCfg) if err != nil { log.Println("Listen:", err) return } - // Attempt to set the read and write buffers to 2^24 bytes (16 MB) or as high as - // possible. - for i := 24; i >= 16; i-- { - if conn.SetReadBuffer(1<= 16; i-- { - if conn.SetWriteBuffer(1< 0 { - ann := discover.Announce{ - Magic: discover.AnnouncementMagic, - This: discover.Device{ - ID: pkt.DeviceID, - Addresses: addrs, - Relays: relays, - }, - } - - tb, err := ann.MarshalXDR() - if err != nil { - return fmt.Errorf("QueryV2 response marshal: %v", err) - } - _, err = conn.WriteToUDP(tb, addr) - if err != nil { - return fmt.Errorf("QueryV2 response write: %v", err) - } - - globalStats.Answer() - } - - return nil -} - -func (s *querysrv) limit(addr *net.UDPAddr) bool { - key := addr.IP.String() +func (s *querysrv) limit(remote net.IP) bool { + key := remote.String() bkt, ok := s.limiter.Get(key) if ok { @@ -279,26 +371,21 @@ func (s *querysrv) getAddresses(device protocol.DeviceID) ([]string, error) { return res, nil } -func (s *querysrv) getRelays(device protocol.DeviceID) ([]discover.Relay, error) { +func (s *querysrv) getRelays(device protocol.DeviceID) ([]annRelay, error) { rows, err := s.prep["selectRelay"].Query(device.String()) if err != nil { return nil, err } - var res []discover.Relay + var res []annRelay for rows.Next() { - var addr string - var latency int32 + var rel annRelay - err := rows.Scan(&addr, &latency) + err := rows.Scan(&rel.URL, &rel.Latency) if err != nil { - log.Println("Scan:", err) - continue + return nil, err } - res = append(res, discover.Relay{ - Address: addr, - Latency: latency, - }) + res = append(res, rel) } return res, nil