{"record":{"id":"f38f7b22416f06e0","repo":"ginuerzh/gost","slug":"broken-pipe","errorCode":null,"errorMessage":"broken pipe","messagePattern":"broken pipe","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"dns.go","lineNumber":367,"sourceCode":"\t}\n}\n\nfunc (c *dnsServerConn) Read(b []byte) (n int, err error) {\n\tselect {\n\tcase mb := <-c.mq:\n\t\tn = copy(b, mb)\n\tcase <-c.cclose:\n\t\terr = errors.New(\"connection is closed\")\n\t}\n\treturn\n}\n\nfunc (c *dnsServerConn) Write(b []byte) (n int, err error) {\n\tselect {\n\tcase c.mr <- b:\n\t\tn = len(b)\n\tcase <-c.cclose:\n\t\terr = errors.New(\"broken pipe\")\n\t}\n\n\treturn\n}\n\nfunc (c *dnsServerConn) Close() error {\n\tselect {\n\tcase <-c.cclose:\n\tdefault:\n\t\tclose(c.cclose)\n\t}\n\treturn nil\n}\n\nfunc (c *dnsServerConn) LocalAddr() net.Addr {\n\treturn c.laddr\n}\n","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/dns.go#L349-L385","documentation":"dnsServerConn.Write returns this when cclose is closed, meaning the server side of the DNS exchange is gone and there is no reader left on the mr channel — analogous to writing to a broken pipe on a socket. It lets the code satisfy the net.Conn interface while signaling the DNS response can no longer be delivered.","triggerScenarios":"Writing a DNS response after the serve() exchange has closed the virtual connection (response already delivered, timed out, or conn closed by peer abort).","commonSituations":"Duplicate response writes in DNS tunneling handlers, late writes from a goroutine after the request timed out, client disconnected between query and reply.","solutions":["Check cclose state / treat this error as benign and stop writing","Ensure only one Write per DNS exchange occurs before Close","Guard late goroutines with a done channel or context cancellation","Log at debug level — the DNS client simply drops the missed reply"],"exampleFix":"// before\nn, err := conn.Write(resp)\nif err != nil {\n    log.Fatal(err)\n}\n// after\nn, err := conn.Write(resp)\nif err != nil {\n    if err.Error() == \"broken pipe\" {\n        return // client gone; safe to ignore\n    }\n    log.Log(err)\n}","handlingStrategy":"try-catch","validationCode":"// no pre-check possible; Write fails only after cclose fires\nselect {\ncase <-connClosed():\n    return // skip Write, conn already closed\ndefault:\n}","typeGuard":"func isBrokenPipe(err error) bool {\n    return err != nil && err.Error() == \"broken pipe\"\n}","tryCatchPattern":"_, err := conn.Write(resp)\nif err != nil {\n    if isBrokenPipe(err) {\n        return // benign: peer/exchange gone, drop the reply\n    }\n    return err\n}","preventionTips":["Write each DNS response exactly once per exchange","Cancel writer goroutines when the exchange ends to avoid late writes","Log broken-pipe on DNS conns at debug level, not as an error"],"tags":["dns","go","broken-pipe","closed-connection"],"backgroundTag":"broken-pipe","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}