Network stack refactor
[novacoin.git] / src / netbase.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "netbase.h"
7 #include "util.h"
8
9 #ifndef WIN32
10 #include <sys/fcntl.h>
11 #endif
12
13 #include "strlcpy.h"
14
15 using namespace std;
16
17 // Settings
18 int fUseProxy = false;
19 CService addrProxy("127.0.0.1",9050);
20 int nConnectTimeout = 5000;
21
22
23 static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
24
25 bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, int nMaxSolutions, bool fAllowLookup)
26 {
27     vIP.clear();
28     struct addrinfo aiHint = {};
29     aiHint.ai_socktype = SOCK_STREAM;
30     aiHint.ai_protocol = IPPROTO_TCP;
31 #ifdef WIN32
32 #  ifdef USE_IPV6
33     aiHint.ai_family = AF_UNSPEC;
34     aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
35 #  else
36     aiHint.ai_family = AF_INET;
37     aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
38 #  endif
39 #else
40 #  ifdef USE_IPV6
41     aiHint.ai_family = AF_UNSPEC;
42     aiHint.ai_flags = AI_ADDRCONFIG | (fAllowLookup ? 0 : AI_NUMERICHOST);
43 #  else
44     aiHint.ai_family = AF_INET;
45     aiHint.ai_flags = AI_ADDRCONFIG | (fAllowLookup ? 0 : AI_NUMERICHOST);
46 #  endif
47 #endif
48     struct addrinfo *aiRes = NULL;
49     int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
50     if (nErr)
51         return false;
52
53     struct addrinfo *aiTrav = aiRes;
54     while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
55     {
56         if (aiTrav->ai_family == AF_INET)
57         {
58             assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
59             vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr));
60         }
61
62 #ifdef USE_IPV6
63         if (aiTrav->ai_family == AF_INET6)
64         {
65             assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
66             vIP.push_back(CNetAddr(((struct sockaddr_in6*)(aiTrav->ai_addr))->sin6_addr));
67         }
68 #endif
69
70         aiTrav = aiTrav->ai_next;
71     }
72
73     freeaddrinfo(aiRes);
74
75     return (vIP.size() > 0);
76 }
77
78 bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, int nMaxSolutions, bool fAllowLookup)
79 {
80     if (pszName[0] == 0)
81         return false;
82     char psz[256];
83     char *pszHost = psz;
84     strlcpy(psz, pszName, sizeof(psz));
85     if (psz[0] == '[' && psz[strlen(psz)-1] == ']')
86     {
87         pszHost = psz+1;
88         psz[strlen(psz)-1] = 0;
89     }
90
91     return LookupIntern(pszHost, vIP, nMaxSolutions, fAllowLookup);
92 }
93
94 bool LookupHostNumeric(const char *pszName, std::vector<CNetAddr>& vIP, int nMaxSolutions)
95 {
96     return LookupHost(pszName, vIP, nMaxSolutions, false);
97 }
98
99 bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup)
100 {
101     if (pszName[0] == 0)
102         return false;
103     int port = portDefault;
104     char psz[256];
105     char *pszHost = psz;
106     strlcpy(psz, pszName, sizeof(psz));
107     char* pszColon = strrchr(psz+1,':');
108     char *pszPortEnd = NULL;
109     int portParsed = pszColon ? strtoul(pszColon+1, &pszPortEnd, 10) : 0;
110     if (pszColon && pszPortEnd && pszPortEnd[0] == 0)
111     {
112         if (psz[0] == '[' && pszColon[-1] == ']')
113         {
114             pszHost = psz+1;
115             pszColon[-1] = 0;
116         }
117         else
118             pszColon[0] = 0;
119         if (port >= 0 && port <= USHRT_MAX)
120             port = portParsed;
121     }
122     else
123     {
124         if (psz[0] == '[' && psz[strlen(psz)-1] == ']')
125         {
126             pszHost = psz+1;
127             psz[strlen(psz)-1] = 0;
128         }
129
130     }
131
132     std::vector<CNetAddr> vIP;
133     bool fRet = LookupIntern(pszHost, vIP, 1, fAllowLookup);
134     if (!fRet)
135         return false;
136     addr = CService(vIP[0], port);
137     return true;
138 }
139
140 bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
141 {
142     return Lookup(pszName, addr, portDefault, false);
143 }
144
145 bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout)
146 {
147     hSocketRet = INVALID_SOCKET;
148
149     SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
150     if (hSocket == INVALID_SOCKET)
151         return false;
152 #ifdef SO_NOSIGPIPE
153     int set = 1;
154     setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
155 #endif
156
157     bool fProxy = (fUseProxy && addrDest.IsRoutable());
158     struct sockaddr_in sockaddr;
159     if (fProxy)
160         addrProxy.GetSockAddr(&sockaddr);
161     else
162         addrDest.GetSockAddr(&sockaddr);
163
164 #ifdef WIN32
165     u_long fNonblock = 1;
166     if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
167 #else
168     int fFlags = fcntl(hSocket, F_GETFL, 0);
169     if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1)
170 #endif
171     {
172         closesocket(hSocket);
173         return false;
174     }
175
176
177     if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
178     {
179         // WSAEINVAL is here because some legacy version of winsock uses it
180         if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL)
181         {
182             struct timeval timeout;
183             timeout.tv_sec  = nTimeout / 1000;
184             timeout.tv_usec = (nTimeout % 1000) * 1000;
185
186             fd_set fdset;
187             FD_ZERO(&fdset);
188             FD_SET(hSocket, &fdset);
189             int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout);
190             if (nRet == 0)
191             {
192                 printf("connection timeout\n");
193                 closesocket(hSocket);
194                 return false;
195             }
196             if (nRet == SOCKET_ERROR)
197             {
198                 printf("select() for connection failed: %i\n",WSAGetLastError());
199                 closesocket(hSocket);
200                 return false;
201             }
202             socklen_t nRetSize = sizeof(nRet);
203 #ifdef WIN32
204             if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR)
205 #else
206             if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR)
207 #endif
208             {
209                 printf("getsockopt() for connection failed: %i\n",WSAGetLastError());
210                 closesocket(hSocket);
211                 return false;
212             }
213             if (nRet != 0)
214             {
215                 printf("connect() failed after select(): %s\n",strerror(nRet));
216                 closesocket(hSocket);
217                 return false;
218             }
219         }
220 #ifdef WIN32
221         else if (WSAGetLastError() != WSAEISCONN)
222 #else
223         else
224 #endif
225         {
226             printf("connect() failed: %i\n",WSAGetLastError());
227             closesocket(hSocket);
228             return false;
229         }
230     }
231
232     // this isn't even strictly necessary
233     // CNode::ConnectNode immediately turns the socket back to non-blocking
234     // but we'll turn it back to blocking just in case
235 #ifdef WIN32
236     fNonblock = 0;
237     if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
238 #else
239     fFlags = fcntl(hSocket, F_GETFL, 0);
240     if (fcntl(hSocket, F_SETFL, fFlags & !O_NONBLOCK) == SOCKET_ERROR)
241 #endif
242     {
243         closesocket(hSocket);
244         return false;
245     }
246
247     if (fProxy)
248     {
249         printf("proxy connecting %s\n", addrDest.ToString().c_str());
250         char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user";
251         struct sockaddr_in addr;
252         addrDest.GetSockAddr(&addr);
253         memcpy(pszSocks4IP + 2, &addr.sin_port, 2);
254         memcpy(pszSocks4IP + 4, &addr.sin_addr, 4);
255         char* pszSocks4 = pszSocks4IP;
256         int nSize = sizeof(pszSocks4IP);
257
258         int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL);
259         if (ret != nSize)
260         {
261             closesocket(hSocket);
262             return error("Error sending to proxy");
263         }
264         char pchRet[8];
265         if (recv(hSocket, pchRet, 8, 0) != 8)
266         {
267             closesocket(hSocket);
268             return error("Error reading proxy response");
269         }
270         if (pchRet[1] != 0x5a)
271         {
272             closesocket(hSocket);
273             if (pchRet[1] != 0x5b)
274                 printf("ERROR: Proxy returned error %d\n", pchRet[1]);
275             return false;
276         }
277         printf("proxy connected %s\n", addrDest.ToString().c_str());
278     }
279
280     hSocketRet = hSocket;
281     return true;
282 }
283
284 void CNetAddr::Init()
285 {
286     memset(ip, 0, 16);
287 }
288
289 void CNetAddr::SetIP(const CNetAddr& ipIn)
290 {
291     memcpy(ip, ipIn.ip, sizeof(ip));
292 }
293
294 CNetAddr::CNetAddr()
295 {
296     Init();
297 }
298
299 CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
300 {
301     memcpy(ip,    pchIPv4, 12);
302     memcpy(ip+12, &ipv4Addr, 4);
303 }
304
305 #ifdef USE_IPV6
306 CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr)
307 {
308     memcpy(ip, &ipv6Addr, 16);
309 }
310 #endif
311
312 CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup)
313 {
314     Init();
315     std::vector<CNetAddr> vIP;
316     if (LookupHost(pszIp, vIP, 1, fAllowLookup))
317         *this = vIP[0];
318 }
319
320 CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup)
321 {
322     Init();
323     std::vector<CNetAddr> vIP;
324     if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup))
325         *this = vIP[0];
326 }
327
328 int CNetAddr::GetByte(int n) const
329 {
330     return ip[15-n];
331 }
332
333 bool CNetAddr::IsIPv4() const
334 {
335     return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
336 }
337
338 bool CNetAddr::IsRFC1918() const
339 {
340     return IsIPv4() && (
341         GetByte(3) == 10 || 
342         (GetByte(3) == 192 && GetByte(2) == 168) || 
343         (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31)));
344 }
345
346 bool CNetAddr::IsRFC3927() const
347 {
348     return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254);
349 }
350
351 bool CNetAddr::IsRFC3849() const
352 {
353     return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8;
354 }
355
356 bool CNetAddr::IsRFC3964() const
357 {
358     return (GetByte(15) == 0x20 && GetByte(14) == 0x02);
359 }
360
361 bool CNetAddr::IsRFC6052() const
362 {
363     static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
364     return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0);
365 }
366
367 bool CNetAddr::IsRFC4380() const
368 {
369     return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0);
370 }
371
372 bool CNetAddr::IsRFC4862() const
373 {
374     static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
375     return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0);
376 }
377
378 bool CNetAddr::IsRFC4193() const
379 {
380     return ((GetByte(15) & 0xFE) == 0xFC);
381 }
382
383 bool CNetAddr::IsRFC6145() const
384 {
385     static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
386     return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0);
387 }
388
389 bool CNetAddr::IsRFC4843() const
390 {
391     return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && GetByte(12) & 0xF0 == 0x10);
392 }
393
394 bool CNetAddr::IsLocal() const
395 {
396     // IPv4 loopback
397    if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
398        return true;
399
400    // IPv6 loopback (::1/128)
401    static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
402    if (memcmp(ip, pchLocal, 16) == 0)
403        return true;
404
405    return false;
406 }
407
408 bool CNetAddr::IsMulticast() const
409 {
410     return    (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0)
411            || (GetByte(15) == 0xFF);
412 }
413
414 bool CNetAddr::IsValid() const
415 {
416     // Clean up 3-byte shifted addresses caused by garbage in size field
417     // of addr messages from versions before 0.2.9 checksum.
418     // Two consecutive addr messages look like this:
419     // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
420     // so if the first length field is garbled, it reads the second batch
421     // of addr misaligned by 3 bytes.
422     if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
423         return false;
424
425     // unspecified IPv6 address (::/128)
426     unsigned char ipNone[16] = {};
427     if (memcmp(ip, ipNone, 16) == 0)
428         return false;
429
430     // documentation IPv6 address
431     if (IsRFC3849())
432         return false;
433
434     if (IsIPv4())
435     {
436         // INADDR_NONE
437         uint32_t ipNone = INADDR_NONE;
438         if (memcmp(ip+12, &ipNone, 4) == 0)
439             return false;
440
441         // 0
442         ipNone = 0;
443         if (memcmp(ip+12, &ipNone, 4) == 0)
444             return false;
445     }
446
447     return true;
448 }
449
450 bool CNetAddr::IsRoutable() const
451 {
452     return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || IsRFC4193() || IsRFC4843() || IsLocal());
453 }
454
455 std::string CNetAddr::ToStringIP() const
456 {
457     if (IsIPv4())
458         return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
459     else
460         return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
461                          GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12),
462                          GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8),
463                          GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4),
464                          GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0));
465 }
466
467 std::string CNetAddr::ToString() const
468 {
469     return ToStringIP();
470 }
471
472 bool operator==(const CNetAddr& a, const CNetAddr& b)
473 {
474     return (memcmp(a.ip, b.ip, 16) == 0);
475 }
476
477 bool operator!=(const CNetAddr& a, const CNetAddr& b)
478 {
479     return (memcmp(a.ip, b.ip, 16) != 0);
480 }
481
482 bool operator<(const CNetAddr& a, const CNetAddr& b)
483 {
484     return (memcmp(a.ip, b.ip, 16) < 0);
485 }
486
487 bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
488 {
489     if (!IsIPv4())
490         return false;
491     memcpy(pipv4Addr, ip+12, 4);
492     return true;
493 }
494
495 #ifdef USE_IPV6
496 bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
497 {
498     memcpy(pipv6Addr, ip, 16);
499     return true;
500 }
501 #endif
502
503 // get canonical identifier of an address' group
504 // no two connections will be attempted to addresses with the same group
505 std::vector<unsigned char> CNetAddr::GetGroup() const
506 {
507     std::vector<unsigned char> vchRet;
508     int nClass = 0; // 0=IPv6, 1=IPv4, 255=unroutable
509     int nStartByte = 0;
510     int nBits = 16;
511
512     // for unroutable addresses, each address is considered different
513     if (!IsRoutable())
514     {
515         nClass = 255;
516         nBits = 128;
517     }
518     // for IPv4 addresses, '1' + the 16 higher-order bits of the IP
519     // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix
520     else if (IsIPv4() || IsRFC6145() || IsRFC6052())
521     {
522         nClass = 1;
523         nStartByte = 12;
524     }
525     // for 6to4 tunneled addresses, use the encapsulated IPv4 address
526     else if (IsRFC3964())
527     {
528         nClass = 1;
529         nStartByte = 2;
530     }
531     // for Teredo-tunneled IPv6 addresses, use the encapsulated IPv4 address
532     else if (IsRFC4380())
533     {
534         vchRet.push_back(1);
535         vchRet.push_back(GetByte(3) ^ 0xFF);
536         vchRet.push_back(GetByte(2) ^ 0xFF);
537         return vchRet;
538     }
539     // for he.net, use /36 groups
540     else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
541         nBits = 36;
542     // for the rest of the IPv6 network, use /32 groups
543     else
544         nBits = 32;
545
546     vchRet.push_back(nClass);
547     while (nBits >= 8)
548     {
549         vchRet.push_back(GetByte(15 - nStartByte));
550         nStartByte++;
551         nBits -= 8;
552     }
553     if (nBits > 0)
554         vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1));
555
556     return vchRet;
557 }
558
559 int64 CNetAddr::GetHash() const
560 {
561     uint256 hash = Hash(&ip[0], &ip[16]);
562     int64 nRet;
563     memcpy(&nRet, &hash, sizeof(nRet));
564     return nRet;
565 }
566
567 void CNetAddr::print() const
568 {
569     printf("CNetAddr(%s)\n", ToString().c_str());
570 }
571
572 void CService::Init()
573 {
574     port = 0;
575 }
576
577 CService::CService()
578 {
579     Init();
580 }
581
582 CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn)
583 {
584 }
585
586 CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn)
587 {
588 }
589
590 #ifdef USE_IPV6
591 CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn)
592 {
593 }
594 #endif
595
596 CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port))
597 {
598     assert(addr.sin_family == AF_INET);
599 }
600
601 #ifdef USE_IPV6
602 CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port))
603 {
604    assert(addr.sin6_family == AF_INET6);
605 }
606 #endif
607
608 CService::CService(const char *pszIpPort, bool fAllowLookup)
609 {
610     Init();
611     CService ip;
612     if (Lookup(pszIpPort, ip, 0, fAllowLookup))
613         *this = ip;
614 }
615
616 CService::CService(const char *pszIp, int portIn, bool fAllowLookup)
617 {
618     std::vector<CNetAddr> ip;
619     if (LookupHost(pszIp, ip, 1, fAllowLookup))
620         *this = CService(ip[0], portIn);
621 }
622
623 CService::CService(const std::string &strIpPort, bool fAllowLookup)
624 {
625     Init();
626     CService ip;
627     if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup))
628         *this = ip;
629 }
630
631 CService::CService(const std::string &strIp, int portIn, bool fAllowLookup)
632 {
633     std::vector<CNetAddr> ip;
634     if (LookupHost(strIp.c_str(), ip, 1, fAllowLookup))
635         *this = CService(ip[0], portIn);
636 }
637
638 unsigned short CService::GetPort() const
639 {
640     return port;
641 }
642
643 bool operator==(const CService& a, const CService& b)
644 {
645     return (CNetAddr)a == (CNetAddr)b && a.port == b.port;
646 }
647
648 bool operator!=(const CService& a, const CService& b)
649 {
650     return (CNetAddr)a != (CNetAddr)b || a.port != b.port;
651 }
652
653 bool operator<(const CService& a, const CService& b)
654 {
655     return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port);
656 }
657
658 bool CService::GetSockAddr(struct sockaddr_in* paddr) const
659 {
660     if (!IsIPv4())
661         return false;
662     memset(paddr, 0, sizeof(struct sockaddr_in));
663     if (!GetInAddr(&paddr->sin_addr))
664         return false;
665     paddr->sin_family = AF_INET;
666     paddr->sin_port = htons(port);
667     return true;
668 }
669
670 #ifdef USE_IPV6
671 bool CService::GetSockAddr6(struct sockaddr_in6* paddr) const
672 {
673     memset(paddr, 0, sizeof(struct sockaddr_in6));
674     if (!GetIn6Addr(&paddr->sin6_addr))
675         return false;
676     paddr->sin6_family = AF_INET6;
677     paddr->sin6_port = htons(port);
678     return true;
679 }
680 #endif
681
682 std::vector<unsigned char> CService::GetKey() const
683 {
684      std::vector<unsigned char> vKey;
685      vKey.resize(18);
686      memcpy(&vKey[0], ip, 16);
687      vKey[16] = port / 0x100;
688      vKey[17] = port & 0x0FF;
689      return vKey;
690 }
691
692 std::string CService::ToStringPort() const
693 {
694     return strprintf(":%i", port);
695 }
696
697 std::string CService::ToStringIPPort() const
698 {
699     return ToStringIP() + ToStringPort();
700 }
701
702 std::string CService::ToString() const
703 {
704     return ToStringIPPort();
705 }
706
707 void CService::print() const
708 {
709     printf("CService(%s)\n", ToString().c_str());
710 }
711
712 void CService::SetPort(unsigned short portIn)
713 {
714     port = portIn;
715 }