// Close removes the peer from the model and closes the underlying connection if possible. // Implements the protocol.Model interface. func (m *Model) Close(device protocol.DeviceID, err error) { l.Infof("Connection to %s closed: %v", device, err) events.Default.Log(events.DeviceDisconnected, map[string]string{ "id": device.String(), "error": err.Error(), }) m.pmut.Lock() m.fmut.RLock() for _, folder := range m.deviceFolders[device] { m.folderFiles[folder].Replace(device, nil) } m.fmut.RUnlock() conn, ok := m.rawConn[device] if ok { if conn, ok := conn.(*tls.Conn); ok { // If the underlying connection is a *tls.Conn, Close() does more // than it says on the tin. Specifically, it sends a TLS alert // message, which might block forever if the connection is dead // and we don't have a deadline site. conn.SetWriteDeadline(time.Now().Add(250 * time.Millisecond)) } conn.Close() } delete(m.protoConn, device) delete(m.rawConn, device) delete(m.deviceVer, device) m.pmut.Unlock() }
func withDetailsMiddleware(id protocol.DeviceID, h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Syncthing-Version", Version) w.Header().Set("X-Syncthing-ID", id.String()) h.ServeHTTP(w, r) }) }
func NewDeviceStatisticsReference(ldb *leveldb.DB, device protocol.DeviceID) *DeviceStatisticsReference { prefix := string(db.KeyTypeDeviceStatistic) + device.String() return &DeviceStatisticsReference{ ns: db.NewNamespacedKV(ldb, prefix), device: device, } }
// Lookup returns the list of addresses where the given device is available; // direct, and via relays. func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) { qURL, err := url.Parse(c.server) if err != nil { return nil, nil, err } q := qURL.Query() q.Set("device", device.String()) qURL.RawQuery = q.Encode() resp, err := c.queryClient.Get(qURL.String()) if err != nil { if debug { l.Debugln("globalClient.Lookup", qURL.String(), err) } return nil, nil, err } if resp.StatusCode != 200 { resp.Body.Close() if debug { l.Debugln("globalClient.Lookup", qURL.String(), resp.Status) } return nil, nil, errors.New(resp.Status) } // TODO: Handle 429 and Retry-After? var ann announcement err = json.NewDecoder(resp.Body).Decode(&ann) resp.Body.Close() return ann.Direct, ann.Relays, err }
// IndexUpdate is called for incremental updates to connected devices' indexes. // Implements the protocol.Model interface. func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, flags uint32, options []protocol.Option) { if flags != 0 { l.Warnln("protocol error: unknown flags 0x%x in IndexUpdate message", flags) return } if debug { l.Debugf("%v IDXUP(in): %s / %q: %d files", m, deviceID, folder, len(fs)) } if !m.folderSharedWith(folder, deviceID) { l.Infof("Update for unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID) return } m.fmut.RLock() files := m.folderFiles[folder] runner, ok := m.folderRunners[folder] m.fmut.RUnlock() if !ok { l.Fatalf("IndexUpdate for nonexistant folder %q", folder) } for i := 0; i < len(fs); { if fs[i].Flags&^protocol.FlagsAll != 0 { if debug { l.Debugln("dropping update for file with unknown bits set", fs[i]) } fs[i] = fs[len(fs)-1] fs = fs[:len(fs)-1] } else if symlinkInvalid(folder, fs[i]) { if debug { l.Debugln("dropping update for unsupported symlink", fs[i]) } fs[i] = fs[len(fs)-1] fs = fs[:len(fs)-1] } else { i++ } } files.Update(deviceID, fs) events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{ "device": deviceID.String(), "folder": folder, "items": len(fs), "version": files.LocalVersion(deviceID), }) runner.IndexUpdated() }
func (s *querysrv) updateAddress(tx *sql.Tx, device protocol.DeviceID, uri string) error { res, err := tx.Stmt(s.prep["updateAddress"]).Exec(device.String(), uri) if err != nil { return err } if rows, _ := res.RowsAffected(); rows == 0 { _, err := tx.Stmt(s.prep["insertAddress"]).Exec(device.String(), uri) if err != nil { return err } } return nil }
// Index is called when a new device is connected and we receive their full index. // Implements the protocol.Model interface. func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) { if debug { l.Debugf("IDX(in): %s %q: %d files", deviceID, folder, len(fs)) } if !m.folderSharedWith(folder, deviceID) { events.Default.Log(events.FolderRejected, map[string]string{ "folder": folder, "device": deviceID.String(), }) l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID) return } m.fmut.RLock() files, ok := m.folderFiles[folder] m.fmut.RUnlock() if !ok { l.Fatalf("Index for nonexistant folder %q", folder) } for i := 0; i < len(fs); { lamport.Default.Tick(fs[i].Version) if symlinkInvalid(fs[i].IsSymlink()) { if debug { l.Debugln("dropping update for unsupported symlink", fs[i]) } fs[i] = fs[len(fs)-1] fs = fs[:len(fs)-1] } else { i++ } } files.Replace(deviceID, fs) events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{ "device": deviceID.String(), "folder": folder, "items": len(fs), "version": files.LocalVersion(deviceID), }) }
func (s *querysrv) getAddresses(device protocol.DeviceID) ([]string, error) { rows, err := s.prep["selectAddress"].Query(device.String()) if err != nil { return nil, err } var res []string for rows.Next() { var addr string err := rows.Scan(&addr) if err != nil { log.Println("Scan:", err) continue } res = append(res, addr) } return res, nil }
func (s *querysrv) getRelays(device protocol.DeviceID) ([]discover.Relay, error) { rows, err := s.prep["selectRelay"].Query(device.String()) if err != nil { return nil, err } var res []discover.Relay for rows.Next() { var addr string var latency int32 err := rows.Scan(&addr, &latency) if err != nil { log.Println("Scan:", err) continue } res = append(res, discover.Relay{ Address: addr, Latency: latency, }) } return res, nil }
func (s *querysrv) handleAnnounce(addr *net.UDPAddr, buf []byte) error { var pkt discover.Announce err := pkt.UnmarshalXDR(buf) if err != nil && err != io.EOF { return err } var id protocol.DeviceID copy(id[:], pkt.This.ID) if id == protocol.LocalDeviceID { return fmt.Errorf("Rejecting announce for local device ID from %v", addr) } tx, err := s.db.Begin() if err != nil { return err } for _, annAddr := range pkt.This.Addresses { uri, err := url.Parse(annAddr) if err != nil { continue } host, port, err := net.SplitHostPort(uri.Host) if err != nil { continue } if len(host) == 0 { uri.Host = net.JoinHostPort(addr.IP.String(), port) } if err := s.updateAddress(tx, id, uri.String()); err != nil { tx.Rollback() return err } } _, err = tx.Stmt(s.prep["deleteRelay"]).Exec(id.String()) if err != nil { tx.Rollback() return err } for _, relay := range pkt.This.Relays { uri, err := url.Parse(relay.Address) if err != nil { continue } _, err = tx.Stmt(s.prep["insertRelay"]).Exec(id.String(), uri, relay.Latency) if err != nil { tx.Rollback() return err } } if err := s.updateDevice(tx, id); err != nil { tx.Rollback() return err } return tx.Commit() }
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfigMessage) { m.pmut.Lock() if cm.ClientName == "syncthing" { m.deviceVer[deviceID] = cm.ClientVersion } else { m.deviceVer[deviceID] = cm.ClientName + " " + cm.ClientVersion } event := map[string]string{ "id": deviceID.String(), "clientName": cm.ClientName, "clientVersion": cm.ClientVersion, } if conn, ok := m.rawConn[deviceID].(*tls.Conn); ok { event["addr"] = conn.RemoteAddr().String() } m.pmut.Unlock() events.Default.Log(events.DeviceConnected, event) l.Infof(`Device %s client is "%s %s"`, deviceID, cm.ClientName, cm.ClientVersion) var changed bool if name := cm.GetOption("name"); name != "" { l.Infof("Device %s name is %q", deviceID, name) device, ok := m.cfg.Devices()[deviceID] if ok && device.Name == "" { device.Name = name m.cfg.SetDevice(device) changed = true } } if m.cfg.Devices()[deviceID].Introducer { // This device is an introducer. Go through the announced lists of folders // and devices and add what we are missing. for _, folder := range cm.Folders { // If we don't have this folder yet, skip it. Ideally, we'd // offer up something in the GUI to create the folder, but for the // moment we only handle folders that we already have. if _, ok := m.folderDevices[folder.ID]; !ok { continue } nextDevice: for _, device := range folder.Devices { var id protocol.DeviceID copy(id[:], device.ID) if _, ok := m.cfg.Devices()[id]; !ok { // The device is currently unknown. Add it to the config. l.Infof("Adding device %v to config (vouched for by introducer %v)", id, deviceID) newDeviceCfg := config.DeviceConfiguration{ DeviceID: id, Compression: m.cfg.Devices()[deviceID].Compression, Addresses: []string{"dynamic"}, } // The introducers' introducers are also our introducers. if device.Flags&protocol.FlagIntroducer != 0 { l.Infof("Device %v is now also an introducer", id) newDeviceCfg.Introducer = true } m.cfg.SetDevice(newDeviceCfg) changed = true } for _, er := range m.deviceFolders[id] { if er == folder.ID { // We already share the folder with this device, so // nothing to do. continue nextDevice } } // We don't yet share this folder with this device. Add the device // to sharing list of the folder. l.Infof("Adding device %v to share %q (vouched for by introducer %v)", id, folder.ID, deviceID) m.deviceFolders[id] = append(m.deviceFolders[id], folder.ID) m.folderDevices[folder.ID] = append(m.folderDevices[folder.ID], id) folderCfg := m.cfg.Folders()[folder.ID] folderCfg.Devices = append(folderCfg.Devices, config.FolderDeviceConfiguration{ DeviceID: id, }) m.cfg.SetFolder(folderCfg) changed = true } } } if changed { m.cfg.Save() } }