{"record":{"id":"77a24f0b505488a1","repo":"txthinking/brook","slug":"expired-request","errorCode":null,"errorMessage":"Expired request","messagePattern":"Expired request","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packetserverconn.go","lineNumber":71,"sourceCode":"\t\treturn nil, nil, err\n\t}\n\n\tcb, err := aes.NewCipher(ck)\n\tif err != nil {\n\t\tx.BP32.Put(ck)\n\t\treturn nil, nil, err\n\t}\n\tx.BP32.Put(ck)\n\tca, err := cipher.NewGCM(cb)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif _, err := ca.Open(b[:12], b[:12], b[12:], nil); err != nil {\n\t\treturn nil, nil, err\n\t}\n\ti := int64(binary.BigEndian.Uint32(b[12 : 12+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[12+4:])\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif 12+4+1+len(h)+2 >= len(b)-16 {\n\t\treturn nil, nil, errors.New(fmt.Sprintf(\"invalid packet. length: %d address: %#x %#x %#x\", len(b), a, h, p))\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[12+4+1+len(h)+2 : len(b)-16])\n\t\treturn nil, nil, nil\n\t}\n\tf.Lock.Lock()\n\tc = NewPacketConn(b[12+4+1+len(h)+2:len(b)-16], w, timeout, func() {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/txthinking/brook/blob/5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8/packetserverconn.go#L53-L89","documentation":"The packet server's Handle method decrypts an incoming UDP packet and reads a 4-byte big-endian timestamp embedded after the nonce. If the packet's timestamp is more than 60 seconds old, the request is considered stale (a replay or delayed datagram) and is rejected with \"Expired request\". This protects the packet protocol against replay attacks.","triggerScenarios":"Sending a UDP packet to the brook packet server whose embedded Unix timestamp (bytes 12..16 after decryption) is older than 60 seconds at processing time; system clock skew between client and server exceeding 60s; replaying captured packets.","commonSituations":"Client machine clock drifted behind server clock; queued/delayed UDP datagrams on a congested network; packet-capture replay tools; VMs with suspended clocks resuming and flushing old packets.","solutions":["Sync the client clock (NTP) so the embedded timestamp is within 60 seconds of the server clock.","Regenerate/rebuild the client so each packet embeds time.Now().Unix() at send time instead of a stale value.","Check for proxies/NAT buffering UDP datagrams for long periods; reduce queuing upstream.","Verify client and server are in the same timezone-independent UTC epoch (they should be; investigate if timestamps differ systematically)."],"exampleFix":"// before: packet built with stale cached timestamp\nts := cachedTimestamp\n// after: embed current time at send time\nts := uint32(time.Now().Unix())","handlingStrategy":"retry","validationCode":"ts := time.Now().Unix()\n// ensure client embeds ts at send time and clocks are NTP-synced\nif math.Abs(float64(ts-serverTimeHint)) > 30 { go syncClockViaNTP() }","typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"Expired request\") {\n    // resend a fresh packet immediately after resyncing the clock\n    time.Sleep(500 * time.Millisecond)\n    return sendFreshPacket()\n}","preventionTips":["Run NTP/chrony on all hosts running brook clients or servers.","Never cache the timestamp used to build packets; always call time.Now() per packet.","Avoid long-lived UDP send queues; drop datagrams older than ~30s client-side.","Monitor clock skew between client and server hosts."],"tags":["udp","replay-protection","clock-skew","timestamp"],"backgroundTag":"request-timeout","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"}