@@ -27,6 +27,7 @@ maxMessageIdLen = 128
27maxTokenLen = 256
27maxTokenLen = 256
28maxChallenges = 10000
28maxChallenges = 10000
29maxSessions = 10000
29maxSessions = 10000
30maxChallengesPerKey = 5
31maxChallengesPerKey = 5
31maxSessionsPerKey = 10
32maxSessionsPerKey = 10
32challengeExpiry = 60000 -- 1 minute in milliseconds
33challengeExpiry = 60000 -- 1 minute in milliseconds
@@ -167,18 +168,22 @@ isCgnatRange = \s ->
167 if not (take 4 s == "100.") then False {}
168 if not (take 4 s == "100.") then False {}
168 else if (take 1 (drop 4 s) == "7" || take 1 (drop 4 s) == "8" || take 1 (drop 4 s) == "9")
169 else if (take 1 (drop 4 s) == "7" || take 1 (drop 4 s) == "8" || take 1 (drop 4 s) == "9")
169 && isDigitChar (take 1 (drop 5 s)) && take 1 (drop 6 s) == "."
170 && isDigitChar (take 1 (drop 5 s)) && take 1 (drop 6 s) == "."
171+ && isAllDigitsOrDotsUntilColonOrEnd (drop 7 s)
170 then True {}
172 then True {}
171 else if take 1 (drop 4 s) == "6"
173 else if take 1 (drop 4 s) == "6"
172 && (take 1 (drop 5 s) == "4" || take 1 (drop 5 s) == "5" || take 1 (drop 5 s) == "6" || take 1 (drop 5 s) == "7" || take 1 (drop 5 s) == "8" || take 1 (drop 5 s) == "9")
174 && (take 1 (drop 5 s) == "4" || take 1 (drop 5 s) == "5" || take 1 (drop 5 s) == "6" || take 1 (drop 5 s) == "7" || take 1 (drop 5 s) == "8" || take 1 (drop 5 s) == "9")
173 && take 1 (drop 6 s) == "."
175 && take 1 (drop 6 s) == "."
176+ && isAllDigitsOrDotsUntilColonOrEnd (drop 7 s)
174 then True {}
177 then True {}
175 else if take 1 (drop 4 s) == "1" && (take 1 (drop 5 s) == "0" || take 1 (drop 5 s) == "1")
178 else if take 1 (drop 4 s) == "1" && (take 1 (drop 5 s) == "0" || take 1 (drop 5 s) == "1")
176 && isDigitChar (take 1 (drop 6 s)) && take 1 (drop 7 s) == "."
179 && isDigitChar (take 1 (drop 6 s)) && take 1 (drop 7 s) == "."
180+ && isAllDigitsOrDotsUntilColonOrEnd (drop 8 s)
177 then True {}
181 then True {}
178 else if take 1 (drop 4 s) == "1" && take 1 (drop 5 s) == "2"
182 else if take 1 (drop 4 s) == "1" && take 1 (drop 5 s) == "2"
179 && (take 1 (drop 6 s) == "0" || take 1 (drop 6 s) == "1" || take 1 (drop 6 s) == "2" || take 1 (drop 6 s) == "3"
183 && (take 1 (drop 6 s) == "0" || take 1 (drop 6 s) == "1" || take 1 (drop 6 s) == "2" || take 1 (drop 6 s) == "3"
180 || take 1 (drop 6 s) == "4" || take 1 (drop 6 s) == "5" || take 1 (drop 6 s) == "6" || take 1 (drop 6 s) == "7")
184 || take 1 (drop 6 s) == "4" || take 1 (drop 6 s) == "5" || take 1 (drop 6 s) == "6" || take 1 (drop 6 s) == "7")
181 && take 1 (drop 7 s) == "."
185 && take 1 (drop 7 s) == "."
186+ && isAllDigitsOrDotsUntilColonOrEnd (drop 8 s)
182 then True {}
187 then True {}
183 else False {}
188 else False {}
@@ -328,8 +333,8 @@ verifyMaintenanceAuth = \authorization ->
328clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
333clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
330safeCompareLoop = \s1 s2 result ->
335safeCompareLoop = \s1 s2 result ->
331− if s1 == "" && s2 == ""
336+ if s2 == ""
332− then result
337+ then result + (if s1 == "" then 0 else 1)
333 else safeCompareLoop (drop 1 s1) (drop 1 s2) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
338 else safeCompareLoop (drop 1 s1) (drop 1 s2) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
335safeCompare = \a b ->
340safeCompare = \a b ->
@@ -344,11 +349,8 @@ sendToEach = \servers action ->
344 if server == ""
349 if server == ""
345 then yield {}
350 then yield {}
347− catch (action server) (\err -> do
352+ catch (action server) (\err ->
348− println ("federation: error reaching " ++ server ++ ", retrying: " ++ show err)
353+ println ("federation: error reaching " ++ server ++ ": " ++ show err))
349− catch (action server) (\err2 -> do
350− println ("federation: retry failed for " ++ server ++ ": " ++ show err2)
352 sendToEach rest action
354 sendToEach rest action
354pruneExpired = \t -> do
356pruneExpired = \t -> do
@@ -436,10 +438,12 @@ retryPendingForwards = \pending ->
436 if f.messageId == ""
438 if f.messageId == ""
437 then yield {}
439 then yield {}
439− catch (fetch (federationScheme ++ f.toServer) (ForwardMessage {
442+ fetch (federationScheme ++ f.toServer) (ForwardMessage {
440− toKey: f.toKey, fromKey: f.fromKey, encryptedBlob: f.encryptedBlob,
443+ toKey: f.toKey, fromKey: f.fromKey, encryptedBlob: f.encryptedBlob,
441− timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
444+ timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
442− authorization: "Bearer " ++ federationSecret})) (\err ->
445+ authorization: "Bearer " ++ federationSecret})
443 println ("prune: retry forward to " ++ f.toServer ++ " failed: " ++ show err))
447 println ("prune: retry forward to " ++ f.toServer ++ " failed: " ++ show err))
444 retryPendingForwards rest
448 retryPendingForwards rest
@@ -676,7 +680,7 @@ serve = \req -> case req of
676 then union forwards (do s <- remoteServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
680 then union forwards (do s <- remoteServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
677 else forwards
681 else forwards
678 yield {status: status, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
682 yield {status: status, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
679− let finalStatus = if result.status == "federated" && federationSecret == "" then "queued" else result.status
683+ let finalStatus = if result.status == "federated" && (federationSecret == "" || result.forwardsAtCapacity) then "queued" else result.status
680 respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else if result.status == "invalid" then "" else msgId)}
684 respond {status: finalStatus, messageId: (if result.status == "unauthorized" then "" else if result.status == "invalid" then "" else msgId)}
681 if count result.remoteServers > 0 && not (result.status == "unauthorized") && not (result.status == "invalid") && not (federationSecret == "")
685 if count result.remoteServers > 0 && not (result.status == "unauthorized") && not (result.status == "invalid") && not (federationSecret == "")
682 then sendToEach result.remoteServers (\server ->
686 then sendToEach result.remoteServers (\server ->
@@ -721,7 +725,16 @@ serve = \req -> case req of
721 where not (m.originServer == serverHostN)
725 where not (m.originServer == serverHostN)
722 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t}
726 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t}
723 ackedIds <- *ackedIds
727 ackedIds <- *ackedIds
724− set *ackedIds = union ackedIds ackedFederated
728+ let combinedAckedIds = union ackedIds ackedFederated
729+ let oldestAckedAt = if count combinedAckedIds > maxAckedIds
730+ then fold (\acc a -> if acc == -1 || a.ackedAt < acc then a.ackedAt else acc) -1 combinedAckedIds
732+ set *ackedIds = if oldestAckedAt >= 0
733+ then (fold (\acc a ->
734+ if not acc.removed && a.ackedAt == oldestAckedAt
735+ then {removed: True {}, items: acc.items}
736+ else {removed: acc.removed, items: union acc.items [a]}) {removed: False {}, items: []} combinedAckedIds).items
737+ else combinedAckedIds
725 let ok = case session of
738 let ok = case session of
726 Nothing {} -> False {}
739 Nothing {} -> False {}
727 _ -> True {}
740 _ -> True {}