{"record":{"id":"344905d071d738f5","repo":"txthinking/brook","slug":"expired-request-344905","errorCode":null,"errorMessage":"Expired request","messagePattern":"Expired request","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"simplepacketserverconn.go","lineNumber":49,"sourceCode":"}\n\nfunc NewSimplePacketServerConnFactory() *SimplePacketServerConnFactory {\n\treturn &SimplePacketServerConnFactory{\n\t\tConns: make(map[string]*PacketConn),\n\t\tLock:  &sync.Mutex{},\n\t}\n}\n\nfunc (f *SimplePacketServerConnFactory) Handle(addr *net.UDPAddr, b, p []byte, w func([]byte) (int, error), timeout int) (net.Conn, []byte, error) {\n\tif len(b) < 32+4 {\n\t\treturn nil, nil, errors.New(\"data too small\")\n\t}\n\tif bytes.Compare(p, b[:32]) != 0 {\n\t\treturn nil, nil, errors.New(\"Password is wrong\")\n\t}\n\ti := int64(binary.BigEndian.Uint32(b[32 : 32+4]))\n\tif time.Now().Unix()-i > 60 {\n\t\treturn nil, nil, errors.New(\"Expired request\")\n\t}\n\ta, h, p, err := socks5.ParseBytesAddress(b[32+4:])\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdst := socks5.ToAddress(a, h, p)\n\tf.Lock.Lock()\n\tc, ok := f.Conns[addr.String()+dst]\n\tf.Lock.Unlock()\n\tif ok {\n\t\t_ = c.In(b[32+4+1+len(h)+2:])\n\t\treturn nil, nil, nil\n\t}\n\tf.Lock.Lock()\n\tc = NewPacketConn(b[32+4+1+len(h)+2:], w, timeout, func() {\n\t\tf.Lock.Lock()\n\t\tdelete(f.Conns, addr.String()+dst)\n\t\tf.Lock.Unlock()","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/txthinking/brook/blob/5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8/simplepacketserverconn.go#L31-L67","documentation":"This error is thrown in the packet server's Handle function when the Unix timestamp embedded at bytes 32-36 of the incoming request packet is more than 60 seconds older than the server's current time. It is an anti-replay measure: clients must include a fresh timestamp so stale or replayed packets are rejected.","triggerScenarios":"A UDP packet arrives whose embedded 4-byte BigEndian timestamp (b[32:36]) is older than 60 seconds, e.g. a replayed capture, a client with a skewed clock, or a packet delayed in transit beyond the 60s window.","commonSituations":"Client machine clock drifts behind the server by more than a minute; a proxy or queuing network delays datagrams; automated replay of captured packets; NTP disabled or misconfigured on the client.","solutions":["Sync the client's clock (enable NTP) so its Unix time matches the server within 60 seconds","Ensure the client regenerates the timestamp for every request instead of reusing/caching packets","Check for network paths (queued proxies, offline buffering) that delay packets more than 60s and reduce latency or retransmit promptly","Verify the client writes the timestamp at offset 32 in BigEndian uint32, matching the server's read position"],"exampleFix":"// before (client caches/stale timestamp)\ni := cachedTimestamp\nbinary.BigEndian.PutUint32(b[32:36], uint32(i))\n// after\ni := time.Now().Unix()\nbinary.BigEndian.PutUint32(b[32:36], uint32(i))","handlingStrategy":"validation","validationCode":"ts := int64(binary.BigEndian.Uint32(req[32:36]))\nif time.Now().Unix()-ts > 55 {\n    return errors.New(\"request would be rejected: timestamp older than 60s\")\n}","typeGuard":null,"tryCatchPattern":"dst, h, p, err := conn.Handle(req)\nif err != nil && err.Error() == \"Expired request\" {\n    // resync clock and rebuild the request with a fresh timestamp\n}","preventionTips":["Run NTP/chrony on all client hosts","Generate a fresh timestamp per request, never cache packets","Keep end-to-end latency under the 60s window","Write the timestamp at offset 32 BigEndian to match the server"],"tags":["network","replay-protection","timestamp","udp"],"backgroundTag":"request-timestamp-expired","analyzedSha":"5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8","analyzedAt":"2026-09-06T04:35:00.432Z","contentChangedAt":"2026-09-06T04:35:00.432Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}