shadow1ng/fscan · error

rsync_connect_failed

Error message

rsync_connect_failed

What it means

doRsyncAuth first opens a TCP connection and requests the module list via connectToRsync. If connectToRsync returns nil (connection or LIST-phase handshake failed), the auth attempt is aborted with rsync_connect_failed and ErrorTypeNetwork, indicating a network-level or protocol-level failure to talk to the rsync daemon.

Source

Thrown at plugins/services/rsync.go:113

	}
}

// createAuthFunc 创建Rsync认证函数
func (p *RsyncPlugin) createAuthFunc(info *common.HostInfo, session *common.ScanSession) AuthFunc {
	return func(ctx context.Context, cred Credential) *AuthResult {
		return p.doRsyncAuth(ctx, info, cred, session)
	}
}

// doRsyncAuth 执行Rsync认证
func (p *RsyncPlugin) doRsyncAuth(ctx context.Context, info *common.HostInfo, cred Credential, session *common.ScanSession) *AuthResult {
	// 先获取可用模块列表
	conn := p.connectToRsync(ctx, info, session)
	if conn == nil {
		return &AuthResult{
			Success:   false,
			ErrorType: ErrorTypeNetwork,
			Error:     fmt.Errorf("%s", i18n.GetText("rsync_connect_failed")),
		}
	}
	modules := p.getModules(conn, session.Config)
	_ = conn.Close()

	if len(modules) == 0 {
		return &AuthResult{
			Success:   false,
			ErrorType: ErrorTypeUnknown,
			Error:     fmt.Errorf("%s", i18n.GetText("rsync_modules_failed")),
		}
	}

	// 提取第一个模块名
	var firstModule string
	for _, moduleLine := range modules {
		if fields := strings.Fields(moduleLine); len(fields) > 0 {
			firstModule = fields[0]

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the rsync daemon is reachable: nc -vz <host> 873 or telnet and expect the @RSYNCD banner.
  2. Confirm the daemon port matches the scan target (port option in rsyncd.conf / non-default ports).
  3. Check rsyncd.conf 'hosts allow'/'hosts deny' so the scanner IP is permitted.
  4. Increase the connection timeout in the scan config for slow links.

Example fix

// before (wrong port)
target := "10.0.0.5:22" // rsync_connect_failed
// after
target := "10.0.0.5:873" // @RSYNCD 31.0 banner received
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host+":873", 5*time.Second)
if err != nil { /* skip host: rsync not reachable */ }
else { conn.SetReadDeadline(time.Now().Add(3*time.Second)); buf := make([]byte, 64); n, _ := conn.Read(buf); if !strings.HasPrefix(string(buf[:n]), "@RSYNCD") { /* not rsync */ } conn.Close() }

Try / catch

res, err := plugin.Scan(ctx, target)
if err != nil && strings.Contains(err.Error(), "rsync_connect_failed") {
    // transient network? retry with backoff, else mark host unreachable
}

Prevention

When it happens

Trigger: connectToRsync cannot dial info.Target() within the timeout, the TCP connection is refused/reset, or the initial rsync protocol handshake (@RSYNCD greeting) does not complete, so the function returns nil and the error is raised at rsync.go:113.

Common situations: rsync daemon not running or bound to a different port; firewall dropping 873; module list restricted by hosts allow/deny; daemon configured with 'list = no' or proxy in between; network timeouts in cloud environments.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/27f63c0d9703188d. Report an issue: GitHub.