@@ -55,7 +55,7 @@ type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Int,
5555
5656*seqCounter : Int
5757
58−*forwards : [{messageId: Text, toServer: Text, createdAt: Int}]
58+*forwards : [{messageId: Text, toServer: Text, createdAt: Int, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Int}]
5959
6060*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int}]
6161
@@ -221,6 +221,12 @@ isAllDigitsOrDotsUntilColonOrEnd = \s ->
221221 else if isDigitChar (take 1 s) || take 1 s == "." then isAllDigitsOrDotsUntilColonOrEnd (drop 1 s)
222222 else False {}
223223
224+isAllDigitsUntilColonOrEnd = \s ->
225+ if s == "" then True {}
226+ else if take 1 s == ":" then True {}
227+ else if isDigitChar (take 1 s) then isAllDigitsUntilColonOrEnd (drop 1 s)
228+ else False {}
229+
224230toLowerChar = \c ->
225231 if c == "A" then "a" else if c == "B" then "b" else if c == "C" then "c"
226232 else if c == "D" then "d" else if c == "E" then "e" else if c == "F" then "f"
@@ -250,6 +256,8 @@ stripTrailingDot = \s ->
250256
251257normalizeServerName = \s -> toLower (stripTrailingDot s)
252258
259+serverHostN = normalizeServerName serverHost
260+
253261containsLocalhostSuffix = \s ->
254262 if s == "" then False {}
255263 else if take 10 s == ".localhost" && (drop 10 s == "" || take 1 (drop 10 s) == ":") then True {}
@@ -281,7 +289,15 @@ isBadServerName = \s ->
281289 || containsMultipleColons sn
282290 || hasOctalOctet sn
283291 || hasHexPrefix sn
284− || (isDigitChar (take 1 sn) && isAllDigitsOrDotsUntilColonOrEnd sn)
292+ || (isDigitChar (take 1 sn) && isAllDigitsUntilColonOrEnd sn)
293+ || take 4 sn == "224." || take 4 sn == "225." || take 4 sn == "226." || take 4 sn == "227."
294+ || take 4 sn == "228." || take 4 sn == "229." || take 4 sn == "230." || take 4 sn == "231."
295+ || take 4 sn == "232." || take 4 sn == "233." || take 4 sn == "234." || take 4 sn == "235."
296+ || take 4 sn == "236." || take 4 sn == "237." || take 4 sn == "238." || take 4 sn == "239."
297+ || take 4 sn == "240." || take 4 sn == "241." || take 4 sn == "242." || take 4 sn == "243."
298+ || take 4 sn == "244." || take 4 sn == "245." || take 4 sn == "246." || take 4 sn == "247."
299+ || take 4 sn == "248." || take 4 sn == "249." || take 4 sn == "250." || take 4 sn == "251."
300+ || take 4 sn == "252." || take 4 sn == "253." || take 4 sn == "254." || take 4 sn == "255."
285301
286302lookupSession = \sessions token t ->
287303 single (do
@@ -312,8 +328,8 @@ verifyMaintenanceAuth = \authorization ->
312328clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
313329
314330safeCompareLoop = \s1 s2 result ->
315− if s2 == ""
316− then result + (if s1 == "" then 0 else 1)
331+ if s1 == "" && s2 == ""
332+ then result
317333 else safeCompareLoop (drop 1 s1) (drop 1 s2) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
318334
319335safeCompare = \a b ->
@@ -330,7 +346,6 @@ sendToEach = \servers action ->
330346 else do
331347 catch (action server) (\err -> do
332348 println ("federation: error reaching " ++ server ++ ", retrying: " ++ show err)
333− sleep 500
334349 catch (action server) (\err2 -> do
335350 println ("federation: retry failed for " ++ server ++ ": " ++ show err2)
336351 yield {}))
@@ -349,7 +364,7 @@ pruneExpired = \t -> do
349364 yield s
350365 presenceBefore <- *presence
351366 let peerServersBefore = fold (\acc p ->
352− if p.server == serverHost then acc
367+ if p.server == serverHostN then acc
353368 else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
354369 else union acc [p.server]) [] presenceBefore
355370 let presenceAfter = do
@@ -375,11 +390,11 @@ pruneExpired = \t -> do
375390 yield {expiredLocal: (do
376391 p <- presenceBefore
377392 where p.expiresAt <= t
378− where p.server == serverHost
393+ where p.server == serverHostN
379394 where count (do
380395 q <- presenceAfter
381396 where q.pubkey == p.pubkey
382− where q.server == serverHost
397+ where q.server == serverHostN
383398 yield q) == 0
384399 yield p), peerServers: peerServersBefore}
385400
@@ -397,7 +412,7 @@ sendBatchedGossip = \peerServers allEvents ->
397412 sendToEach peerServers (\server ->
398413 fetch (federationScheme ++ server) (RecvGossip {
399414 authorization: "Bearer " ++ federationSecret,
400− events: batch, hopsRemaining: 2, fromServer: serverHost}))
415+ events: batch, hopsRemaining: 2, fromServer: serverHostN}))
401416 sendBatchedGossip peerServers rest
402417
403418broadcastOfflineGossip = \t peerServers expiredLocal -> do
@@ -408,7 +423,7 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
408423 then do
409424 let gossipEvents = do
410425 e <- expiredEntries
411− yield {eventType: "offline", pubkey: e.pubkey, server: serverHost, timestamp: t, ttl: e.ttl}
426+ yield {eventType: "offline", pubkey: e.pubkey, server: serverHostN, timestamp: t, ttl: e.ttl}
412427 sendBatchedGossip peerServers gossipEvents
413428 else yield {}
414429
@@ -423,7 +438,7 @@ retryPendingForwards = \pending ->
423438 else do
424439 catch (fetch (federationScheme ++ f.toServer) (ForwardMessage {
425440 toKey: f.toKey, fromKey: f.fromKey, encryptedBlob: f.encryptedBlob,
426− timestamp: f.timestamp, originServer: serverHost, messageId: f.messageId,
441+ timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
427442 authorization: "Bearer " ++ federationSecret})) (\err ->
428443 println ("prune: retry forward to " ++ f.toServer ++ " failed: " ++ show err))
429444 retryPendingForwards rest
@@ -525,7 +540,7 @@ serve = \req -> case req of
525540 else sessions
526541 let presenceWithout = do
527542 p <- presence
528− where not (p.pubkey == pubkey && p.server == serverHost)
543+ where not (p.pubkey == pubkey && p.server == serverHostN)
529544 yield p
530545 let oldestPresenceExp = fold (\acc p -> if acc == -1 || p.expiresAt < acc then p.expiresAt else acc) -1 presenceWithout
531546 let presenceEvicted = if count presenceWithout >= maxPresenceEntries && oldestPresenceExp >= 0
@@ -535,12 +550,12 @@ serve = \req -> case req of
535550 else {removed: acc.removed, items: union acc.items [p]}) {removed: False {}, items: []} presenceWithout).items
536551 else presenceWithout
537552 let updatedPresence = if sigValid
538− then union presenceEvicted [{pubkey: pubkey, server: serverHost, expiresAt: expiry, timestamp: t}]
553+ then union presenceEvicted [{pubkey: pubkey, server: serverHostN, expiresAt: expiry, timestamp: t}]
539554 else presence
540555 set *presence = updatedPresence
541556 let peerServers = if sigValid && not (federationSecret == "")
542557 then fold (\acc p ->
543− if p.server == serverHost then acc
558+ if p.server == serverHostN then acc
544559 else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
545560 else union acc [p.server]) [] updatedPresence
546561 else []
@@ -552,7 +567,7 @@ serve = \req -> case req of
552567 then sendToEach result.peerServers (\server ->
553568 fetch (federationScheme ++ server) (RecvGossip {
554569 authorization: "Bearer " ++ federationSecret,
555− events: [{eventType: "online", pubkey: pubkey, server: serverHost, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHost}))
570+ events: [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHostN}))
556571 else yield {}
557572 broadcastOfflineGossip t result.offlinePeers (do
558573 p <- result.expiredLocal
@@ -635,7 +650,7 @@ serve = \req -> case req of
635650 Just {value: _} -> count (do
636651 p <- presence
637652 where p.pubkey == to
638− where p.server == serverHost
653+ where p.server == serverHostN
639654 where p.expiresAt > t
640655 yield p) > 0
641656 let remoteServers = case session of
@@ -643,7 +658,7 @@ serve = \req -> case req of
643658 Just {value: _} -> do
644659 p <- presence
645660 where p.pubkey == to
646− where not (p.server == serverHost)
661+ where not (p.server == serverHostN)
647662 where p.expiresAt > t
648663 yield p.server
649664 let fromKey = case session of
@@ -658,7 +673,7 @@ serve = \req -> case req of
658673 forwards <- *forwards
659674 let forwardsAtCapacity = count forwards >= maxForwardsEntries
660675 set *forwards = if shouldInsert && not forwardsAtCapacity && count remoteServers > 0
661− then union forwards (do s <- remoteServers; yield {messageId: msgId, toServer: s, createdAt: t})
676+ then union forwards (do s <- remoteServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
662677 else forwards
663678 yield {status: status, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
664679 let finalStatus = if result.status == "federated" && federationSecret == "" then "queued" else result.status
@@ -667,7 +682,7 @@ serve = \req -> case req of
667682 then sendToEach result.remoteServers (\server ->
668683 fetch (federationScheme ++ server) (ForwardMessage {
669684 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
670− timestamp: clampedTs, originServer: serverHost, messageId: msgId,
685+ timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
671686 authorization: "Bearer " ++ federationSecret}))
672687 else yield {}
673688 if result.forwardsAtCapacity && count result.remoteServers > 0 && not (result.status == "unauthorized") && not (result.status == "invalid")
@@ -703,7 +718,7 @@ serve = \req -> case req of
703718 let ackedFederated = do
704719 m <- ackedMessages
705720 where not (m.originServer == "")
706− where not (m.originServer == serverHost)
721+ where not (m.originServer == serverHostN)
707722 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t}
708723 ackedIds <- *ackedIds
709724 set *ackedIds = union ackedIds ackedFederated
@@ -714,7 +729,7 @@ serve = \req -> case req of
714729 let fedMessages = do
715730 m <- result.ackedMessages
716731 where not (m.originServer == "")
717− where not (m.originServer == serverHost)
732+ where not (m.originServer == serverHostN)
718733 yield m
719734 let originServers = fold (\acc m ->
720735 if count (do a <- acc; where a == m.originServer; yield a) > 0 then acc
@@ -725,7 +740,7 @@ serve = \req -> case req of
725740 fetch (federationScheme ++ server) (FederationAck {
726741 authorization: "Bearer " ++ federationSecret,
727742 messageIds: (do m <- fedMessages; where m.originServer == server; yield m.messageId),
728− originServer: server, fromServer: serverHost}))
743+ originServer: server, fromServer: serverHostN}))
729744 else yield {}
730745 broadcastOfflineGossip t result.peerServers result.expiredLocal
731746
@@ -753,7 +768,7 @@ serve = \req -> case req of
753768 then ""
754769 else case single (do
755770 p <- valid
756− where p.server == serverHost
771+ where p.server == serverHostN
757772 yield p) of
758773 Just {value: p} -> p.server
759774 Nothing {} -> (fold (\acc p -> if p.expiresAt > acc.best then {server: p.server, best: p.expiresAt} else acc) {server: "", best: 0} valid).server
@@ -766,7 +781,7 @@ serve = \req -> case req of
766781 let authorized = verifyFederationAuth authorization
767782 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
768783 let fromServer = normalizeServerName fromServer
769− let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer) && count events <= maxGossipEvents
784+ let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHostN) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer) && count events <= maxGossipEvents
770785 let onlineEntries = do
771786 e <- events
772787 where validRequest
@@ -777,7 +792,7 @@ serve = \req -> case req of
777792 where not (tooLong e.server maxServerNameLen)
778793 where not (isBadServerName e.server)
779794 where isValidHex e.pubkey
780− where not (normalizeServerName e.server == serverHost)
795+ where not (normalizeServerName e.server == serverHostN)
781796 where e.timestamp <= t + clockSkewTolerance
782797 where e.timestamp + (clampTtl e.ttl) * 1000 > t
783798 yield {pubkey: normalizeHex e.pubkey, server: normalizeServerName e.server, expiresAt: e.timestamp + (clampTtl e.ttl) * 1000, timestamp: e.timestamp}
@@ -791,7 +806,7 @@ serve = \req -> case req of
791806 where not (tooLong e.server maxServerNameLen)
792807 where not (isBadServerName e.server)
793808 where isValidHex e.pubkey
794− where not (normalizeServerName e.server == serverHost)
809+ where not (normalizeServerName e.server == serverHostN)
795810 where e.timestamp <= t + clockSkewTolerance
796811 where t - e.timestamp < (clampTtl e.ttl) * 1000
797812 yield {pubkey: normalizeHex e.pubkey, server: normalizeServerName e.server, timestamp: e.timestamp}
@@ -954,7 +969,7 @@ serve = \req -> case req of
954969 else presence
955970 set *presence = finalPresence
956971 let gossipPeerServers = fold (\acc p ->
957− if p.server == serverHost then acc
972+ if p.server == serverHostN then acc
958973 else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
959974 else union acc [p.server]) [] finalPresence
960975 yield {appliedKeys: (if withinCapacity then union appliedOnline appliedOffline else union appliedOnlineUpdates appliedOffline), expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, gossipPeerServers: gossipPeerServers, withinCapacity: withinCapacity}
@@ -968,7 +983,7 @@ serve = \req -> case req of
968983 where not (tooLong e.server maxServerNameLen)
969984 where not (isBadServerName e.server)
970985 where isValidHex e.pubkey
971− where not (normalizeServerName e.server == serverHost)
986+ where not (normalizeServerName e.server == serverHostN)
972987 where e.timestamp <= t + clockSkewTolerance
973988 where e.timestamp + (clampTtl e.ttl) * 1000 > t
974989 where not (e.eventType == "online" && count (do
@@ -998,7 +1013,7 @@ serve = \req -> case req of
9981013 sendToEach targets (\server ->
9991014 fetch (federationScheme ++ server) (RecvGossip {
10001015 authorization: "Bearer " ++ federationSecret,
1001− events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHost}))
1016+ events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHostN}))
10021017 else yield {}
10031018 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal
10041019
@@ -1011,7 +1026,7 @@ serve = \req -> case req of
10111026 let originServer = normalizeServerName originServer
10121027 t <- now
10131028 let authorized = verifyFederationAuth authorization
1014− let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxServerNameLen) && not (isBadServerName originServer)
1029+ let validOrigin = not (originServer == "") && not (originServer == serverHostN) && not (tooLong originServer maxServerNameLen) && not (isBadServerName originServer)
10151030 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
10161031 result <- atomic do
10171032 pr <- pruneExpired t
@@ -1059,7 +1074,7 @@ serve = \req -> case req of
10591074 else if not (isValidHex mid) then acc
10601075 else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
10611076 else union acc [normalizeHex mid]) [] messageIds
1062− let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer)
1077+ let validAckFields = authorized && originServer == serverHostN && not (fromServer == "") && not (fromServer == serverHostN) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer)
10631078 pruneResult <- atomic do
10641079 pr <- pruneExpired t
10651080 forwards <- *forwards
@@ -1085,13 +1100,9 @@ serve = \req -> case req of
10851100 result <- atomic do
10861101 pr <- pruneExpired t
10871102 forwards <- *forwards
1088− messages <- *messages
10891103 let pendingForwards = do
10901104 f <- forwards
1091− m <- messages
1092− where m.messageId == f.messageId
1093− where m.originServer == ""
1094− yield {toServer: f.toServer, toKey: m.toKey, fromKey: m.fromKey, encryptedBlob: m.encryptedBlob, timestamp: m.timestamp, messageId: m.messageId}
1105+ yield {toServer: f.toServer, toKey: f.toKey, fromKey: f.fromKey, encryptedBlob: f.encryptedBlob, timestamp: f.timestamp, messageId: f.messageId}
10951106 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: pendingForwards}
10961107 respond {ok: True {}}
10971108 broadcastOfflineGossip t result.peerServers result.expiredLocal