diff options
| -rw-r--r-- | src/nostr.cpp | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp index 009edd1..6f3b6a7 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -398,6 +398,12 @@ static bool BuildSignedEvent(CNostrKey& key, int kind, const json& tags, | |||
| 398 | // | 398 | // |
| 399 | // Minimal WebSocket client over TLS (wss) or TCP (ws), blocking with timeout. | 399 | // Minimal WebSocket client over TLS (wss) or TCP (ws), blocking with timeout. |
| 400 | // | 400 | // |
| 401 | // Ceilings on what a relay may make this node allocate. Nostr events are | ||
| 402 | // small -- relays themselves usually refuse anything over a few hundred KB -- | ||
| 403 | // so these are generous, and they bound an otherwise attacker-chosen size. | ||
| 404 | static const uint64 MAX_WS_FRAME = 1 << 20; // 1 MiB per frame | ||
| 405 | static const uint64 MAX_WS_MESSAGE = 4 << 20; // 4 MiB reassembled | ||
| 406 | |||
| 401 | class CWebSocket | 407 | class CWebSocket |
| 402 | { | 408 | { |
| 403 | public: | 409 | public: |
| @@ -597,6 +603,14 @@ public: | |||
| 597 | len = 0; | 603 | len = 0; |
| 598 | for (int i = 0; i < 8; i++) len = (len << 8) | e[i]; | 604 | for (int i = 0; i < 8; i++) len = (len << 8) | e[i]; |
| 599 | } | 605 | } |
| 606 | // The length is whatever the relay says it is, and these are public | ||
| 607 | // relays the node does not control. Unbounded, a single frame header | ||
| 608 | // claiming 2^40 bytes makes the node allocate until it dies -- and | ||
| 609 | // reassembly across continuation frames grows without limit even | ||
| 610 | // when each frame is small. Cap both, and refuse anything that will | ||
| 611 | // not survive the int narrowing ReadN takes. | ||
| 612 | if (len > MAX_WS_FRAME) return false; | ||
| 613 | |||
| 600 | unsigned char mask[4] = {0,0,0,0}; | 614 | unsigned char mask[4] = {0,0,0,0}; |
| 601 | if (masked && !ReadN((char*)mask, 4)) return false; | 615 | if (masked && !ReadN((char*)mask, 4)) return false; |
| 602 | 616 | ||
| @@ -610,6 +624,7 @@ public: | |||
| 610 | if (opcode == 0x8) return false; // close | 624 | if (opcode == 0x8) return false; // close |
| 611 | if (opcode == 0x9) { SendPong(payload); continue; } // ping | 625 | if (opcode == 0x9) { SendPong(payload); continue; } // ping |
| 612 | if (opcode == 0xA) continue; // pong | 626 | if (opcode == 0xA) continue; // pong |
| 627 | if (out.size() + payload.size() > MAX_WS_MESSAGE) return false; | ||
| 613 | out += payload; // text/continuation | 628 | out += payload; // text/continuation |
| 614 | if (fin) return true; | 629 | if (fin) return true; |
| 615 | } | 630 | } |
| @@ -847,7 +862,7 @@ bool BtfQueryPoolAnnouncement(const std::string& poolBtfAddr, BtfPoolAnnouncemen | |||
| 847 | break; | 862 | break; |
| 848 | json j; | 863 | json j; |
| 849 | try { j = json::parse(msg); } catch (...) { continue; } | 864 | try { j = json::parse(msg); } catch (...) { continue; } |
| 850 | if (!j.is_array() || j.empty()) continue; | 865 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 851 | string type = j[0].get<string>(); | 866 | string type = j[0].get<string>(); |
| 852 | if (type == "EVENT" && j.size() >= 3) | 867 | if (type == "EVENT" && j.size() >= 3) |
| 853 | { | 868 | { |
| @@ -957,7 +972,7 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | |||
| 957 | break; | 972 | break; |
| 958 | json j; | 973 | json j; |
| 959 | try { j = json::parse(msg); } catch (...) { continue; } | 974 | try { j = json::parse(msg); } catch (...) { continue; } |
| 960 | if (!j.is_array() || j.empty()) continue; | 975 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 961 | string t = j[0].get<string>(); | 976 | string t = j[0].get<string>(); |
| 962 | if (t == "EVENT" && j.size() >= 3) | 977 | if (t == "EVENT" && j.size() >= 3) |
| 963 | { | 978 | { |
| @@ -1068,7 +1083,7 @@ bool BtfResolveMany(const std::vector<std::string>& btfAddrs, | |||
| 1068 | break; | 1083 | break; |
| 1069 | json j; | 1084 | json j; |
| 1070 | try { j = json::parse(msg); } catch (...) { continue; } | 1085 | try { j = json::parse(msg); } catch (...) { continue; } |
| 1071 | if (!j.is_array() || j.empty()) continue; | 1086 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 1072 | string t = j[0].get<string>(); | 1087 | string t = j[0].get<string>(); |
| 1073 | if (t == "EVENT" && j.size() >= 3) | 1088 | if (t == "EVENT" && j.size() >= 3) |
| 1074 | { | 1089 | { |
| @@ -1144,7 +1159,7 @@ static bool SeedFromRelay(CNostrKey& key, const string& relay) | |||
| 1144 | break; | 1159 | break; |
| 1145 | json j; | 1160 | json j; |
| 1146 | try { j = json::parse(msg); } catch (...) { continue; } | 1161 | try { j = json::parse(msg); } catch (...) { continue; } |
| 1147 | if (!j.is_array() || j.empty()) continue; | 1162 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 1148 | string type = j[0].get<string>(); | 1163 | string type = j[0].get<string>(); |
| 1149 | if (type == "EVENT" && j.size() >= 3) | 1164 | if (type == "EVENT" && j.size() >= 3) |
| 1150 | { | 1165 | { |
| @@ -1194,7 +1209,7 @@ static bool SeedFromRelay(CNostrKey& key, const string& relay) | |||
| 1194 | break; | 1209 | break; |
| 1195 | json j; | 1210 | json j; |
| 1196 | try { j = json::parse(msg); } catch (...) { continue; } | 1211 | try { j = json::parse(msg); } catch (...) { continue; } |
| 1197 | if (!j.is_array() || j.empty()) continue; | 1212 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 1198 | string type = j[0].get<string>(); | 1213 | string type = j[0].get<string>(); |
| 1199 | if (type == "EVENT" && j.size() >= 3) | 1214 | if (type == "EVENT" && j.size() >= 3) |
| 1200 | { | 1215 | { |
| @@ -1234,7 +1249,7 @@ static bool SeedFromRelay(CNostrKey& key, const string& relay) | |||
| 1234 | break; | 1249 | break; |
| 1235 | json j; | 1250 | json j; |
| 1236 | try { j = json::parse(msg); } catch (...) { continue; } | 1251 | try { j = json::parse(msg); } catch (...) { continue; } |
| 1237 | if (!j.is_array() || j.empty()) continue; | 1252 | if (!j.is_array() || j.empty() || !j[0].is_string()) continue; |
| 1238 | string type = j[0].get<string>(); | 1253 | string type = j[0].get<string>(); |
| 1239 | if (type == "EVENT" && j.size() >= 3) | 1254 | if (type == "EVENT" && j.size() >= 3) |
| 1240 | HandlePoolAnnouncement(j[2]); | 1255 | HandlePoolAnnouncement(j[2]); |