1 //=============================================================================
2 //
3 // File : KviNetUtils.cpp
4 // Creation date : Sun Jun 18 2000 18:37:27 by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #define _KVI_NETUTILS_CPP_
26
27 #include "KviNetUtils.h"
28 #include "KviCString.h"
29 #include "KviMemory.h"
30
31 #include <QString>
32 #include <QStringList>
33
34 #if !defined(COMPILE_ON_WINDOWS) && !defined(COMPILE_ON_MINGW)
35 #include <sys/time.h> // struct timeval
36 #include <unistd.h>
37 #include <netdb.h>
38 #endif
39
40 #ifdef COMPILE_ON_WINDOWS
41 #include <Ws2tcpip.h>
42 #endif
43
44 #include <sys/types.h>
45
46 #if defined(__SVR4) && defined(__sun)
47 #include <sys/sockio.h>
48 #endif
49
50 #ifdef COMPILE_GET_INTERFACE_ADDRESS
51 #include <sys/ioctl.h>
52 #include <net/if.h>
53 #endif //COMPILE_GET_INTERFACE_ADDRESS
54
55 #ifndef HAVE_INET_ATON
56
57 // FIXME: #warning "Your system lacks the inet_aton function,"
58 // FIXME: #warning "you're trying to compile this file without"
59 // FIXME: #warning "the kvi_sysconfig.h created by the cmake script,"
60 // FIXME: #warning "Using own internal implementation of inet_aton."
61
62 #include <ctype.h>
63
64 // Need own inet_aton implementation
65 //
66 // Check whether "cp" is a valid ascii representation
67 // of an Internet address and convert to a binary address.
68 // Returns 1 if the address is valid, 0 if not.
69 // This replaces inet_addr, the return value from which
70 // cannot distinguish between failure and a local broadcast address.
71 //
72 // Original code comes from the ircd source.
73 //
74
kvi_stringIpToBinaryIp(const char * szIp,struct in_addr * address)75 bool kvi_stringIpToBinaryIp(const char * szIp, struct in_addr * address)
76 {
77 unsigned long val;
78 int base, n;
79 char c;
80 unsigned int parts[4];
81 unsigned int * pp = parts;
82 if(!szIp)
83 return false;
84 c = *szIp;
85 for(;;)
86 {
87 // Collect number up to ``.''.
88 // Values are specified as for C:
89 // 0x=hex, 0=octal, isdigit=decimal.
90 if(!isdigit(c))
91 return false;
92 val = 0;
93 base = 10;
94
95 if(c == '0')
96 {
97 c = *++szIp;
98 if((c == 'x') || (c == 'X'))
99 base = 16, c = *++szIp;
100 else
101 base = 8;
102 }
103
104 for(;;)
105 {
106 if(isascii(c) && isdigit(c))
107 {
108 val = (val * base) + (c - '0');
109 c = *++szIp;
110 }
111 else if(base == 16 && isascii(c) && isxdigit(c))
112 {
113 val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
114 c = *++szIp;
115 }
116 else
117 break;
118 }
119
120 if(c == '.')
121 {
122 // Internet format:
123 // a.b.c.d
124 // a.b.c (with c treated as 16 bits)
125 // a.b (with b treated as 24 bits)
126
127 if(pp >= (parts + 3))
128 return false;
129 *pp++ = val;
130 c = *++szIp;
131 }
132 else
133 break;
134 }
135
136 // Check for trailing characters.
137 if((c != '\0') && (!isascii(c) || !isspace(c)))
138 return false;
139
140 // Concact the address according to
141 // the number of parts specified.
142 n = pp - parts + 1;
143
144 switch(n)
145 {
146 case 0:
147 return false; // initial nondigit
148 case 1:
149 break; // a -- 32 bits
150 case 2: // a.b -- 8.24 bits
151 if(val > 0xffffff)
152 return false;
153 val |= parts[0] << 24;
154 break;
155 case 3: // a.b.c -- 8.8.16 bits
156 if(val > 0xffff)
157 return false;
158 val |= (parts[0] << 24) | (parts[1] << 16);
159 break;
160 case 4: // a.b.c.d -- 8.8.8.8 bits
161 if(val > 0xff)
162 return false;
163 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
164 break;
165 }
166
167 if(address)
168 address->s_addr = htonl(val);
169 return true;
170 }
171
172 #else //!HAVE_INET_ATON
173
kvi_stringIpToBinaryIp(const char * szIp,struct in_addr * address)174 bool kvi_stringIpToBinaryIp(const char * szIp, struct in_addr * address)
175 {
176 if(!szIp)
177 return false;
178 return (inet_aton(szIp, address) != 0);
179 }
180 #endif //!HAVE_INET_ATON
181
182 #ifndef HAVE_INET_NTOA
183
184 // FIXME: #warning "Your system lacks the inet_ntoa function,"
185 // FIXME: #warning "you're trying to compile this file without"
186 // FIXME: #warning "the config.h created by the configure script,"
187 // FIXME: #warning "Using own internal implementation of inet_ntoa."
188
189 //
190 // Original code comes from the ircd source.
191 //
192
kvi_binaryIpToStringIp(struct in_addr in,QString & szBuffer)193 bool kvi_binaryIpToStringIp(struct in_addr in, QString & szBuffer)
194 {
195 unsigned char * s = (unsigned char *)∈
196 int a, b, c, d;
197 a = (int)*s++;
198 b = (int)*s++;
199 c = (int)*s++;
200 d = (int)*s;
201
202 szBuffer = QString("%1.%2.%3.%4").arg(a).arg(b).arg(c).arg(d);
203 return true;
204 }
205
206 #else //HAVE_INET_NTOA
207
kvi_binaryIpToStringIp(struct in_addr in,QString & szBuffer)208 bool kvi_binaryIpToStringIp(struct in_addr in, QString & szBuffer)
209 {
210 // FIXME: #warning "This is NOT thread safe!"
211
212 char * ptr = inet_ntoa(in);
213 if(!ptr)
214 return false;
215 szBuffer = ptr;
216 return true;
217 }
218
219 #endif //HAVE_INET_NTOA
220
kvi_isValidStringIp(const char * szIp)221 bool kvi_isValidStringIp(const char * szIp)
222 {
223 struct in_addr address;
224 if(!szIp)
225 return false;
226 if(!isdigit(*szIp))
227 return false;
228 return kvi_stringIpToBinaryIp(szIp, &address);
229 }
230
231 #ifdef COMPILE_IPV6_SUPPORT
232
kvi_binaryIpToStringIp_V6(struct in6_addr in,QString & szBuffer)233 bool kvi_binaryIpToStringIp_V6(struct in6_addr in, QString & szBuffer)
234 {
235 char buf[46];
236 bool bRet = inet_ntop(AF_INET6, (void *)&in, buf, 46);
237
238 szBuffer = buf;
239 return bRet;
240 }
241
242 #endif
243
244 #include <cerrno>
245
kvi_select(int fd,bool * bCanRead,bool * bCanWrite,int iUSecs)246 bool kvi_select(int fd, bool * bCanRead, bool * bCanWrite, int iUSecs)
247 {
248 // FIXME: This stuff should DIE!
249 fd_set rs;
250 fd_set ws;
251 FD_ZERO(&rs);
252 FD_ZERO(&ws);
253 FD_SET(fd, &rs);
254 FD_SET(fd, &ws);
255 struct timeval tv;
256
257 tv.tv_sec = 0;
258 tv.tv_usec = iUSecs;
259
260 int ret = select(fd + 1, &rs, &ws, nullptr, &tv);
261 if(ret < 1)
262 return false; // EINTR or ENOSTUFFATALL
263
264 *bCanRead = FD_ISSET(fd, &rs);
265 *bCanWrite = FD_ISSET(fd, &ws);
266
267 return true;
268 }
269
270 namespace KviNetUtils
271 {
stringIpToBinaryIp(const QString & szStringIp,struct in_addr * address)272 bool stringIpToBinaryIp(const QString & szStringIp, struct in_addr * address)
273 {
274 #ifndef HAVE_INET_ATON
275 QString szAddr = szStringIp.simplified();
276 quint32 iAddr = 0;
277 QStringList ipv4 = szAddr.split(".", QString::KeepEmptyParts, Qt::CaseInsensitive);
278 if(ipv4.count() == 4)
279 {
280 int i = 0;
281 bool ok = true;
282 while(ok && i < 4)
283 {
284 uint byteValue = ipv4[i].toUInt(&ok);
285 if((byteValue > 255) && ok)
286 ok = false;
287 if(ok)
288 iAddr = (iAddr << 8) + byteValue;
289 ++i;
290 }
291 if(ok)
292 {
293 if(address)
294 address->s_addr = htonl(iAddr);
295 return true;
296 }
297 }
298 return false;
299 #else //HAVE_INET_ATON
300 if(szStringIp.isEmpty())
301 return false;
302 return (inet_aton(szStringIp.toUtf8().data(), address) != 0);
303 #endif //HAVE_INET_ATON
304 }
305
isValidStringIp(const QString & szIp)306 bool isValidStringIp(const QString & szIp)
307 {
308 struct in_addr address;
309 if(szIp.isEmpty())
310 return false;
311 if(!szIp[0].isNumber())
312 return false;
313 return stringIpToBinaryIp(szIp, &address);
314 }
315
316 #ifdef COMPILE_IPV6_SUPPORT
stringIpToBinaryIp_V6(const QString & szStringIp,struct in6_addr * address)317 bool stringIpToBinaryIp_V6(const QString & szStringIp, struct in6_addr * address)
318 {
319 return (inet_pton(AF_INET6, szStringIp.toUtf8().data(), (void *)address) == 1);
320 }
321
isValidStringIPv6(const QString & szIp)322 bool isValidStringIPv6(const QString & szIp)
323 {
324 struct in6_addr address;
325 if(szIp.isEmpty())
326 return false;
327 return stringIpToBinaryIp_V6(szIp, &address);
328 }
329
binaryIpToStringIp_V6(struct in6_addr in,QString & szBuffer)330 bool binaryIpToStringIp_V6(struct in6_addr in, QString & szBuffer)
331 {
332 char buf[46];
333 bool bRet = inet_ntop(AF_INET6, (void *)&in, buf, 46);
334 szBuffer = buf;
335 return bRet;
336 }
337
338 #endif //COMPILE_IPV6_SUPPORT
339
binaryIpToStringIp(struct in_addr in,QString & szBuffer)340 bool binaryIpToStringIp(struct in_addr in, QString & szBuffer)
341 {
342 char * ptr = inet_ntoa(in);
343 if(!ptr)
344 return false;
345 szBuffer = ptr;
346 return true;
347 }
348
isRoutableIpString(const QString & szIpString)349 bool isRoutableIpString(const QString & szIpString)
350 {
351 struct in_addr a;
352 if(szIpString.isEmpty())
353 return false;
354 stringIpToBinaryIp(szIpString, &a);
355 return isRoutableIp((const char *)&a);
356 }
357
isRoutableIp(const char * ipaddr)358 bool isRoutableIp(const char * ipaddr)
359 {
360 if(!ipaddr)
361 return false;
362 const unsigned char * ip = (const unsigned char *)ipaddr;
363 if(ip[0] == 0)
364 return false; // old-style broadcast
365 if(ip[0] == 10)
366 return false; // Class A VPN
367 if(ip[0] == 127)
368 return false; // loopback
369 if((ip[0] == 172) && (ip[1] >= 16) && (ip[1] <= 31))
370 return false; // Class B VPN
371 if((ip[0] == 192) && (ip[1] == 168))
372 return false; // Class C VPN
373 if((ip[0] == 169) && (ip[1] == 254))
374 return false; // APIPA
375 if((ip[0] == 192) && (ip[1] == 0) && (ip[2] == 2))
376 return false; // Class B VPN
377 if(ip[0] >= 224)
378 return false; // class D multicast and class E reserved
379
380 return true;
381 }
382
383 #ifdef COMPILE_GET_INTERFACE_ADDRESS
384 union sockaddr_union {
385 struct sockaddr sa;
386 struct sockaddr_in sin;
387 };
388
getInterfaceAddress(const QString & szInterfaceName,QString & szBuffer)389 bool getInterfaceAddress(const QString & szInterfaceName, QString & szBuffer)
390 {
391 union sockaddr_union * su;
392 struct ifreq ifr;
393 int len = szInterfaceName.length();
394 if(len > (IFNAMSIZ - 1))
395 return false; // invalid interface anyway
396
397 KviMemory::move(ifr.ifr_name, szInterfaceName.toUtf8().data(), len + 1);
398
399 int fd = socket(AF_INET, SOCK_STREAM, 0);
400 if(fd < 0)
401 return false;
402
403 if(ioctl(fd, SIOCGIFADDR, &ifr) == -1)
404 return false; // supports only IPV4 ?
405
406 close(fd);
407
408 su = (union sockaddr_union *)&(ifr.ifr_addr);
409
410 if(su->sa.sa_family != AF_INET)
411 return false;
412
413 return binaryIpToStringIp((struct in_addr)su->sin.sin_addr, szBuffer);
414
415 // (this seems to work for AF_INET only anyway)
416 #else //!COMPILE_GET_INTERFACE_ADDRESS
417 bool getInterfaceAddress(const QString &, QString &)
418 {
419 return false;
420 #endif //!COMPILE_GET_INTERFACE_ADDRESS
421 }
422
423 void formatNetworkBandwidthString(QString & szBuffer, unsigned int uBytesPerSec)
424 {
425 if(uBytesPerSec > (1024 * 1024))
426 {
427 unsigned int uMB = uBytesPerSec / (1024 * 1024);
428 unsigned int uRem = ((uBytesPerSec % (1024 * 1024)) * 100) / (1024 * 1024);
429
430 szBuffer = QString("%1.%2%3 MiB/s").arg(uMB).arg(uRem / 10).arg(uRem % 10);
431
432 return;
433 }
434
435 if(uBytesPerSec >= 1024)
436 {
437 unsigned int uKB = uBytesPerSec / 1024;
438 unsigned int uRem = ((uBytesPerSec % 1024) * 100) / 1024;
439
440 szBuffer = QString("%1.%2%3 KiB/s").arg(uKB).arg(uRem / 10).arg(uRem % 10);
441
442 return;
443 }
444
445 szBuffer = QString("%1 B/s").arg(uBytesPerSec);
446 }
447 }
448
kvi_isRoutableIpString(const char * ipstring)449 bool kvi_isRoutableIpString(const char * ipstring)
450 {
451 struct in_addr a;
452 if(!ipstring)
453 return false;
454 kvi_stringIpToBinaryIp(ipstring, &a);
455 return kvi_isRoutableIp((const char *)&a);
456 }
457
kvi_isRoutableIp(const char * ipaddr)458 bool kvi_isRoutableIp(const char * ipaddr)
459 {
460 if(!ipaddr)
461 return false;
462 const unsigned char * ip = (const unsigned char *)ipaddr;
463
464 if(ip[0] == 0)
465 return false; // old-style broadcast
466 if(ip[0] == 10)
467 return false; // Class A VPN
468 if(ip[0] == 127)
469 return false; // loopback
470 if((ip[0] == 172) && (ip[1] >= 16) && (ip[1] <= 31))
471 return false; // Class B VPN
472 if((ip[0] == 192) && (ip[1] == 168))
473 return false; // Class C VPN
474 if((ip[0] == 169) && (ip[1] == 254))
475 return false; // APIPA
476 if((ip[0] == 192) && (ip[1] == 0) && (ip[2] == 2))
477 return false; // Class B VPN
478 if(ip[0] >= 224)
479 return false; // class D multicast and class E reserved
480
481 return true;
482 }
483
kvi_getLocalHostAddress(QString & buffer)484 bool kvi_getLocalHostAddress(QString & buffer)
485 {
486 // This will work only on windoze...
487 char buf[1024];
488 if(gethostname(buf, 1024) != 0)
489 return false;
490 struct hostent * h = gethostbyname(buf);
491 if(!h)
492 return false;
493 QString tmp;
494 int i = 0;
495 while(h->h_addr_list[i])
496 {
497 if(kvi_binaryIpToStringIp(*((struct in_addr *)(h->h_addr_list[i])), tmp))
498 {
499 if(kvi_isRoutableIp(h->h_addr_list[i]))
500 {
501 buffer = tmp;
502 return true;
503 }
504 }
505 i++;
506 }
507
508 buffer = tmp;
509 return true;
510 }
511
KviSockaddr(const char * szIpAddress,kvi_u32_t uPort,bool bIPv6,bool bUdp)512 KviSockaddr::KviSockaddr(const char * szIpAddress, kvi_u32_t uPort, bool bIPv6, bool bUdp)
513 {
514 struct addrinfo hints;
515 KviMemory::set((void *)&hints, 0, sizeof(hints));
516 hints.ai_flags = AI_NUMERICHOST;
517 #ifdef COMPILE_IPV6_SUPPORT
518 hints.ai_family = bIPv6 ? PF_INET6 : PF_INET;
519 #else
520 hints.ai_family = PF_INET;
521 #endif
522
523 hints.ai_socktype = bUdp ? SOCK_DGRAM : SOCK_STREAM;
524
525 hints.ai_protocol = 0;
526 m_pData = nullptr;
527 KviCString szPort(KviCString::Format, "%u", uPort);
528 getaddrinfo(szIpAddress, szPort.ptr(), &hints, &m_pData);
529 }
530
KviSockaddr(kvi_u32_t uPort,bool bIPv6,bool bUdp)531 KviSockaddr::KviSockaddr(kvi_u32_t uPort, bool bIPv6, bool bUdp) // passive sockaddr
532
533 {
534 struct addrinfo hints;
535 KviMemory::set((void *)&hints, 0, sizeof(hints));
536 hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
537 #ifdef COMPILE_IPV6_SUPPORT
538 hints.ai_family = bIPv6 ? PF_INET6 : PF_INET;
539 #else
540 hints.ai_family = PF_INET;
541 #endif
542 hints.ai_socktype = bUdp ? SOCK_DGRAM : SOCK_STREAM;
543 hints.ai_protocol = 0;
544 m_pData = nullptr;
545 KviCString szPort(KviCString::Format, "%u", uPort);
546 getaddrinfo(nullptr, szPort.ptr(), &hints, &m_pData);
547 }
548
~KviSockaddr()549 KviSockaddr::~KviSockaddr()
550 {
551 if(m_pData)
552 {
553 freeaddrinfo(m_pData);
554 m_pData = nullptr;
555 }
556 }
557
socketAddress()558 struct sockaddr * KviSockaddr::socketAddress()
559 {
560 if(!m_pData)
561 return nullptr;
562 return (m_pData)->ai_addr;
563 }
564
addressLength()565 size_t KviSockaddr::addressLength()
566 {
567 if(!m_pData)
568 return 0;
569 return (m_pData)->ai_addrlen;
570 }
571
addressFamily()572 int KviSockaddr::addressFamily()
573 {
574 if(!m_pData)
575 return 0;
576 return (m_pData)->ai_family;
577 }
578
isIPv6()579 bool KviSockaddr::isIPv6()
580 {
581 if(!m_pData)
582 return false;
583 #ifdef COMPILE_IPV6_SUPPORT
584 return false;
585 #else
586 return (addressFamily() == AF_INET6);
587 #endif
588 }
589
port()590 kvi_u32_t KviSockaddr::port()
591 {
592 if(!m_pData)
593 return 0;
594 #ifdef COMPILE_IPV6_SUPPORT
595 switch(m_pData->ai_family)
596 {
597 case AF_INET:
598 return ntohs(((struct sockaddr_in *)(m_pData->ai_addr))->sin_port);
599 break;
600 case AF_INET6:
601 return ntohs(((struct sockaddr_in6 *)(m_pData->ai_addr))->sin6_port);
602 break;
603 }
604 return 0;
605 #else
606 return ntohs(((struct sockaddr_in *)(m_pData->ai_addr))->sin_port);
607 #endif
608 }
609
getStringAddress(QString & szBuffer)610 bool KviSockaddr::getStringAddress(QString & szBuffer)
611 {
612 if(!m_pData)
613 return false;
614 #ifdef COMPILE_IPV6_SUPPORT
615 switch(((struct addrinfo *)m_pData)->ai_family)
616 {
617 case AF_INET:
618 return kvi_binaryIpToStringIp(((struct sockaddr_in *)(m_pData->ai_addr))->sin_addr, szBuffer);
619 break;
620 case AF_INET6:
621 return kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)(m_pData->ai_addr))->sin6_addr, szBuffer);
622 break;
623 }
624
625 return false;
626 #else
627 return kvi_binaryIpToStringIp(((struct sockaddr_in *)(m_pData->ai_addr))->sin_addr, szBuffer);
628 #endif
629 }
630