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 { if debug { l.Debugln("Failed to parse relay address", addr, err) } continue } existing[uri.String()] = uri } // Expand dynamic addresses into a set of relays for key, uri := range existing { if uri.Scheme != "dynamic+http" && uri.Scheme != "dynamic+https" { continue } delete(existing, key) uri.Scheme = uri.Scheme[8:] data, err := http.Get(uri.String()) if err != nil { if debug { l.Debugln("Failed to lookup dynamic relays", err) } continue } var ann dynamicAnnouncement err = json.NewDecoder(data.Body).Decode(&ann) data.Body.Close() if err != nil { if debug { l.Debugln("Failed to lookup dynamic relays", err) } continue } for _, relayAnn := range ann.Relays { ruri, err := url.Parse(relayAnn.URL) if err != nil { if debug { l.Debugln("Failed to parse dynamic relay address", relayAnn.URL, err) } continue } if debug { l.Debugln("Found", ruri, "via", uri) } existing[ruri.String()] = ruri } } for key, uri := range existing { _, ok := s.tokens[key] if !ok { if debug { l.Debugln("Connecting to relay", uri) } c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations) s.tokens[key] = s.Add(c) s.mut.Lock() s.clients[key] = c s.mut.Unlock() } } for key, token := range s.tokens { _, ok := existing[key] if !ok { err := s.Remove(token) delete(s.tokens, key) s.mut.Lock() delete(s.clients, key) s.mut.Unlock() if debug { l.Debugln("Disconnecting from relay", key, err) } } } return true }
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 { if debug { l.Debugln("Failed to parse relay address", addr, err) } continue } existing[uri.String()] = uri } // Query dynamic addresses, and pick the closest relay from the ones they provide. for key, uri := range existing { if uri.Scheme != "dynamic+http" && uri.Scheme != "dynamic+https" { continue } delete(existing, key) // Trim off the `dynamic+` prefix uri.Scheme = uri.Scheme[8:] if debug { l.Debugln("Looking up dynamic relays from", uri) } data, err := http.Get(uri.String()) if err != nil { if debug { l.Debugln("Failed to lookup dynamic relays", err) } continue } var ann dynamicAnnouncement err = json.NewDecoder(data.Body).Decode(&ann) data.Body.Close() if err != nil { if debug { l.Debugln("Failed to lookup dynamic relays", err) } continue } dynRelays := make([]discover.Relay, 0, len(ann.Relays)) for _, relayAnn := range ann.Relays { ruri, err := url.Parse(relayAnn.URL) if err != nil { if debug { l.Debugln("Failed to parse dynamic relay address", relayAnn.URL, err) } continue } if debug { l.Debugln("Found", ruri, "via", uri) } dynRelays = append(dynRelays, discover.Relay{ Address: ruri.String(), }) } dynRelayAddrs := discover.RelayAddressesSortedByLatency(dynRelays) if len(dynRelayAddrs) > 0 { closestRelay := dynRelayAddrs[0] if debug { l.Debugln("Picking", closestRelay, "as closest dynamic relay from", uri) } ruri, _ := url.Parse(closestRelay) existing[closestRelay] = ruri } else if debug { l.Debugln("No dynamic relay found on", uri) } } s.mut.Lock() for key, uri := range existing { _, ok := s.tokens[key] if !ok { if debug { l.Debugln("Connecting to relay", uri) } c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations) 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) if debug { l.Debugln("Disconnecting from relay", key, err) } } } s.mut.Unlock() return true }
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 := client.NewProtocolClient(uri, []tls.Certificate{cert}, nil) 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}) 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}) { log.Println("OK") } else { log.Println("FAIL") } } else { log.Fatal("Requires either join or connect") } }