{"record":{"id":"47bd9c146ca81fbf","repo":"tailscale/tailscale","slug":"unsupported-command-v","errorCode":null,"errorMessage":"unsupported command %v","messagePattern":"unsupported command (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"net/socks5/socks5.go","lineNumber":212,"sourceCode":"\treq, err := parseClientRequest(c.clientConn)\n\tif err != nil {\n\t\tres := errorResponse(generalFailure)\n\t\tbuf, _ := res.marshal()\n\t\tc.clientConn.Write(buf)\n\t\treturn err\n\t}\n\n\tc.request = req\n\tswitch req.command {\n\tcase connect:\n\t\treturn c.handleTCP()\n\tcase udpAssociate:\n\t\treturn c.handleUDP()\n\tdefault:\n\t\tres := errorResponse(commandNotSupported)\n\t\tbuf, _ := res.marshal()\n\t\tc.clientConn.Write(buf)\n\t\treturn fmt.Errorf(\"unsupported command %v\", req.command)\n\t}\n}\n\nfunc (c *Conn) handleTCP() error {\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\tsrv, err := c.srv.dial(\n\t\tctx,\n\t\t\"tcp\",\n\t\tc.request.destination.hostPort(),\n\t)\n\tif err != nil {\n\t\tres := errorResponse(generalFailure)\n\t\tbuf, _ := res.marshal()\n\t\tc.clientConn.Write(buf)\n\t\treturn err\n\t}\n\tdefer srv.Close()","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/tailscale/tailscale/blob/57c3357fdb542d26c6f9e9f0b815ae5077e63d77/net/socks5/socks5.go#L194-L230","documentation":"The SOCKS5 server only implements CONNECT (1) and UDP ASSOCIATE (3). A client request with any other command byte — practically BIND (2), since those are the three RFC 1928 commands — gets a commandNotSupported (0x07) reply written to the client and then this error. It is a protocol-level rejection, not an I/O failure.","triggerScenarios":"Conn.Run parses a request whose req.command is not connect (1) or udpAssociate (3) — e.g. byte 2 (bind) or 0/garbage — hits the default switch arm at socks5.go:204, writes errorResponse(commandNotSupported), and returns fmt.Errorf(\"unsupported command %v\", req.command).","commonSituations":"FTP clients in active mode (they issue BIND); some P2P/game clients requiring BIND; hand-rolled clients sending a zeroed command byte; version drift where a client sends an extended command the server doesn't know.","solutions":["Client side: use CONNECT for TCP and UDP ASSOCIATE for UDP; for FTP switch the client to passive (PASV) mode so no BIND is needed","Server side: implement bind by adding a case bind: return c.handleBind() arm to the switch","Log req.command at connect time to identify the offending client","If garbage commands appear, check for a non-SOCKS5 client (e.g. plain HTTP proxy request) hitting the SOCKS port"],"exampleFix":"// before\nswitch req.command {\ncase connect:\n    return c.handleTCP()\ncase udpAssociate:\n    return c.handleUDP()\ndefault:\n    return fmt.Errorf(\"unsupported command %v\", req.command)\n}\n\n// after: support BIND for legacy FTP\ncase bind:\n    return c.handleBind()","handlingStrategy":"validation","validationCode":"// client side: only ever send supported commands\nconst (\n    cmdConnect      byte = 1\n    cmdUDPAssociate byte = 3\n)\nreq := []byte{5, cmdConnect, 0, 1, ...} // never 2 (BIND)","typeGuard":"func supportedCommand(cmd byte) bool { return cmd == 1 || cmd == 3 }","tryCatchPattern":null,"preventionTips":["Use CONNECT for TCP and UDP ASSOCIATE for UDP; no mainstream use case needs BIND (FTP active mode should switch to passive)","Validate the command byte after parsing and reject/log before dispatch","Expect the 0x07 (command not supported) reply and surface it as a client-side configuration error"],"tags":["socks5","proxy","network","go","protocol"],"backgroundTag":"socks5-unsupported-command","analyzedSha":"57c3357fdb542d26c6f9e9f0b815ae5077e63d77","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}