{"record":{"id":"fc8f9d3e2e3dd3d0","repo":"shadow1ng/fscan","slug":"s-w-socks5-handshake-read-failed","errorCode":null,"errorMessage":"%s: %w [socks5_handshake_read_failed]","messagePattern":"(.+?): %w \\[socks5_handshake_read_failed\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/local/socks5proxy.go","lineNumber":165,"sourceCode":"\tif err != nil {\n\t\tif ctx.Err() == nil {\n\t\t\tsession.LogError(i18n.Tr(\"socks5_request_failed\", err))\n\t\t}\n\t\treturn\n\t}\n\tdefer func() { _ = targetConn.Close() }()\n\n\tsession.LogSuccess(i18n.GetText(\"socks5_connected\"))\n\n\t// 双向数据转发\n\tp.relayData(clientConn, targetConn)\n}\n\n// handleSocks5Handshake 处理SOCKS5握手\nfunc (p *Socks5ProxyPlugin) handleSocks5Handshake(conn net.Conn) error {\n\theader := make([]byte, 2)\n\tif _, err := io.ReadFull(conn, header); err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"socks5_handshake_read_failed\"), err)\n\t}\n\n\tif header[0] != 0x05 || header[1] == 0 {\n\t\treturn fmt.Errorf(\"%s\", i18n.GetText(\"socks5_unsupported_version\"))\n\t}\n\tmethods := make([]byte, int(header[1]))\n\tif _, err := io.ReadFull(conn, methods); err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"socks5_handshake_read_failed\"), err)\n\t}\n\tif !containsByte(methods, 0x00) {\n\t\t_, _ = conn.Write([]byte{0x05, 0xff})\n\t\treturn fmt.Errorf(\"%s\", i18n.GetText(\"socks5_unsupported_version\"))\n\t}\n\n\t// 发送握手响应（无认证）\n\tresponse := []byte{0x05, 0x00} // 版本5，无认证\n\tif _, err := conn.Write(response); err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"socks5_handshake_write_failed\"), err)","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/local/socks5proxy.go#L147-L183","documentation":"Wrap of an io.ReadFull failure at the start of the SOCKS5 handshake: fewer than the 2 header bytes (VER + NMETHODS) arrived from the client, usually because the client disconnected, sent a non-SOCKS5 protocol, or the connection timed out. The i18n socks5_handshake_read_failed prefix identifies the handshake read stage; %w preserves the I/O cause.","triggerScenarios":"io.ReadFull(conn, header[:2]) in handleSocks5Handshake fails — client closed before sending 2 bytes, sent a partial greeting, or a non-SOCKS client (e.g. an HTTP health check) connected to the port.","commonSituations":"Port scanners or browsers pointing plain HTTP at the SOCKS port; load balancer health probes connecting and disconnecting; client using SOCKS4, which sends a different greeting; network reset mid-handshake.","solutions":["Point only SOCKS5-capable clients at the proxy port; move health checks to a dedicated HTTP endpoint.","Set a read deadline on the conn before ReadFull so stalled clients time out cleanly instead of hanging.","Log the bytes received (if any) to detect non-SOCKS protocols probing the port.","Retry the connection from the client; if persistent, check for middleboxes resetting the TCP stream."],"exampleFix":"// before\nheader := make([]byte, 2)\nif _, err := io.ReadFull(conn, header); err != nil {\n\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"socks5_handshake_read_failed\"), err)\n}\n// after — bound the handshake so dead clients don't hang the handler\nif err := conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {\n\treturn err\n}\nheader := make([]byte, 2)\nif _, err := io.ReadFull(conn, header); err != nil {\n\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\treturn fmt.Errorf(\"client closed before SOCKS5 greeting\")\n\t}\n\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"socks5_handshake_read_failed\"), err)\n}","handlingStrategy":"validation","validationCode":"// caller-side sanity before connecting\nfunc probeSocks5(addr string, timeout time.Duration) error {\n\tc, err := net.DialTimeout(\"tcp\", addr, timeout)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer c.Close()\n\tc.SetDeadline(time.Now().Add(timeout))\n\t_, err = c.Write([]byte{0x05, 0x01, 0x00})\n\treturn err\n}","typeGuard":"func isShortRead(err error) bool {\n\treturn errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"if err := handleSocks5Handshake(conn); err != nil {\n\tif isShortRead(errors.Unwrap(err)) {\n\t\t// client vanished mid-handshake: close quietly, no alert\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Point only SOCKS5 clients at the proxy port","Send the greeting as one atomic write","Set read deadlines on every handshake","Keep health checks on a separate HTTP port"],"tags":["network","socks5","protocol","io"],"backgroundTag":"invalid-json-response","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}