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 *)&in;
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