func (d *relayDialer) Dial(id protocol.DeviceID, uri *url.URL) (IntermediateConnection, error) { inv, err := client.GetInvitationFromRelay(uri, id, d.tlsCfg.Certificates, 10*time.Second) if err != nil { return IntermediateConnection{}, err } conn, err := client.JoinSession(inv) if err != nil { return IntermediateConnection{}, err } err = dialer.SetTCPOptions(conn) if err != nil { conn.Close() return IntermediateConnection{}, err } var tc *tls.Conn if inv.ServerSocket { tc = tls.Server(conn, d.tlsCfg) } else { tc = tls.Client(conn, d.tlsCfg) } err = tc.Handshake() if err != nil { tc.Close() return IntermediateConnection{}, err } return IntermediateConnection{tc, "Relay (Client)", relayPriority}, nil }
func (s *connectionSvc) connectViaRelay(deviceID protocol.DeviceID, addr discover.Relay) *tls.Conn { uri, err := url.Parse(addr.URL) if err != nil { l.Infoln("Failed to parse relay connection url:", addr, err) return nil } inv, err := client.GetInvitationFromRelay(uri, deviceID, s.tlsCfg.Certificates) if err != nil { l.Debugf("Failed to get invitation for %s from %s: %v", deviceID, uri, err) return nil } l.Debugln("Succesfully retrieved relay invitation", inv, "from", uri) conn, err := client.JoinSession(inv) if err != nil { l.Debugf("Failed to join relay session %s: %v", inv, err) return nil } l.Debugln("Successfully joined relay session", inv) var tc *tls.Conn if inv.ServerSocket { tc = tls.Server(conn, s.tlsCfg) } else { tc = tls.Client(conn, s.tlsCfg) } err = tc.Handshake() if err != nil { l.Infof("TLS handshake (BEP/relay %s): %v", inv, err) tc.Close() return nil } return tc }
func (d *relayDialer) Dial(id protocol.DeviceID, uri *url.URL) (internalConn, error) { inv, err := client.GetInvitationFromRelay(uri, id, d.tlsCfg.Certificates, 10*time.Second) if err != nil { return internalConn{}, err } conn, err := client.JoinSession(inv) if err != nil { return internalConn{}, err } err = dialer.SetTCPOptions(conn) if err != nil { conn.Close() return internalConn{}, err } err = dialer.SetTrafficClass(conn, d.cfg.Options().TrafficClass) if err != nil { l.Debugf("failed to set traffic class: %s", err) } var tc *tls.Conn if inv.ServerSocket { tc = tls.Server(conn, d.tlsCfg) } else { tc = tls.Client(conn, d.tlsCfg) } err = tlsTimedHandshake(tc) if err != nil { tc.Close() return internalConn{}, err } return internalConn{tc, connTypeRelayClient, relayPriority}, nil }
func (s *connectionSvc) connect() { delay := time.Second for { nextDevice: for deviceID, deviceCfg := range s.cfg.Devices() { if deviceID == myID { continue } if s.model.IsPaused(deviceID) { continue } connected := s.model.ConnectedTo(deviceID) s.mut.RLock() ct, ok := s.connType[deviceID] relaysEnabled := s.relaysEnabled s.mut.RUnlock() if connected && ok && ct.IsDirect() { continue } var addrs []string var relays []discover.Relay for _, addr := range deviceCfg.Addresses { if addr == "dynamic" { if s.discoverer != nil { if t, r, err := s.discoverer.Lookup(deviceID); err == nil { addrs = append(addrs, t...) relays = append(relays, r...) } } } else { addrs = append(addrs, addr) } } for _, addr := range addrs { uri, err := url.Parse(addr) if err != nil { l.Infoln("Failed to parse connection url:", addr, err) continue } dialer, ok := dialers[uri.Scheme] if !ok { l.Infoln("Unknown address schema", uri.String()) continue } if debugNet { l.Debugln("dial", deviceCfg.DeviceID, uri.String()) } conn, err := dialer(uri, s.tlsCfg) if err != nil { if debugNet { l.Debugln("dial failed", deviceCfg.DeviceID, uri.String(), err) } continue } if connected { s.model.Close(deviceID, fmt.Errorf("switching connections")) } s.conns <- model.IntermediateConnection{ conn, model.ConnectionTypeDirectDial, } continue nextDevice } // Only connect via relays if not already connected // Also, do not set lastRelayCheck time if we have no relays, // as otherwise when we do discover relays, we might have to // wait up to RelayReconnectIntervalM to connect again. // Also, do not try relays if we are explicitly told not to. if connected || len(relays) == 0 || !relaysEnabled { continue nextDevice } reconIntv := time.Duration(s.cfg.Options().RelayReconnectIntervalM) * time.Minute if last, ok := s.lastRelayCheck[deviceID]; ok && time.Since(last) < reconIntv { if debugNet { l.Debugln("Skipping connecting via relay to", deviceID, "last checked at", last) } continue nextDevice } else if debugNet { l.Debugln("Trying relay connections to", deviceID, relays) } s.lastRelayCheck[deviceID] = time.Now() for _, addr := range relays { uri, err := url.Parse(addr.URL) if err != nil { l.Infoln("Failed to parse relay connection url:", addr, err) continue } inv, err := client.GetInvitationFromRelay(uri, deviceID, s.tlsCfg.Certificates) if err != nil { if debugNet { l.Debugf("Failed to get invitation for %s from %s: %v", deviceID, uri, err) } continue } else if debugNet { l.Debugln("Succesfully retrieved relay invitation", inv, "from", uri) } conn, err := client.JoinSession(inv) if err != nil { if debugNet { l.Debugf("Failed to join relay session %s: %v", inv, err) } continue } else if debugNet { l.Debugln("Sucessfully joined relay session", inv) } err = osutil.SetTCPOptions(conn.(*net.TCPConn)) if err != nil { l.Infoln(err) } var tc *tls.Conn if inv.ServerSocket { tc = tls.Server(conn, s.tlsCfg) } else { tc = tls.Client(conn, s.tlsCfg) } err = tc.Handshake() if err != nil { l.Infof("TLS handshake (BEP/relay %s): %v", inv, err) tc.Close() continue } s.conns <- model.IntermediateConnection{ tc, model.ConnectionTypeRelayDial, } continue nextDevice } } time.Sleep(delay) delay *= 2 if maxD := time.Duration(s.cfg.Options().ReconnectIntervalS) * time.Second; delay > maxD { delay = maxD } } }
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") } }