1//
2// DNS.cpp
3//
4// Library: Net
5// Package: NetCore
6// Module: DNS
7//
8// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/DNS.h"
16#include "Poco/Net/NetException.h"
17#include "Poco/Net/SocketAddress.h"
18#include "Poco/Environment.h"
19#include "Poco/NumberFormatter.h"
20#include "Poco/RWLock.h"
21#include "Poco/TextIterator.h"
22#include "Poco/TextConverter.h"
23#include "Poco/UTF8Encoding.h"
24#include "Poco/UTF32Encoding.h"
25#include "Poco/Unicode.h"
26#include <cstring>
27
28
29#if defined(POCO_HAVE_LIBRESOLV)
30#include <resolv.h>
31#endif
32
33
34using Poco::Environment;
35using Poco::NumberFormatter;
36using Poco::IOException;
37
38
39namespace Poco {
40namespace Net {
41
42
43typedef Poco::UInt32 punycode_uint;
44
45
46enum
47{
48 punycode_success = 0,
49 punycode_overflow = -1,
50 punycode_big_output = -2,
51 punycode_bad_input = -3
52};
53
54
55static int punycode_encode(size_t input_length, const punycode_uint input[], size_t* output_length, char output[]);
56static int punycode_decode(size_t input_length, const char input[], size_t* output_length, punycode_uint output[]);
57
58
59#if defined(POCO_HAVE_LIBRESOLV)
60static Poco::RWLock resolverLock;
61#endif
62
63
64HostEntry DNS::hostByName(const std::string& hostname, unsigned
65#ifdef POCO_HAVE_ADDRINFO
66 hintFlags
67#endif
68 )
69{
70#if defined(POCO_HAVE_LIBRESOLV)
71 Poco::ScopedReadRWLock readLock(resolverLock);
72#endif
73
74#if defined(POCO_HAVE_ADDRINFO)
75 struct addrinfo* pAI;
76 struct addrinfo hints;
77 std::memset(&hints, 0, sizeof(hints));
78 hints.ai_flags = hintFlags;
79 int rc = getaddrinfo(hostname.c_str(), NULL, &hints, &pAI);
80 if (rc == 0)
81 {
82 HostEntry result(pAI);
83 freeaddrinfo(pAI);
84 return result;
85 }
86 else
87 {
88 aierror(rc, hostname);
89 }
90#elif defined(POCO_VXWORKS)
91 int addr = hostGetByName(const_cast<char*>(hostname.c_str()));
92 if (addr != ERROR)
93 {
94 return HostEntry(hostname, IPAddress(&addr, sizeof(addr)));
95 }
96#else
97 struct hostent* he = gethostbyname(hostname.c_str());
98 if (he)
99 {
100 return HostEntry(he);
101 }
102#endif
103 error(lastError(), hostname); // will throw an appropriate exception
104 throw NetException(); // to silence compiler
105}
106
107
108HostEntry DNS::hostByAddress(const IPAddress& address, unsigned
109#ifdef POCO_HAVE_ADDRINFO
110 hintFlags
111#endif
112 )
113{
114#if defined(POCO_HAVE_LIBRESOLV)
115 Poco::ScopedReadRWLock readLock(resolverLock);
116#endif
117
118#if defined(POCO_HAVE_ADDRINFO)
119 SocketAddress sa(address, 0);
120 static char fqname[1024];
121 int rc = getnameinfo(sa.addr(), sa.length(), fqname, sizeof(fqname), NULL, 0, NI_NAMEREQD);
122 if (rc == 0)
123 {
124 struct addrinfo* pAI;
125 struct addrinfo hints;
126 std::memset(&hints, 0, sizeof(hints));
127 hints.ai_flags = hintFlags;
128 rc = getaddrinfo(fqname, NULL, &hints, &pAI);
129 if (rc == 0)
130 {
131 HostEntry result(pAI);
132 freeaddrinfo(pAI);
133 return result;
134 }
135 else
136 {
137 aierror(rc, address.toString());
138 }
139 }
140 else
141 {
142 aierror(rc, address.toString());
143 }
144#elif defined(POCO_VXWORKS)
145 char name[MAXHOSTNAMELEN + 1];
146 if (hostGetByAddr(*reinterpret_cast<const int*>(address.addr()), name) == OK)
147 {
148 return HostEntry(std::string(name), address);
149 }
150#else
151 struct hostent* he = gethostbyaddr(reinterpret_cast<const char*>(address.addr()), address.length(), address.af());
152 if (he)
153 {
154 return HostEntry(he);
155 }
156#endif
157 int err = lastError();
158 error(err, address.toString()); // will throw an appropriate exception
159 throw NetException(); // to silence compiler
160}
161
162
163HostEntry DNS::resolve(const std::string& address)
164{
165 IPAddress ip;
166 if (IPAddress::tryParse(address, ip))
167 {
168 return hostByAddress(ip);
169 }
170 else if (isIDN(address))
171 {
172 std::string encoded = encodeIDN(address);
173 return hostByName(encoded);
174 }
175 else
176 {
177 return hostByName(address);
178 }
179}
180
181
182IPAddress DNS::resolveOne(const std::string& address)
183{
184 const HostEntry& entry = resolve(address);
185 if (!entry.addresses().empty())
186 return entry.addresses()[0];
187 else
188 throw NoAddressFoundException(address);
189}
190
191
192HostEntry DNS::thisHost()
193{
194 return hostByName(hostName());
195}
196
197
198void DNS::reload()
199{
200#if defined(POCO_HAVE_LIBRESOLV)
201 Poco::ScopedWriteRWLock writeLock(resolverLock);
202 res_init();
203#endif
204}
205
206
207std::string DNS::hostName()
208{
209 char buffer[256];
210 int rc = gethostname(buffer, sizeof(buffer));
211 if (rc == 0)
212 return std::string(buffer);
213 else
214 throw NetException("Cannot get host name");
215}
216
217
218bool DNS::isIDN(const std::string& hostname)
219{
220 for (std::string::const_iterator it = hostname.begin(); it != hostname.end(); ++it)
221 {
222 if (static_cast<unsigned char>(*it) >= 0x80) return true;
223 }
224 return false;
225}
226
227
228bool DNS::isEncodedIDN(const std::string& hostname)
229{
230 return hostname.compare(0, 4, "xn--") == 0 || hostname.find(".xn--") != std::string::npos;
231}
232
233
234std::string DNS::encodeIDN(const std::string& idn)
235{
236 std::string encoded;
237 std::string::const_iterator it = idn.begin();
238 std::string::const_iterator end = idn.end();
239 while (it != end)
240 {
241 std::string label;
242 bool mustEncode = false;
243 while (it != end && *it != '.')
244 {
245 if (static_cast<unsigned char>(*it) >= 0x80) mustEncode = true;
246 label += *it++;
247 }
248 if (mustEncode)
249 encoded += encodeIDNLabel(label);
250 else
251 encoded += label;
252 if (it != end) encoded += *it++;
253 }
254 return encoded;
255}
256
257
258std::string DNS::decodeIDN(const std::string& encodedIDN)
259{
260 std::string decoded;
261 std::string::const_iterator it = encodedIDN.begin();
262 std::string::const_iterator end = encodedIDN.end();
263 while (it != end)
264 {
265 std::string label;
266 while (it != end && *it != '.')
267 {
268 label += *it++;
269 }
270 decoded += decodeIDNLabel(label);
271 if (it != end) decoded += *it++;
272 }
273 return decoded;
274}
275
276
277std::string DNS::encodeIDNLabel(const std::string& label)
278{
279 std::string encoded = "xn--";
280 std::vector<Poco::UInt32> uniLabel;
281 Poco::UTF8Encoding utf8;
282 Poco::TextIterator it(label, utf8);
283 Poco::TextIterator end(label);
284 while (it != end)
285 {
286 int ch = *it;
287 if (ch < 0) throw DNSException("Invalid UTF-8 character in IDN label", label);
288 if (Poco::Unicode::isUpper(ch))
289 {
290 ch = Poco::Unicode::toLower(ch);
291 }
292 uniLabel.push_back(static_cast<Poco::UInt32>(ch));
293 ++it;
294 }
295 char buffer[64];
296 std::size_t size = 64;
297 int rc = punycode_encode(uniLabel.size(), &uniLabel[0], &size, buffer);
298 if (rc == punycode_success)
299 encoded.append(buffer, size);
300 else
301 throw DNSException("Failed to encode IDN label", label);
302 return encoded;
303}
304
305
306std::string DNS::decodeIDNLabel(const std::string& encodedIDN)
307{
308 std::string decoded;
309 if (encodedIDN.compare(0, 4, "xn--") == 0)
310 {
311 std::size_t size = 64;
312 punycode_uint buffer[64];
313 int rc = punycode_decode(encodedIDN.size() - 4, encodedIDN.data() + 4, &size, buffer);
314 if (rc == punycode_success)
315 {
316 Poco::UTF32Encoding utf32;
317 Poco::UTF8Encoding utf8;
318 Poco::TextConverter converter(utf32, utf8);
319 converter.convert(buffer, static_cast<int>(size*sizeof(punycode_uint)), decoded);
320 }
321 else throw DNSException("Failed to decode IDN label: ", encodedIDN);
322 }
323 else
324 {
325 decoded = encodedIDN;
326 }
327 return decoded;
328}
329
330
331int DNS::lastError()
332{
333#if defined(_WIN32)
334 return GetLastError();
335#elif defined(POCO_VXWORKS)
336 return errno;
337#else
338 return h_errno;
339#endif
340}
341
342
343void DNS::error(int code, const std::string& arg)
344{
345 switch (code)
346 {
347 case POCO_ESYSNOTREADY:
348 throw NetException("Net subsystem not ready");
349 case POCO_ENOTINIT:
350 throw NetException("Net subsystem not initialized");
351 case POCO_HOST_NOT_FOUND:
352 throw HostNotFoundException(arg);
353 case POCO_TRY_AGAIN:
354 throw DNSException("Temporary DNS error while resolving", arg);
355 case POCO_NO_RECOVERY:
356 throw DNSException("Non recoverable DNS error while resolving", arg);
357 case POCO_NO_DATA:
358 throw NoAddressFoundException(arg);
359 default:
360 throw IOException(NumberFormatter::format(code));
361 }
362}
363
364
365void DNS::aierror(int code, const std::string& arg)
366{
367#if defined(POCO_HAVE_IPv6) || defined(POCO_HAVE_ADDRINFO)
368 switch (code)
369 {
370 case EAI_AGAIN:
371 throw DNSException("Temporary DNS error while resolving", arg);
372 case EAI_FAIL:
373 throw DNSException("Non recoverable DNS error while resolving", arg);
374#if !defined(_WIN32) // EAI_NODATA and EAI_NONAME have the same value
375#if defined(EAI_NODATA) // deprecated in favor of EAI_NONAME on FreeBSD
376 case EAI_NODATA:
377 throw NoAddressFoundException(arg);
378#endif
379#endif
380 case EAI_NONAME:
381 throw HostNotFoundException(arg);
382#if defined(EAI_SYSTEM)
383 case EAI_SYSTEM:
384 error(lastError(), arg);
385 break;
386#endif
387#if defined(_WIN32)
388 case WSANO_DATA: // may happen on XP
389 throw HostNotFoundException(arg);
390#endif
391 default:
392 throw DNSException("EAI", NumberFormatter::format(code));
393 }
394#endif // POCO_HAVE_IPv6 || defined(POCO_HAVE_ADDRINFO)
395}
396
397
398/*
399 Code copied from http://www.nicemice.net/idn/punycode-spec.gz on
400 2018-02-17 with SHA-1 a966a8017f6be579d74a50a226accc7607c40133
401 labeled punycode-spec 1.0.3 (2006-Mar-23-Thu).
402
403 Modified for POCO C++ Libraries by Guenter Obiltschnig.
404
405 License on the original code:
406
407 punycode-spec 1.0.3 (2006-Mar-23-Thu)
408 http://www.nicemice.net/idn/
409 Adam M. Costello
410 http://www.nicemice.net/amc/
411
412 B. Disclaimer and license
413
414 Regarding this entire document or any portion of it (including
415 the pseudocode and C code), the author makes no guarantees and
416 is not responsible for any damage resulting from its use. The
417 author grants irrevocable permission to anyone to use, modify,
418 and distribute it in any way that does not diminish the rights
419 of anyone else to use, modify, and distribute it, provided that
420 redistributed derivative works do not contain misleading author or
421 version information. Derivative works need not be licensed under
422 similar terms.
423
424 C. Punycode sample implementation
425
426 punycode-sample.c 2.0.0 (2004-Mar-21-Sun)
427 http://www.nicemice.net/idn/
428 Adam M. Costello
429 http://www.nicemice.net/amc/
430
431 This is ANSI C code (C89) implementing Punycode 1.0.x.
432*/
433
434
435/*** Bootstring parameters for Punycode ***/
436
437enum
438{
439 base = 36,
440 tmin = 1,
441 tmax = 26,
442 skew = 38,
443 damp = 700,
444 initial_bias = 72,
445 initial_n = 0x80,
446 delimiter = 0x2D
447};
448
449/* basic(cp) tests whether cp is a basic code point: */
450#define basic(cp) ((punycode_uint)(cp) < 0x80)
451
452/* delim(cp) tests whether cp is a delimiter: */
453#define delim(cp) ((cp) == delimiter)
454
455/* encode_digit(d,flag) returns the basic code point whose value */
456/* (when used for representing integers) is d, which needs to be in */
457/* the range 0 to base-1. The lowercase form is used unless flag is */
458/* nonzero, in which case the uppercase form is used. The behavior */
459/* is undefined if flag is nonzero and digit d has no uppercase form. */
460
461static char encode_digit(punycode_uint d, int flag)
462{
463 return static_cast<char>(d + 22 + 75 * (d < 26) - ((flag != 0) << 5));
464 /* 0..25 map to ASCII a..z or A..Z */
465 /* 26..35 map to ASCII 0..9 */
466}
467
468/* decode_digit(cp) returns the numeric value of a basic code */
469/* point (for use in representing integers) in the range 0 to */
470/* base-1, or base if cp does not represent a value. */
471
472static unsigned decode_digit(int cp)
473{
474 return (unsigned) (cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 :
475 cp - 97 < 26 ? cp - 97 : base);
476}
477
478/*** Platform-specific constants ***/
479
480/* maxint is the maximum value of a punycode_uint variable: */
481static const punycode_uint maxint = punycode_uint (-1);
482/* Because maxint is unsigned, -1 becomes the maximum value. */
483
484/*** Bias adaptation function ***/
485
486static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, int firsttime);
487
488static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, int firsttime)
489{
490 punycode_uint k;
491
492 delta = firsttime ? delta / damp : delta >> 1;
493 /* delta >> 1 is a faster way of doing delta / 2 */
494 delta += delta / numpoints;
495
496 for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base)
497 {
498 delta /= base - tmin;
499 }
500
501 return k + (base - tmin + 1) * delta / (delta + skew);
502}
503
504/*** Main encode function ***/
505
506int punycode_encode(size_t input_length_orig, const punycode_uint input[], size_t *output_length, char output[])
507{
508 punycode_uint input_length, n, delta, h, b, bias, j, m, q, k, t;
509 size_t out, max_out;
510
511 /* The Punycode spec assumes that the input length is the same type */
512 /* of integer as a code point, so we need to convert the size_t to */
513 /* a punycode_uint, which could overflow. */
514
515 if (input_length_orig > maxint) return punycode_overflow;
516 input_length = (punycode_uint) input_length_orig;
517
518 /* Initialize the state: */
519
520 n = initial_n;
521 delta = 0;
522 out = 0;
523 max_out = *output_length;
524 bias = initial_bias;
525
526 /* Handle the basic code points: */
527
528 for (j = 0; j < input_length; ++j)
529 {
530 if (basic(input[j]))
531 {
532 if (max_out - out < 2) return punycode_big_output;
533 output[out++] = (char) input[j];
534 }
535 /* else if (input[j] < n) return punycode_bad_input; */
536 /* (not needed for Punycode with unsigned code points) */
537 }
538
539 h = b = (punycode_uint) out;
540 /* cannot overflow because out <= input_length <= maxint */
541
542 /* h is the number of code points that have been handled, b is the */
543 /* number of basic code points, and out is the number of ASCII code */
544 /* points that have been output. */
545
546 if (b > 0) output[out++] = delimiter;
547
548 /* Main encoding loop: */
549
550 while (h < input_length)
551 {
552 /* All non-basic code points < n have been */
553 /* handled already. Find the next larger one: */
554
555 for (m = maxint, j = 0; j < input_length; ++j)
556 {
557 /* if (basic(input[j])) continue; */
558 /* (not needed for Punycode) */
559 if (input[j] >= n && input[j] < m) m = input[j];
560 }
561
562 /* Increase delta enough to advance the decoder's */
563 /* <n,i> state to <m,0>, but guard against overflow: */
564
565 if (m - n > (maxint - delta) / (h + 1)) return punycode_overflow;
566 delta += (m - n) * (h + 1);
567 n = m;
568
569 for (j = 0; j < input_length; ++j)
570 {
571 /* Punycode does not need to check whether input[j] is basic: */
572 if (input[j] < n /* || basic(input[j]) */ )
573 {
574 if (++delta == 0) return punycode_overflow;
575 }
576
577 if (input[j] == n)
578 {
579 /* Represent delta as a generalized variable-length integer: */
580
581 for (q = delta, k = base; ; k += base)
582 {
583 if (out >= max_out) return punycode_big_output;
584 t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
585 k >= bias + tmax ? tmax : k - bias;
586 if (q < t) break;
587 output[out++] = encode_digit(t + (q - t) % (base - t), 0);
588 q = (q - t) / (base - t);
589 }
590
591 output[out++] = encode_digit(q, 0);
592 bias = adapt(delta, h + 1, h == b);
593 delta = 0;
594 ++h;
595 }
596 }
597
598 ++delta, ++n;
599 }
600
601 *output_length = out;
602 return punycode_success;
603}
604
605/*** Main decode function ***/
606
607int punycode_decode(size_t input_length, const char input[], size_t *output_length, punycode_uint output[])
608{
609 punycode_uint n, out, i, max_out, bias, oldi, w, k, digit, t;
610 size_t b, j, in;
611
612 /* Initialize the state: */
613
614 n = initial_n;
615 out = i = 0;
616 max_out = *output_length > maxint ? maxint : (punycode_uint) *output_length;
617 bias = initial_bias;
618
619 /* Handle the basic code points: Let b be the number of input code */
620 /* points before the last delimiter, or 0 if there is none, then */
621 /* copy the first b code points to the output. */
622
623 for (b = j = 0; j < input_length; ++j)
624 {
625 if (delim(input[j])) b = j;
626 }
627 if (b > max_out) return punycode_big_output;
628
629 for (j = 0; j < b; ++j)
630 {
631 if (!basic(input[j])) return punycode_bad_input;
632 output[out++] = input[j];
633 }
634
635 /* Main decoding loop: Start just after the last delimiter if any */
636 /* basic code points were copied; start at the beginning otherwise. */
637
638 for (in = b > 0 ? b + 1 : 0; in < input_length; ++out)
639 {
640 /* in is the index of the next ASCII code point to be consumed, */
641 /* and out is the number of code points in the output array. */
642
643 /* Decode a generalized variable-length integer into delta, */
644 /* which gets added to i. The overflow checking is easier */
645 /* if we increase i as we go, then subtract off its starting */
646 /* value at the end to obtain delta. */
647
648 for (oldi = i, w = 1, k = base; ; k += base)
649 {
650 if (in >= input_length) return punycode_bad_input;
651 digit = decode_digit(input[in++]);
652 if (digit >= base) return punycode_bad_input;
653 if (digit > (maxint - i) / w) return punycode_overflow;
654 i += digit * w;
655 t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
656 k >= bias + tmax ? tmax : k - bias;
657 if (digit < t) break;
658 if (w > maxint / (base - t)) return punycode_overflow;
659 w *= (base - t);
660 }
661
662 bias = adapt(i - oldi, out + 1, oldi == 0);
663
664 /* i was supposed to wrap around from out+1 to 0, */
665 /* incrementing n each time, so we'll fix that now: */
666
667 if (i / (out + 1) > maxint - n) return punycode_overflow;
668 n += i / (out + 1);
669 i %= (out + 1);
670
671 /* Insert n at position i of the output: */
672
673 /* not needed for Punycode: */
674 /* if (basic(n)) return punycode_bad_input; */
675 if (out >= max_out) return punycode_big_output;
676
677 std::memmove(output + i + 1, output + i, (out - i) * sizeof *output);
678 output[i++] = n;
679 }
680
681 *output_length = (size_t) out;
682 /* cannot overflow because out <= old value of *output_length */
683 return punycode_success;
684}
685
686
687} } // namespace Poco::Net
688