func (s *Svc) CommitConfiguration(from, to config.Configuration) bool { existing := make(map[string]*url.URL, len(to.Options.RelayServers)) for _, addr := range to.Options.RelayServers { uri, err := url.Parse(addr) if err != nil { l.Debugln("Failed to parse relay address", addr, err) continue } existing[uri.String()] = uri } s.mut.Lock() for key, uri := range existing { _, ok := s.tokens[key] if !ok { l.Debugln("Connecting to relay", uri) c, err := client.NewClient(uri, s.tlsCfg.Certificates, s.invitations, 10*time.Second) if err != nil { l.Infoln("Failed to connect to relay", uri, err) continue } s.tokens[key] = s.Add(c) s.clients[key] = c } } for key, token := range s.tokens { _, ok := existing[key] if !ok { err := s.Remove(token) delete(s.tokens, key) delete(s.clients, key) l.Debugln("Disconnecting from relay", key, err) } } s.mut.Unlock() return true }
func (t *relayListener) Serve() { t.mut.Lock() t.err = nil t.mut.Unlock() clnt, err := client.NewClient(t.uri, t.tlsCfg.Certificates, nil, 10*time.Second) if err != nil { t.mut.Lock() t.err = err t.mut.Unlock() l.Warnln("listen (BEP/relay):", err) return } go clnt.Serve() t.mut.Lock() t.client = clnt t.mut.Unlock() oldURI := clnt.URI() for { select { case inv, ok := <-t.client.Invitations(): if !ok { return } conn, err := client.JoinSession(inv) if err != nil { l.Warnln("Joining relay session (BEP/relay):", err) continue } err = dialer.SetTCPOptions(conn.(*net.TCPConn)) if err != nil { l.Infoln(err) } var tc *tls.Conn if inv.ServerSocket { tc = tls.Server(conn, t.tlsCfg) } else { tc = tls.Client(conn, t.tlsCfg) } err = tc.Handshake() if err != nil { tc.Close() l.Infoln("TLS handshake (BEP/relay):", err) continue } t.conns <- IntermediateConnection{tc, "Relay (Server)", relayPriority} // Poor mans notifier that informs the connection service that the // relay URI has changed. This can only happen when we connect to a // relay via dynamic+http(s) pool, which upon a relay failing/dropping // us, would pick a different one. case <-time.After(10 * time.Second): currentURI := clnt.URI() if currentURI != oldURI { oldURI = currentURI t.notifyAddressesChanged(t) } } } }
func main() { log.SetOutput(os.Stdout) log.SetFlags(log.LstdFlags | log.Lshortfile) var connect, relay, dir string var join, test bool flag.StringVar(&connect, "connect", "", "Device ID to which to connect to") flag.BoolVar(&join, "join", false, "Join relay") flag.BoolVar(&test, "test", false, "Generic relay test") flag.StringVar(&relay, "relay", "relay://127.0.0.1:22067", "Relay address") flag.StringVar(&dir, "keys", ".", "Directory where cert.pem and key.pem is stored") flag.Parse() certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem") cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { log.Fatalln("Failed to load X509 key pair:", err) } id := syncthingprotocol.NewDeviceID(cert.Certificate[0]) log.Println("ID:", id) uri, err := url.Parse(relay) if err != nil { log.Fatal(err) } stdin := make(chan string) go stdinReader(stdin) if join { log.Println("Creating client") relay, err := client.NewClient(uri, []tls.Certificate{cert}, nil, 10*time.Second) if err != nil { log.Fatal(err) } log.Println("Created client") go relay.Serve() recv := make(chan protocol.SessionInvitation) go func() { log.Println("Starting invitation receiver") for invite := range relay.Invitations() { select { case recv <- invite: log.Println("Received invitation", invite) default: log.Println("Discarding invitation", invite) } } }() for { conn, err := client.JoinSession(<-recv) if err != nil { log.Fatalln("Failed to join", err) } log.Println("Joined", conn.RemoteAddr(), conn.LocalAddr()) connectToStdio(stdin, conn) log.Println("Finished", conn.RemoteAddr(), conn.LocalAddr()) } } else if connect != "" { id, err := syncthingprotocol.DeviceIDFromString(connect) if err != nil { log.Fatal(err) } invite, err := client.GetInvitationFromRelay(uri, id, []tls.Certificate{cert}, 10*time.Second) if err != nil { log.Fatal(err) } log.Println("Received invitation", invite) conn, err := client.JoinSession(invite) if err != nil { log.Fatalln("Failed to join", err) } log.Println("Joined", conn.RemoteAddr(), conn.LocalAddr()) connectToStdio(stdin, conn) log.Println("Finished", conn.RemoteAddr(), conn.LocalAddr()) } else if test { if client.TestRelay(uri, []tls.Certificate{cert}, time.Second, 2*time.Second, 4) { log.Println("OK") } else { log.Println("FAIL") } } else { log.Fatal("Requires either join or connect") } }