more addr message error checking
[novacoin.git] / net.h
1 // Copyright (c) 2009-2010 Satoshi Nakamoto\r
2 // Distributed under the MIT/X11 software license, see the accompanying\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
4 \r
5 class CMessageHeader;\r
6 class CAddress;\r
7 class CInv;\r
8 class CRequestTracker;\r
9 class CNode;\r
10 class CBlockIndex;\r
11 extern int nBestHeight;\r
12 \r
13 \r
14 \r
15 #define DEFAULT_PORT    htons(8333)\r
16 static const unsigned int PUBLISH_HOPS = 5;\r
17 enum\r
18 {\r
19     NODE_NETWORK = (1 << 0),\r
20 };\r
21 \r
22 \r
23 \r
24 \r
25 bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet);\r
26 bool GetMyExternalIP(unsigned int& ipRet);\r
27 bool AddAddress(CAddress addr, bool fCurrentlyOnline=true);\r
28 void AddressCurrentlyConnected(const CAddress& addr);\r
29 CNode* FindNode(unsigned int ip);\r
30 CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0);\r
31 void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);\r
32 bool AnySubscribed(unsigned int nChannel);\r
33 bool BindListenPort(string& strError=REF(string()));\r
34 void StartNode(void* parg);\r
35 bool StopNode();\r
36 \r
37 \r
38 \r
39 \r
40 \r
41 \r
42 \r
43 \r
44 //\r
45 // Message header\r
46 //  (4) message start\r
47 //  (12) command\r
48 //  (4) size\r
49 //  (4) checksum\r
50 \r
51 // The message start string is designed to be unlikely to occur in normal data.\r
52 // The characters are rarely used upper ascii, not valid as UTF-8, and produce\r
53 // a large 4-byte int at any alignment.\r
54 static const char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };\r
55 \r
56 class CMessageHeader\r
57 {\r
58 public:\r
59     enum { COMMAND_SIZE=12 };\r
60     char pchMessageStart[sizeof(::pchMessageStart)];\r
61     char pchCommand[COMMAND_SIZE];\r
62     unsigned int nMessageSize;\r
63     unsigned int nChecksum;\r
64 \r
65     CMessageHeader()\r
66     {\r
67         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
68         memset(pchCommand, 0, sizeof(pchCommand));\r
69         pchCommand[1] = 1;\r
70         nMessageSize = -1;\r
71         nChecksum = 0;\r
72     }\r
73 \r
74     CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)\r
75     {\r
76         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
77         strncpy(pchCommand, pszCommand, COMMAND_SIZE);\r
78         nMessageSize = nMessageSizeIn;\r
79         nChecksum = 0;\r
80     }\r
81 \r
82     IMPLEMENT_SERIALIZE\r
83     (\r
84         READWRITE(FLATDATA(pchMessageStart));\r
85         READWRITE(FLATDATA(pchCommand));\r
86         READWRITE(nMessageSize);\r
87         if (nVersion >= 209)\r
88             READWRITE(nChecksum);\r
89     )\r
90 \r
91     string GetCommand()\r
92     {\r
93         if (pchCommand[COMMAND_SIZE-1] == 0)\r
94             return string(pchCommand, pchCommand + strlen(pchCommand));\r
95         else\r
96             return string(pchCommand, pchCommand + COMMAND_SIZE);\r
97     }\r
98 \r
99     bool IsValid()\r
100     {\r
101         // Check start string\r
102         if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0)\r
103             return false;\r
104 \r
105         // Check the command string for errors\r
106         for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)\r
107         {\r
108             if (*p1 == 0)\r
109             {\r
110                 // Must be all zeros after the first zero\r
111                 for (; p1 < pchCommand + COMMAND_SIZE; p1++)\r
112                     if (*p1 != 0)\r
113                         return false;\r
114             }\r
115             else if (*p1 < ' ' || *p1 > 0x7E)\r
116                 return false;\r
117         }\r
118 \r
119         // Message size\r
120         if (nMessageSize > 0x10000000)\r
121         {\r
122             printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize);\r
123             return false;\r
124         }\r
125 \r
126         return true;\r
127     }\r
128 };\r
129 \r
130 \r
131 \r
132 \r
133 \r
134 \r
135 static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };\r
136 \r
137 class CAddress\r
138 {\r
139 public:\r
140     uint64 nServices;\r
141     unsigned char pchReserved[12];\r
142     unsigned int ip;\r
143     unsigned short port;\r
144 \r
145     // disk only\r
146     unsigned int nTime;\r
147 \r
148     // memory only\r
149     unsigned int nLastTry;\r
150 \r
151     CAddress()\r
152     {\r
153         Init();\r
154     }\r
155 \r
156     CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=NODE_NETWORK)\r
157     {\r
158         Init();\r
159         ip = ipIn;\r
160         port = portIn;\r
161         nServices = nServicesIn;\r
162     }\r
163 \r
164     explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)\r
165     {\r
166         Init();\r
167         ip = sockaddr.sin_addr.s_addr;\r
168         port = sockaddr.sin_port;\r
169         nServices = nServicesIn;\r
170     }\r
171 \r
172     explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)\r
173     {\r
174         Init();\r
175         SetAddress(pszIn);\r
176         nServices = nServicesIn;\r
177     }\r
178 \r
179     explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)\r
180     {\r
181         Init();\r
182         SetAddress(strIn.c_str());\r
183         nServices = nServicesIn;\r
184     }\r
185 \r
186     void Init()\r
187     {\r
188         nServices = NODE_NETWORK;\r
189         memcpy(pchReserved, pchIPv4, sizeof(pchReserved));\r
190         ip = INADDR_NONE;\r
191         port = DEFAULT_PORT;\r
192         nTime = GetAdjustedTime();\r
193         nLastTry = 0;\r
194     }\r
195 \r
196     bool SetAddress(const char* pszIn)\r
197     {\r
198         ip = INADDR_NONE;\r
199         port = DEFAULT_PORT;\r
200         char psz[100];\r
201         strlcpy(psz, pszIn, sizeof(psz));\r
202         unsigned int a=0, b=0, c=0, d=0, e=0;\r
203         if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)\r
204             return false;\r
205         char* pszPort = strchr(psz, ':');\r
206         if (pszPort)\r
207         {\r
208             *pszPort++ = '\0';\r
209             port = htons(atoi(pszPort));\r
210             if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)\r
211                 port = htons(USHRT_MAX);\r
212         }\r
213         ip = inet_addr(psz);\r
214         return IsValid();\r
215     }\r
216 \r
217     bool SetAddress(string strIn)\r
218     {\r
219         return SetAddress(strIn.c_str());\r
220     }\r
221 \r
222     IMPLEMENT_SERIALIZE\r
223     (\r
224         if (nType & SER_DISK)\r
225         {\r
226             READWRITE(nVersion);\r
227             READWRITE(nTime);\r
228         }\r
229         READWRITE(nServices);\r
230         READWRITE(FLATDATA(pchReserved)); // for IPv6\r
231         READWRITE(ip);\r
232         READWRITE(port);\r
233     )\r
234 \r
235     friend inline bool operator==(const CAddress& a, const CAddress& b)\r
236     {\r
237         return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 &&\r
238                 a.ip   == b.ip &&\r
239                 a.port == b.port);\r
240     }\r
241 \r
242     friend inline bool operator!=(const CAddress& a, const CAddress& b)\r
243     {\r
244         return (!(a == b));\r
245     }\r
246 \r
247     friend inline bool operator<(const CAddress& a, const CAddress& b)\r
248     {\r
249         int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved));\r
250         if (ret < 0)\r
251             return true;\r
252         else if (ret == 0)\r
253         {\r
254             if (ntohl(a.ip) < ntohl(b.ip))\r
255                 return true;\r
256             else if (a.ip == b.ip)\r
257                 return ntohs(a.port) < ntohs(b.port);\r
258         }\r
259         return false;\r
260     }\r
261 \r
262     vector<unsigned char> GetKey() const\r
263     {\r
264         CDataStream ss;\r
265         ss.reserve(18);\r
266         ss << FLATDATA(pchReserved) << ip << port;\r
267 \r
268         #if defined(_MSC_VER) && _MSC_VER < 1300\r
269         return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]);\r
270         #else\r
271         return vector<unsigned char>(ss.begin(), ss.end());\r
272         #endif\r
273     }\r
274 \r
275     struct sockaddr_in GetSockAddr() const\r
276     {\r
277         struct sockaddr_in sockaddr;\r
278         memset(&sockaddr, 0, sizeof(sockaddr));\r
279         sockaddr.sin_family = AF_INET;\r
280         sockaddr.sin_addr.s_addr = ip;\r
281         sockaddr.sin_port = port;\r
282         return sockaddr;\r
283     }\r
284 \r
285     bool IsIPv4() const\r
286     {\r
287         return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0);\r
288     }\r
289 \r
290     bool IsRoutable() const\r
291     {\r
292         return IsValid() &&\r
293             !(GetByte(3) == 10 ||\r
294               (GetByte(3) == 192 && GetByte(2) == 168) ||\r
295               GetByte(3) == 127 ||\r
296               GetByte(3) == 0);\r
297     }\r
298 \r
299     bool IsValid() const\r
300     {\r
301         // Clean up 3-byte shifted addresses caused by garbage in size field\r
302         // of addr messages from versions before 0.2.9 checksum.\r
303         // Two consecutive addr messages look like this:\r
304         // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...\r
305         // so if the first length field is garbled, it reads the second batch\r
306         // of addr misaligned by 3 bytes.\r
307         if (memcmp(pchReserved, pchIPv4+3, sizeof(pchIPv4)-3) == 0)\r
308             return false;\r
309 \r
310         return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX));\r
311     }\r
312 \r
313     unsigned char GetByte(int n) const\r
314     {\r
315         return ((unsigned char*)&ip)[3-n];\r
316     }\r
317 \r
318     string ToStringIPPort() const\r
319     {\r
320         return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
321     }\r
322 \r
323     string ToStringIP() const\r
324     {\r
325         return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));\r
326     }\r
327 \r
328     string ToStringPort() const\r
329     {\r
330         return strprintf("%u", ntohs(port));\r
331     }\r
332 \r
333     string ToStringLog() const\r
334     {\r
335         return "";\r
336     }\r
337 \r
338     string ToString() const\r
339     {\r
340         return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
341     }\r
342 \r
343     void print() const\r
344     {\r
345         printf("CAddress(%s)\n", ToString().c_str());\r
346     }\r
347 };\r
348 \r
349 \r
350 \r
351 \r
352 \r
353 \r
354 \r
355 enum\r
356 {\r
357     MSG_TX = 1,\r
358     MSG_BLOCK,\r
359 };\r
360 \r
361 static const char* ppszTypeName[] =\r
362 {\r
363     "ERROR",\r
364     "tx",\r
365     "block",\r
366 };\r
367 \r
368 class CInv\r
369 {\r
370 public:\r
371     int type;\r
372     uint256 hash;\r
373 \r
374     CInv()\r
375     {\r
376         type = 0;\r
377         hash = 0;\r
378     }\r
379 \r
380     CInv(int typeIn, const uint256& hashIn)\r
381     {\r
382         type = typeIn;\r
383         hash = hashIn;\r
384     }\r
385 \r
386     CInv(const string& strType, const uint256& hashIn)\r
387     {\r
388         int i;\r
389         for (i = 1; i < ARRAYLEN(ppszTypeName); i++)\r
390         {\r
391             if (strType == ppszTypeName[i])\r
392             {\r
393                 type = i;\r
394                 break;\r
395             }\r
396         }\r
397         if (i == ARRAYLEN(ppszTypeName))\r
398             throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str()));\r
399         hash = hashIn;\r
400     }\r
401 \r
402     IMPLEMENT_SERIALIZE\r
403     (\r
404         READWRITE(type);\r
405         READWRITE(hash);\r
406     )\r
407 \r
408     friend inline bool operator<(const CInv& a, const CInv& b)\r
409     {\r
410         return (a.type < b.type || (a.type == b.type && a.hash < b.hash));\r
411     }\r
412 \r
413     bool IsKnownType() const\r
414     {\r
415         return (type >= 1 && type < ARRAYLEN(ppszTypeName));\r
416     }\r
417 \r
418     const char* GetCommand() const\r
419     {\r
420         if (!IsKnownType())\r
421             throw std::out_of_range(strprintf("CInv::GetCommand() : type=% unknown type", type));\r
422         return ppszTypeName[type];\r
423     }\r
424 \r
425     string ToString() const\r
426     {\r
427         return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,16).c_str());\r
428     }\r
429 \r
430     void print() const\r
431     {\r
432         printf("CInv(%s)\n", ToString().c_str());\r
433     }\r
434 };\r
435 \r
436 \r
437 \r
438 \r
439 \r
440 class CRequestTracker\r
441 {\r
442 public:\r
443     void (*fn)(void*, CDataStream&);\r
444     void* param1;\r
445 \r
446     explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)\r
447     {\r
448         fn = fnIn;\r
449         param1 = param1In;\r
450     }\r
451 \r
452     bool IsNull()\r
453     {\r
454         return fn == NULL;\r
455     }\r
456 };\r
457 \r
458 \r
459 \r
460 \r
461 \r
462 extern bool fClient;\r
463 extern uint64 nLocalServices;\r
464 extern CAddress addrLocalHost;\r
465 extern CNode* pnodeLocalHost;\r
466 extern uint64 nLocalHostNonce;\r
467 extern array<int, 10> vnThreadsRunning;\r
468 extern SOCKET hListenSocket;\r
469 extern int64 nThreadSocketHandlerHeartbeat;\r
470 \r
471 extern vector<CNode*> vNodes;\r
472 extern CCriticalSection cs_vNodes;\r
473 extern map<vector<unsigned char>, CAddress> mapAddresses;\r
474 extern CCriticalSection cs_mapAddresses;\r
475 extern map<CInv, CDataStream> mapRelay;\r
476 extern deque<pair<int64, CInv> > vRelayExpiration;\r
477 extern CCriticalSection cs_mapRelay;\r
478 extern map<CInv, int64> mapAlreadyAskedFor;\r
479 \r
480 // Settings\r
481 extern int fUseProxy;\r
482 extern CAddress addrProxy;\r
483 \r
484 \r
485 \r
486 \r
487 \r
488 \r
489 class CNode\r
490 {\r
491 public:\r
492     // socket\r
493     uint64 nServices;\r
494     SOCKET hSocket;\r
495     CDataStream vSend;\r
496     CDataStream vRecv;\r
497     CCriticalSection cs_vSend;\r
498     CCriticalSection cs_vRecv;\r
499     int64 nLastSend;\r
500     int64 nLastRecv;\r
501     int64 nLastSendEmpty;\r
502     int64 nTimeConnected;\r
503     unsigned int nHeaderStart;\r
504     unsigned int nMessageStart;\r
505     CAddress addr;\r
506     int nVersion;\r
507     bool fClient;\r
508     bool fInbound;\r
509     bool fNetworkNode;\r
510     bool fSuccessfullyConnected;\r
511     bool fDisconnect;\r
512 protected:\r
513     int nRefCount;\r
514 public:\r
515     int64 nReleaseTime;\r
516     map<uint256, CRequestTracker> mapRequests;\r
517     CCriticalSection cs_mapRequests;\r
518     uint256 hashContinue;\r
519     CBlockIndex* pindexLastGetBlocksBegin;\r
520     uint256 hashLastGetBlocksEnd;\r
521     int nStartingHeight;\r
522 \r
523     // flood\r
524     vector<CAddress> vAddrToSend;\r
525     set<CAddress> setAddrKnown;\r
526     bool fGetAddr;\r
527 \r
528     // inventory based relay\r
529     set<CInv> setInventoryKnown;\r
530     vector<CInv> vInventoryToSend;\r
531     CCriticalSection cs_inventory;\r
532     multimap<int64, CInv> mapAskFor;\r
533 \r
534     // publish and subscription\r
535     vector<char> vfSubscribe;\r
536 \r
537 \r
538     CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)\r
539     {\r
540         nServices = 0;\r
541         hSocket = hSocketIn;\r
542         vSend.SetType(SER_NETWORK);\r
543         vSend.SetVersion(0);\r
544         vRecv.SetType(SER_NETWORK);\r
545         vRecv.SetVersion(0);\r
546         // Version 0.2 obsoletes 20 Feb 2012\r
547         if (GetTime() > 1329696000)\r
548         {\r
549             vSend.SetVersion(209);\r
550             vRecv.SetVersion(209);\r
551         }\r
552         nLastSend = 0;\r
553         nLastRecv = 0;\r
554         nLastSendEmpty = GetTime();\r
555         nTimeConnected = GetTime();\r
556         nHeaderStart = -1;\r
557         nMessageStart = -1;\r
558         addr = addrIn;\r
559         nVersion = 0;\r
560         fClient = false; // set by version message\r
561         fInbound = fInboundIn;\r
562         fNetworkNode = false;\r
563         fSuccessfullyConnected = false;\r
564         fDisconnect = false;\r
565         nRefCount = 0;\r
566         nReleaseTime = 0;\r
567         hashContinue = 0;\r
568         pindexLastGetBlocksBegin = 0;\r
569         hashLastGetBlocksEnd = 0;\r
570         nStartingHeight = -1;\r
571         fGetAddr = false;\r
572         vfSubscribe.assign(256, false);\r
573 \r
574         // Push a version message\r
575         /// when NTP implemented, change to just nTime = GetAdjustedTime()\r
576         int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());\r
577         CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);\r
578         CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);\r
579         RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));\r
580         PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe,\r
581                     nLocalHostNonce, string(pszSubVer), nBestHeight);\r
582     }\r
583 \r
584     ~CNode()\r
585     {\r
586         if (hSocket != INVALID_SOCKET)\r
587         {\r
588             closesocket(hSocket);\r
589             hSocket = INVALID_SOCKET;\r
590         }\r
591     }\r
592 \r
593 private:\r
594     CNode(const CNode&);\r
595     void operator=(const CNode&);\r
596 public:\r
597 \r
598 \r
599     int GetRefCount()\r
600     {\r
601         return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);\r
602     }\r
603 \r
604     CNode* AddRef(int64 nTimeout=0)\r
605     {\r
606         if (nTimeout != 0)\r
607             nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);\r
608         else\r
609             nRefCount++;\r
610         return this;\r
611     }\r
612 \r
613     void Release()\r
614     {\r
615         nRefCount--;\r
616     }\r
617 \r
618 \r
619 \r
620     void AddAddressKnown(const CAddress& addr)\r
621     {\r
622         setAddrKnown.insert(addr);\r
623     }\r
624 \r
625     void PushAddress(const CAddress& addr)\r
626     {\r
627         // Known checking here is only to save space from duplicates.\r
628         // SendMessages will filter it again for knowns that were added\r
629         // after addresses were pushed.\r
630         if (addr.IsValid() && !setAddrKnown.count(addr))\r
631             vAddrToSend.push_back(addr);\r
632     }\r
633 \r
634 \r
635     void AddInventoryKnown(const CInv& inv)\r
636     {\r
637         CRITICAL_BLOCK(cs_inventory)\r
638             setInventoryKnown.insert(inv);\r
639     }\r
640 \r
641     void PushInventory(const CInv& inv)\r
642     {\r
643         CRITICAL_BLOCK(cs_inventory)\r
644             if (!setInventoryKnown.count(inv))\r
645                 vInventoryToSend.push_back(inv);\r
646     }\r
647 \r
648     void AskFor(const CInv& inv)\r
649     {\r
650         // We're using mapAskFor as a priority queue,\r
651         // the key is the earliest time the request can be sent\r
652         int64& nRequestTime = mapAlreadyAskedFor[inv];\r
653         printf("askfor %s   %"PRI64d"\n", inv.ToString().c_str(), nRequestTime);\r
654 \r
655         // Make sure not to reuse time indexes to keep things in the same order\r
656         int64 nNow = (GetTime() - 1) * 1000000;\r
657         static int64 nLastTime;\r
658         nLastTime = nNow = max(nNow, ++nLastTime);\r
659 \r
660         // Each retry is 2 minutes after the last\r
661         nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow);\r
662         mapAskFor.insert(make_pair(nRequestTime, inv));\r
663     }\r
664 \r
665 \r
666 \r
667     void BeginMessage(const char* pszCommand)\r
668     {\r
669         cs_vSend.Enter();\r
670         if (nHeaderStart != -1)\r
671             AbortMessage();\r
672         nHeaderStart = vSend.size();\r
673         vSend << CMessageHeader(pszCommand, 0);\r
674         nMessageStart = vSend.size();\r
675         if (fDebug)\r
676             printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
677         printf("sending: %s ", pszCommand);\r
678     }\r
679 \r
680     void AbortMessage()\r
681     {\r
682         if (nHeaderStart == -1)\r
683             return;\r
684         vSend.resize(nHeaderStart);\r
685         nHeaderStart = -1;\r
686         nMessageStart = -1;\r
687         cs_vSend.Leave();\r
688         printf("(aborted)\n");\r
689     }\r
690 \r
691     void EndMessage()\r
692     {\r
693         if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)\r
694         {\r
695             printf("dropmessages DROPPING SEND MESSAGE\n");\r
696             AbortMessage();\r
697             return;\r
698         }\r
699 \r
700         if (nHeaderStart == -1)\r
701             return;\r
702 \r
703         // Set the size\r
704         unsigned int nSize = vSend.size() - nMessageStart;\r
705         memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));\r
706 \r
707         // Set the checksum\r
708         if (vSend.GetVersion() >= 209)\r
709         {\r
710             uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());\r
711             unsigned int nChecksum = 0;\r
712             memcpy(&nChecksum, &hash, sizeof(nChecksum));\r
713             assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum));\r
714             memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum));\r
715         }\r
716 \r
717         printf("(%d bytes) ", nSize);\r
718         printf("\n");\r
719 \r
720         nHeaderStart = -1;\r
721         nMessageStart = -1;\r
722         cs_vSend.Leave();\r
723     }\r
724 \r
725     void EndMessageAbortIfEmpty()\r
726     {\r
727         if (nHeaderStart == -1)\r
728             return;\r
729         int nSize = vSend.size() - nMessageStart;\r
730         if (nSize > 0)\r
731             EndMessage();\r
732         else\r
733             AbortMessage();\r
734     }\r
735 \r
736     const char* GetMessageCommand() const\r
737     {\r
738         if (nHeaderStart == -1)\r
739             return "";\r
740         return &vSend[nHeaderStart] + offsetof(CMessageHeader, pchCommand);\r
741     }\r
742 \r
743 \r
744 \r
745 \r
746     void PushMessage(const char* pszCommand)\r
747     {\r
748         try\r
749         {\r
750             BeginMessage(pszCommand);\r
751             EndMessage();\r
752         }\r
753         catch (...)\r
754         {\r
755             AbortMessage();\r
756             throw;\r
757         }\r
758     }\r
759 \r
760     template<typename T1>\r
761     void PushMessage(const char* pszCommand, const T1& a1)\r
762     {\r
763         try\r
764         {\r
765             BeginMessage(pszCommand);\r
766             vSend << a1;\r
767             EndMessage();\r
768         }\r
769         catch (...)\r
770         {\r
771             AbortMessage();\r
772             throw;\r
773         }\r
774     }\r
775 \r
776     template<typename T1, typename T2>\r
777     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)\r
778     {\r
779         try\r
780         {\r
781             BeginMessage(pszCommand);\r
782             vSend << a1 << a2;\r
783             EndMessage();\r
784         }\r
785         catch (...)\r
786         {\r
787             AbortMessage();\r
788             throw;\r
789         }\r
790     }\r
791 \r
792     template<typename T1, typename T2, typename T3>\r
793     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)\r
794     {\r
795         try\r
796         {\r
797             BeginMessage(pszCommand);\r
798             vSend << a1 << a2 << a3;\r
799             EndMessage();\r
800         }\r
801         catch (...)\r
802         {\r
803             AbortMessage();\r
804             throw;\r
805         }\r
806     }\r
807 \r
808     template<typename T1, typename T2, typename T3, typename T4>\r
809     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)\r
810     {\r
811         try\r
812         {\r
813             BeginMessage(pszCommand);\r
814             vSend << a1 << a2 << a3 << a4;\r
815             EndMessage();\r
816         }\r
817         catch (...)\r
818         {\r
819             AbortMessage();\r
820             throw;\r
821         }\r
822     }\r
823 \r
824     template<typename T1, typename T2, typename T3, typename T4, typename T5>\r
825     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)\r
826     {\r
827         try\r
828         {\r
829             BeginMessage(pszCommand);\r
830             vSend << a1 << a2 << a3 << a4 << a5;\r
831             EndMessage();\r
832         }\r
833         catch (...)\r
834         {\r
835             AbortMessage();\r
836             throw;\r
837         }\r
838     }\r
839 \r
840     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>\r
841     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)\r
842     {\r
843         try\r
844         {\r
845             BeginMessage(pszCommand);\r
846             vSend << a1 << a2 << a3 << a4 << a5 << a6;\r
847             EndMessage();\r
848         }\r
849         catch (...)\r
850         {\r
851             AbortMessage();\r
852             throw;\r
853         }\r
854     }\r
855 \r
856     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>\r
857     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7)\r
858     {\r
859         try\r
860         {\r
861             BeginMessage(pszCommand);\r
862             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;\r
863             EndMessage();\r
864         }\r
865         catch (...)\r
866         {\r
867             AbortMessage();\r
868             throw;\r
869         }\r
870     }\r
871 \r
872     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>\r
873     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8)\r
874     {\r
875         try\r
876         {\r
877             BeginMessage(pszCommand);\r
878             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;\r
879             EndMessage();\r
880         }\r
881         catch (...)\r
882         {\r
883             AbortMessage();\r
884             throw;\r
885         }\r
886     }\r
887 \r
888     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>\r
889     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9)\r
890     {\r
891         try\r
892         {\r
893             BeginMessage(pszCommand);\r
894             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;\r
895             EndMessage();\r
896         }\r
897         catch (...)\r
898         {\r
899             AbortMessage();\r
900             throw;\r
901         }\r
902     }\r
903 \r
904 \r
905     void PushRequest(const char* pszCommand,\r
906                      void (*fn)(void*, CDataStream&), void* param1)\r
907     {\r
908         uint256 hashReply;\r
909         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
910 \r
911         CRITICAL_BLOCK(cs_mapRequests)\r
912             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
913 \r
914         PushMessage(pszCommand, hashReply);\r
915     }\r
916 \r
917     template<typename T1>\r
918     void PushRequest(const char* pszCommand, const T1& a1,\r
919                      void (*fn)(void*, CDataStream&), void* param1)\r
920     {\r
921         uint256 hashReply;\r
922         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
923 \r
924         CRITICAL_BLOCK(cs_mapRequests)\r
925             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
926 \r
927         PushMessage(pszCommand, hashReply, a1);\r
928     }\r
929 \r
930     template<typename T1, typename T2>\r
931     void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,\r
932                      void (*fn)(void*, CDataStream&), void* param1)\r
933     {\r
934         uint256 hashReply;\r
935         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
936 \r
937         CRITICAL_BLOCK(cs_mapRequests)\r
938             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
939 \r
940         PushMessage(pszCommand, hashReply, a1, a2);\r
941     }\r
942 \r
943 \r
944 \r
945     void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);\r
946     bool IsSubscribed(unsigned int nChannel);\r
947     void Subscribe(unsigned int nChannel, unsigned int nHops=0);\r
948     void CancelSubscribe(unsigned int nChannel);\r
949     void CloseSocketDisconnect();\r
950     void Cleanup();\r
951 };\r
952 \r
953 \r
954 \r
955 \r
956 \r
957 \r
958 \r
959 \r
960 \r
961 \r
962 inline void RelayInventory(const CInv& inv)\r
963 {\r
964     // Put on lists to offer to the other nodes\r
965     CRITICAL_BLOCK(cs_vNodes)\r
966         foreach(CNode* pnode, vNodes)\r
967             pnode->PushInventory(inv);\r
968 }\r
969 \r
970 template<typename T>\r
971 void RelayMessage(const CInv& inv, const T& a)\r
972 {\r
973     CDataStream ss(SER_NETWORK);\r
974     ss.reserve(10000);\r
975     ss << a;\r
976     RelayMessage(inv, ss);\r
977 }\r
978 \r
979 template<>\r
980 inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)\r
981 {\r
982     CRITICAL_BLOCK(cs_mapRelay)\r
983     {\r
984         // Expire old relay messages\r
985         while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())\r
986         {\r
987             mapRelay.erase(vRelayExpiration.front().second);\r
988             vRelayExpiration.pop_front();\r
989         }\r
990 \r
991         // Save original serialized message so newer versions are preserved\r
992         mapRelay[inv] = ss;\r
993         vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));\r
994     }\r
995 \r
996     RelayInventory(inv);\r
997 }\r
998 \r
999 \r
1000 \r
1001 \r
1002 \r
1003 \r
1004 \r
1005 \r
1006 //\r
1007 // Templates for the publish and subscription system.\r
1008 // The object being published as T& obj needs to have:\r
1009 //   a set<unsigned int> setSources member\r
1010 //   specializations of AdvertInsert and AdvertErase\r
1011 // Currently implemented for CTable and CProduct.\r
1012 //\r
1013 \r
1014 template<typename T>\r
1015 void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
1016 {\r
1017     // Add to sources\r
1018     obj.setSources.insert(pfrom->addr.ip);\r
1019 \r
1020     if (!AdvertInsert(obj))\r
1021         return;\r
1022 \r
1023     // Relay\r
1024     CRITICAL_BLOCK(cs_vNodes)\r
1025         foreach(CNode* pnode, vNodes)\r
1026             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
1027                 pnode->PushMessage("publish", nChannel, nHops, obj);\r
1028 }\r
1029 \r
1030 template<typename T>\r
1031 void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
1032 {\r
1033     uint256 hash = obj.GetHash();\r
1034 \r
1035     CRITICAL_BLOCK(cs_vNodes)\r
1036         foreach(CNode* pnode, vNodes)\r
1037             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
1038                 pnode->PushMessage("pub-cancel", nChannel, nHops, hash);\r
1039 \r
1040     AdvertErase(obj);\r
1041 }\r
1042 \r
1043 template<typename T>\r
1044 void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
1045 {\r
1046     // Remove a source\r
1047     obj.setSources.erase(pfrom->addr.ip);\r
1048 \r
1049     // If no longer supported by any sources, cancel it\r
1050     if (obj.setSources.empty())\r
1051         AdvertStopPublish(pfrom, nChannel, nHops, obj);\r
1052 }\r