1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "sysinfo.h"
22 
23 #include "comms.h"
24 #include "log.h"
25 #include "cfg.h"
26 
27 #include "dns.h"
28 #include "zbxalgo.h"
29 
30 #if defined(_WINDOWS) || defined(__MINGW32__)
31 #	include <windns.h>
32 #	pragma comment(lib, "Dnsapi.lib") /* add the library for DnsQuery function */
33 #endif
34 
35 #if defined(HAVE_RES_QUERY) || defined(_WINDOWS) || defined(__MINGW32__)
36 
decode_type(int q_type)37 static const char	*decode_type(int q_type)
38 {
39 	static ZBX_THREAD_LOCAL char	buf[16];
40 
41 	switch (q_type)
42 	{
43 		case T_A:
44 			return "A";	/* address */
45 		case T_AAAA:
46 			return "AAAA";	/* v6 address */
47 		case T_NS:
48 			return "NS";	/* name server */
49 		case T_MD:
50 			return "MD";	/* mail destination */		/* obsolete */
51 		case T_MF:
52 			return "MF";	/* mail forwarder */		/* obsolete */
53 		case T_CNAME:
54 			return "CNAME";	/* canonical name */
55 		case T_SOA:
56 			return "SOA";	/* start of authority */
57 		case T_MB:
58 			return "MB";	/* mailbox */			/* experimental */
59 		case T_MG:
60 			return "MG";	/* mail group member */		/* experimental */
61 		case T_MR:
62 			return "MR";	/* mail rename */		/* experimental */
63 		case T_NULL:
64 			return "NULL";	/* null */			/* obsolete */
65 		case T_WKS:
66 			return "WKS";	/* well-known service */	/* obsolete */
67 		case T_PTR:
68 			return "PTR";	/* domain name pointer */
69 		case T_HINFO:
70 			return "HINFO";	/* host information */
71 		case T_MINFO:
72 			return "MINFO";	/* mailbox information */	/* experimental */
73 		case T_MX:
74 			return "MX";	/* mail exchanger */
75 		case T_TXT:
76 			return "TXT";	/* text */
77 		case T_SRV:
78 			return "SRV";	/* service locator */
79 		default:
80 			zbx_snprintf(buf, sizeof(buf), "T_%d", q_type);
81 			return buf;
82 	}
83 }
84 
85 #if !defined(_WINDOWS) && !defined(__MINGW32__)
get_name(unsigned char * msg,unsigned char * msg_end,unsigned char ** msg_ptr)86 static char	*get_name(unsigned char *msg, unsigned char *msg_end, unsigned char **msg_ptr)
87 {
88 	int		res;
89 	static char	buffer[MAX_STRING_LEN];
90 
91 	if (-1 == (res = dn_expand(msg, msg_end, *msg_ptr, buffer, sizeof(buffer))))
92 		return NULL;
93 
94 	*msg_ptr += res;
95 
96 	return buffer;
97 }
98 #endif	/* !defined(_WINDOWS) && !defined(__MINGW32__)*/
99 
100 /* Replace zbx_inet_ntop with inet_ntop in case of drop Windows XP/W2k3 support */
101 #if defined(_WINDOWS) || defined(__MINGW32__)
zbx_inet_ntop(int af,const void * src,char * dst,size_t size)102 const char *zbx_inet_ntop(int af, const void *src, char *dst, size_t size)
103 {
104 	struct sockaddr_storage ss;
105 	unsigned long s = size;
106 
107 	memset(&ss, '\0', sizeof(ss));
108 	ss.ss_family = af;
109 
110 	switch(af)
111 	{
112 		case AF_INET:
113 			((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
114 			break;
115 		case AF_INET6:
116 			((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
117 			break;
118 		default:
119 			return NULL;
120 	}
121 
122 	return (0 == WSAAddressToStringA((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s))? dst : NULL;
123 }
124 #endif
125 #endif	/* defined(HAVE_RES_QUERY) || defined(_WINDOWS) || defined(__MINGW32__) */
126 
dns_query(AGENT_REQUEST * request,AGENT_RESULT * result,int short_answer)127 static int	dns_query(AGENT_REQUEST *request, AGENT_RESULT *result, int short_answer)
128 {
129 #if defined(HAVE_RES_QUERY) || defined(_WINDOWS) || defined(__MINGW32__)
130 
131 	size_t			offset = 0;
132 	int			res, type, retrans, retry, use_tcp, i, ret = SYSINFO_RET_FAIL, ip_type = AF_INET;
133 	char			*ip, zone[MAX_STRING_LEN], buffer[MAX_STRING_LEN], *zone_str, *param,
134 				tmp[MAX_STRING_LEN];
135 	struct in_addr		inaddr;
136 	struct in6_addr		in6addr;
137 #if !defined(_WINDOWS) && !defined(__MINGW32__)
138 #if defined(HAVE_RES_NINIT) && !defined(_AIX)
139 	/* It seems that on some AIX systems with no updates installed res_ninit() can */
140 	/* corrupt stack (see ZBX-14559). Use res_init() on AIX. */
141 	struct __res_state	res_state_local;
142 #else	/* thread-unsafe resolver API */
143 	int			saved_retrans, saved_retry, saved_nscount = 0;
144 	unsigned long		saved_options;
145 	struct sockaddr_in	saved_ns;
146 #	if defined(HAVE_RES_U_EXT)		/* thread-unsafe resolver API /Linux/ */
147 	int			save_nssocks, saved_nscount6;
148 #	endif
149 #endif
150 
151 #if defined(HAVE_RES_EXT_EXT)		/* AIX */
152 	union res_sockaddr_union	saved_ns6;
153 #elif defined(HAVE_RES_U_EXT_EXT)	/* BSD */
154 	struct sockaddr_in6		saved_ns6;
155 #else
156 	struct sockaddr_in6		*saved_ns6;
157 #endif
158 	struct sockaddr_in6	sockaddrin6;
159 	struct addrinfo		hint, *hres = NULL;
160 #endif
161 	typedef struct
162 	{
163 		const char	*name;
164 		int		type;
165 	}
166 	resolv_querytype_t;
167 
168 	static const resolv_querytype_t	qt[] =
169 	{
170 		{"ANY",		T_ANY},
171 		{"A",		T_A},
172 		{"AAAA",	T_AAAA},
173 		{"NS",		T_NS},
174 		{"MD",		T_MD},
175 		{"MF",		T_MF},
176 		{"CNAME",	T_CNAME},
177 		{"SOA",		T_SOA},
178 		{"MB",		T_MB},
179 		{"MG",		T_MG},
180 		{"MR",		T_MR},
181 		{"NULL",	T_NULL},
182 #if !defined(_WINDOWS) && !defined(__MINGW32__)
183 		{"WKS",		T_WKS},
184 #endif
185 		{"PTR",		T_PTR},
186 		{"HINFO",	T_HINFO},
187 		{"MINFO",	T_MINFO},
188 		{"MX",		T_MX},
189 		{"TXT",		T_TXT},
190 		{"SRV",		T_SRV},
191 		{NULL}
192 	};
193 
194 #if defined(_WINDOWS) || defined(__MINGW32__)
195 	PDNS_RECORD	pQueryResults, pDnsRecord;
196 	wchar_t		*wzone;
197 	char		tmp2[MAX_STRING_LEN];
198 	DWORD		options;
199 #else
200 	char		*name;
201 	unsigned char	*msg_end, *msg_ptr, *p;
202 	int		num_answers, num_query, q_type, q_class, q_len, value, c, n;
203 	struct servent	*s;
204 	HEADER		*hp;
205 	struct protoent	*pr;
206 #if PACKETSZ > 1024
207 	unsigned char	buf[PACKETSZ];
208 #else
209 	unsigned char	buf[1024];
210 #endif
211 
212 	typedef union
213 	{
214 		HEADER		h;
215 #if defined(NS_PACKETSZ)
216 		unsigned char	buffer[NS_PACKETSZ];
217 #elif defined(PACKETSZ)
218 		unsigned char	buffer[PACKETSZ];
219 #else
220 		unsigned char	buffer[512];
221 #endif
222 	}
223 	answer_t;
224 
225 	answer_t	answer;
226 #endif	/* defined(_WINDOWS) || defined(__MINGW32__) */
227 	zbx_vector_str_t	answers;
228 
229 	*buffer = '\0';
230 
231 	if (6 < request->nparam)
232 	{
233 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
234 		return SYSINFO_RET_FAIL;
235 	}
236 
237 	ip = get_rparam(request, 0);
238 	zone_str = get_rparam(request, 1);
239 
240 #if !defined(_WINDOWS) && !defined(__MINGW32__)
241 	memset(&hint, '\0', sizeof(hint));
242 	hint.ai_family = PF_UNSPEC;
243 	hint.ai_flags = AI_NUMERICHOST;
244 
245 	if (NULL != ip && '\0' != *ip && 0 == getaddrinfo(ip, NULL, &hint, &hres) && AF_INET6 == hres->ai_family)
246 		ip_type = hres->ai_family;
247 
248 	if (NULL != hres)
249 		freeaddrinfo(hres);
250 #endif
251 	if (NULL == zone_str || '\0' == *zone_str)
252 		strscpy(zone, "zabbix.com");
253 	else
254 		strscpy(zone, zone_str);
255 
256 	param = get_rparam(request, 2);
257 
258 	if (NULL == param || '\0' == *param)
259 		type = T_SOA;
260 	else
261 	{
262 		for (i = 0; NULL != qt[i].name; i++)
263 		{
264 			if (0 == strcasecmp(qt[i].name, param))
265 			{
266 				type = qt[i].type;
267 				break;
268 			}
269 		}
270 
271 		if (NULL == qt[i].name)
272 		{
273 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
274 			return SYSINFO_RET_FAIL;
275 		}
276 	}
277 
278 	param = get_rparam(request, 3);
279 
280 	if (NULL == param || '\0' == *param)
281 		retrans = 1;
282 	else if (SUCCEED != is_uint31(param, &retrans) || 0 == retrans)
283 	{
284 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fourth parameter."));
285 		return SYSINFO_RET_FAIL;
286 	}
287 
288 	param = get_rparam(request, 4);
289 
290 	if (NULL == param || '\0' == *param)
291 		retry = 2;
292 	else if (SUCCEED != is_uint31(param, &retry) || 0 == retry)
293 	{
294 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
295 		return SYSINFO_RET_FAIL;
296 	}
297 
298 	param = get_rparam(request, 5);
299 
300 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "udp"))
301 		use_tcp = 0;
302 	else if (0 == strcmp(param, "tcp"))
303 		use_tcp = 1;
304 	else
305 	{
306 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid sixth parameter."));
307 		return SYSINFO_RET_FAIL;
308 	}
309 
310 #if defined(_WINDOWS) || defined(__MINGW32__)
311 	options = DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE;
312 	if (0 != use_tcp)
313 		options |= DNS_QUERY_USE_TCP_ONLY;
314 
315 	wzone = zbx_utf8_to_unicode(zone);
316 	res = DnsQuery(wzone, type, options, NULL, &pQueryResults, NULL);
317 	zbx_free(wzone);
318 
319 	if (1 == short_answer)
320 	{
321 		SET_UI64_RESULT(result, DNS_RCODE_NOERROR != res ? 0 : 1);
322 		ret = SYSINFO_RET_OK;
323 		goto clean_dns;
324 	}
325 
326 	if (DNS_RCODE_NOERROR != res)
327 	{
328 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot perform DNS query: [%d]", res));
329 		return SYSINFO_RET_FAIL;
330 	}
331 
332 	pDnsRecord = pQueryResults;
333 	zbx_vector_str_create(&answers);
334 
335 	while (NULL != pDnsRecord)
336 	{
337 		if (DnsSectionAnswer != pDnsRecord->Flags.S.Section)
338 		{
339 			pDnsRecord = pDnsRecord->pNext;
340 			continue;
341 		}
342 
343 		if (NULL == pDnsRecord->pName)
344 			goto clean;
345 
346 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%-20s",
347 				zbx_unicode_to_utf8_static(pDnsRecord->pName, tmp, sizeof(tmp)));
348 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %-8s",
349 				decode_type(pDnsRecord->wType));
350 
351 		switch (pDnsRecord->wType)
352 		{
353 			case T_A:
354 				inaddr.s_addr = pDnsRecord->Data.A.IpAddress;
355 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
356 						inet_ntoa(inaddr));
357 				break;
358 			case T_AAAA:
359 				memcpy(&in6addr.s6_addr, &(pDnsRecord->Data.AAAA.Ip6Address), sizeof(in6addr.s6_addr));
360 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
361 						zbx_inet_ntop(AF_INET6, &in6addr, tmp, sizeof(tmp)));
362 				break;
363 			case T_NS:
364 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
365 						zbx_unicode_to_utf8_static(pDnsRecord->Data.NS.pNameHost, tmp, sizeof(tmp)));
366 				break;
367 			case T_MD:
368 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
369 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MD.pNameHost, tmp, sizeof(tmp)));
370 				break;
371 			case T_MF:
372 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
373 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MF.pNameHost, tmp, sizeof(tmp)));
374 				break;
375 			case T_CNAME:
376 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
377 						zbx_unicode_to_utf8_static(pDnsRecord->Data.CNAME.pNameHost, tmp, sizeof(tmp)));
378 				break;
379 			case T_SOA:
380 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s %s %lu %lu %lu %lu %lu",
381 						zbx_unicode_to_utf8_static(pDnsRecord->Data.SOA.pNamePrimaryServer, tmp, sizeof(tmp)),
382 						zbx_unicode_to_utf8_static(pDnsRecord->Data.SOA.pNameAdministrator, tmp2, sizeof(tmp2)),
383 						pDnsRecord->Data.SOA.dwSerialNo,
384 						pDnsRecord->Data.SOA.dwRefresh,
385 						pDnsRecord->Data.SOA.dwRetry,
386 						pDnsRecord->Data.SOA.dwExpire,
387 						pDnsRecord->Data.SOA.dwDefaultTtl);
388 				break;
389 			case T_MB:
390 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
391 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MB.pNameHost, tmp, sizeof(tmp)));
392 				break;
393 			case T_MG:
394 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
395 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MG.pNameHost, tmp, sizeof(tmp)));
396 				break;
397 			case T_MR:
398 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
399 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MR.pNameHost, tmp, sizeof(tmp)));
400 				break;
401 			case T_NULL:
402 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " len:%lu",
403 						pDnsRecord->Data.Null.dwByteCount);
404 				break;
405 			case T_PTR:
406 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
407 						zbx_unicode_to_utf8_static(pDnsRecord->Data.PTR.pNameHost, tmp, sizeof(tmp)));
408 				break;
409 			case T_HINFO:
410 				for (i = 0; i < (int)(pDnsRecord->Data.HINFO.dwStringCount); i++)
411 					offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " \"%s\"",
412 							zbx_unicode_to_utf8_static(pDnsRecord->Data.HINFO.pStringArray[i], tmp, sizeof(tmp)));
413 				break;
414 			case T_MINFO:
415 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s %s",
416 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MINFO.pNameMailbox, tmp, sizeof(tmp)),
417 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MINFO.pNameErrorsMailbox, tmp2, sizeof(tmp2)));
418 				break;
419 			case T_MX:
420 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %hu %s",
421 						pDnsRecord->Data.MX.wPreference,
422 						zbx_unicode_to_utf8_static(pDnsRecord->Data.MX.pNameExchange, tmp, sizeof(tmp)));
423 				break;
424 			case T_TXT:
425 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " \"");
426 
427 				for (i = 0; i < (int)(pDnsRecord->Data.TXT.dwStringCount); i++)
428 					offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%s ",
429 							zbx_unicode_to_utf8_static(pDnsRecord->Data.TXT.pStringArray[i], tmp, sizeof(tmp)));
430 
431 				if (0 < i)
432 					offset -= 1;	/* remove the trailing space */
433 
434 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "\"");
435 
436 				break;
437 			case T_SRV:
438 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %hu %hu %hu %s",
439 						pDnsRecord->Data.SRV.wPriority,
440 						pDnsRecord->Data.SRV.wWeight,
441 						pDnsRecord->Data.SRV.wPort,
442 						zbx_unicode_to_utf8_static(pDnsRecord->Data.SRV.pNameTarget, tmp, sizeof(tmp)));
443 				break;
444 			default:
445 				break;
446 		}
447 
448 		zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "\n");
449 
450 		pDnsRecord = pDnsRecord->pNext;
451 		zbx_vector_str_append(&answers, zbx_strdup(NULL, buffer));
452 		offset = 0;
453 		*buffer = '\0';
454 	}
455 #else	/* !defined(_WINDOWS) && !defined(__MINGW32__) */
456 #if defined(HAVE_RES_NINIT) && !defined(_AIX)
457 	memset(&res_state_local, 0, sizeof(res_state_local));
458 	if (-1 == res_ninit(&res_state_local))	/* initialize always, settings might have changed */
459 #else
460 	if (-1 == res_init())	/* initialize always, settings might have changed */
461 #endif
462 	{
463 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot initialize DNS subsystem: %s", zbx_strerror(errno)));
464 		return SYSINFO_RET_FAIL;
465 	}
466 
467 #if defined(HAVE_RES_NINIT) && !defined(_AIX)
468 	if (-1 == (res = res_nmkquery(&res_state_local, QUERY, zone, C_IN, type, NULL, 0, NULL, buf, sizeof(buf))))
469 #else
470 	if (-1 == (res = res_mkquery(QUERY, zone, C_IN, type, NULL, 0, NULL, buf, sizeof(buf))))
471 #endif
472 	{
473 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot create DNS query: %s", zbx_strerror(errno)));
474 		return SYSINFO_RET_FAIL;
475 	}
476 
477 	if (NULL != ip && '\0' != *ip && AF_INET == ip_type)
478 	{
479 		if (0 == inet_aton(ip, &inaddr))
480 		{
481 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid IP address."));
482 			return SYSINFO_RET_FAIL;
483 		}
484 
485 #if defined(HAVE_RES_NINIT) && !defined(_AIX)
486 		res_state_local.nsaddr_list[0].sin_addr = inaddr;
487 		res_state_local.nsaddr_list[0].sin_family = AF_INET;
488 		res_state_local.nsaddr_list[0].sin_port = htons(ZBX_DEFAULT_DNS_PORT);
489 		res_state_local.nscount = 1;
490 #else	/* thread-unsafe resolver API */
491 		memcpy(&saved_ns, &(_res.nsaddr_list[0]), sizeof(struct sockaddr_in));
492 		saved_nscount = _res.nscount;
493 
494 		_res.nsaddr_list[0].sin_addr = inaddr;
495 		_res.nsaddr_list[0].sin_family = AF_INET;
496 		_res.nsaddr_list[0].sin_port = htons(ZBX_DEFAULT_DNS_PORT);
497 		_res.nscount = 1;
498 #endif
499 	}
500 	else if (NULL != ip && '\0' != *ip && AF_INET6 == ip_type)
501 	{
502 		if (0 == inet_pton(ip_type, ip, &in6addr))
503 		{
504 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid IPv6 address."));
505 			return SYSINFO_RET_FAIL;
506 		}
507 
508 		memset(&sockaddrin6, '\0', sizeof(sockaddrin6));
509 #if defined(HAVE_RES_SIN6_LEN)
510 		sockaddrin6.sin6_len = sizeof(sockaddrin6);
511 #endif
512 		sockaddrin6.sin6_family = AF_INET6;
513 		sockaddrin6.sin6_addr = in6addr;
514 		sockaddrin6.sin6_port = htons(ZBX_DEFAULT_DNS_PORT);
515 #if defined(HAVE_RES_NINIT) && !defined(_AIX) && (defined(HAVE_RES_U_EXT) || defined(HAVE_RES_U_EXT_EXT))
516 		memset(&res_state_local.nsaddr_list[0], '\0', sizeof(res_state_local.nsaddr_list[0]));
517 #		ifdef HAVE_RES_U_EXT			/* Linux */
518 		saved_ns6 = res_state_local._u._ext.nsaddrs[0];
519 		res_state_local._u._ext.nsaddrs[0] = &sockaddrin6;
520 		res_state_local._u._ext.nssocks[0] = -1;
521 		res_state_local._u._ext.nscount6 = 1;	/* CentOS */
522 #		elif HAVE_RES_U_EXT_EXT			/* BSD */
523 		if (NULL != res_state_local._u._ext.ext)
524 			memcpy(res_state_local._u._ext.ext, &sockaddrin6, sizeof(sockaddrin6));
525 
526 		res_state_local.nsaddr_list[0].sin_port = htons(ZBX_DEFAULT_DNS_PORT);
527 #		endif
528 		res_state_local.nscount = 1;
529 #else
530 		memcpy(&saved_ns, &(_res.nsaddr_list[0]), sizeof(struct sockaddr_in));
531 		saved_nscount = _res.nscount;
532 #		if defined(HAVE_RES_U_EXT) || defined(HAVE_RES_U_EXT_EXT) || defined(HAVE_RES_EXT_EXT)
533 		memset(&_res.nsaddr_list[0], '\0', sizeof(_res.nsaddr_list[0]));
534 		_res.nscount = 1;
535 #		endif
536 #		if defined(HAVE_RES_U_EXT)		/* thread-unsafe resolver API /Linux/ */
537 		saved_nscount6 = _res._u._ext.nscount6;
538 		saved_ns6 = _res._u._ext.nsaddrs[0];
539 		save_nssocks = _res._u._ext.nssocks[0];
540 		_res._u._ext.nsaddrs[0] = &sockaddrin6;
541 		_res._u._ext.nssocks[0] = -1;
542 		_res._u._ext.nscount6 = 1;
543 #		elif defined(HAVE_RES_U_EXT_EXT)	/* thread-unsafe resolver API /BSD/ */
544 		memcpy(&saved_ns6, _res._u._ext.ext, sizeof(saved_ns6));
545 		_res.nsaddr_list[0].sin_port = htons(ZBX_DEFAULT_DNS_PORT);
546 
547 		if (NULL != _res._u._ext.ext)
548 			memcpy(_res._u._ext.ext, &sockaddrin6, sizeof(sockaddrin6));
549 #		elif defined(HAVE_RES_EXT_EXT)		/* thread-unsafe resolver API /AIX/ */
550 		memcpy(&saved_ns6, &(_res._ext.ext.nsaddrs[0]), sizeof(saved_ns6));
551 		memcpy(&_res._ext.ext.nsaddrs[0], &sockaddrin6, sizeof(sockaddrin6));
552 #		endif /* #if defined(HAVE_RES_U_EXT) */
553 #endif /* #if defined(HAVE_RES_NINIT) && !defined(_AIX) && (defined(HAVE_RES_U_EXT) || defined(HAVE_RES_U_EXT_EXT)) */
554 	}
555 
556 #if defined(HAVE_RES_NINIT) && !defined(_AIX) && (defined(HAVE_RES_U_EXT) || defined(HAVE_RES_U_EXT_EXT))
557 	if (0 != use_tcp)
558 		res_state_local.options |= RES_USEVC;
559 
560 	res_state_local.retrans = retrans;
561 	res_state_local.retry = retry;
562 
563 	res = res_nsend(&res_state_local, buf, res, answer.buffer, sizeof(answer.buffer));
564 
565 #	ifdef HAVE_RES_U_EXT	/* Linux */
566 	if (NULL != ip && '\0' != *ip && AF_INET6 == ip_type)
567 		res_state_local._u._ext.nsaddrs[0] = saved_ns6;
568 #	endif
569 #	ifdef HAVE_RES_NDESTROY
570 	res_ndestroy(&res_state_local);
571 #	else
572 	res_nclose(&res_state_local);
573 #	endif
574 #else	/* thread-unsafe resolver API */
575 	saved_options = _res.options;
576 	saved_retrans = _res.retrans;
577 	saved_retry = _res.retry;
578 
579 	if (0 != use_tcp)
580 		_res.options |= RES_USEVC;
581 
582 	_res.retrans = retrans;
583 	_res.retry = retry;
584 
585 	res = res_send(buf, res, answer.buffer, sizeof(answer.buffer));
586 
587 	_res.options = saved_options;
588 	_res.retrans = saved_retrans;
589 	_res.retry = saved_retry;
590 
591 	if (NULL != ip && '\0' != *ip)
592 	{
593 		if (AF_INET6 == ip_type)
594 		{
595 #			if defined(HAVE_RES_U_EXT)		/* Linux */
596 			_res._u._ext.nsaddrs[0] = saved_ns6;
597 			_res._u._ext.nssocks[0] = save_nssocks;
598 			_res._u._ext.nscount6 = saved_nscount6;
599 #			elif defined(HAVE_RES_U_EXT_EXT)	/* BSD */
600 			if (NULL != _res._u._ext.ext)
601 				memcpy(_res._u._ext.ext, &saved_ns6, sizeof(saved_ns6));
602 #			elif defined(HAVE_RES_EXT_EXT)		/* AIX */
603 			memcpy(&_res._ext.ext.nsaddrs[0], &saved_ns6, sizeof(saved_ns6));
604 #			endif
605 		}
606 
607 		memcpy(&(_res.nsaddr_list[0]), &saved_ns, sizeof(struct sockaddr_in));
608 		_res.nscount = saved_nscount;
609 	}
610 #endif
611 	hp = (HEADER *)answer.buffer;
612 
613 	if (1 == short_answer)
614 	{
615 		SET_UI64_RESULT(result, NOERROR != hp->rcode || 0 == ntohs(hp->ancount) || -1 == res ? 0 : 1);
616 		return SYSINFO_RET_OK;
617 	}
618 
619 	if (NOERROR != hp->rcode || 0 == ntohs(hp->ancount) || -1 == res)
620 	{
621 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot perform DNS query."));
622 		return SYSINFO_RET_FAIL;
623 	}
624 
625 	msg_end = answer.buffer + res;
626 
627 	num_answers = ntohs(answer.h.ancount);
628 	num_query = ntohs(answer.h.qdcount);
629 
630 	msg_ptr = answer.buffer + HFIXEDSZ;
631 	zbx_vector_str_create(&answers);
632 
633 	/* skipping query records */
634 	for (; 0 < num_query && msg_ptr < msg_end; num_query--)
635 		msg_ptr += dn_skipname(msg_ptr, msg_end) + QFIXEDSZ;
636 
637 	for (; 0 < num_answers && msg_ptr < msg_end; num_answers--)
638 	{
639 		if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))
640 		{
641 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
642 			ret = SYSINFO_RET_FAIL;
643 			goto clean;
644 		}
645 
646 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%-20s", name);
647 
648 		GETSHORT(q_type, msg_ptr);
649 		GETSHORT(q_class, msg_ptr);
650 		msg_ptr += INT32SZ;		/* skipping TTL */
651 		GETSHORT(q_len, msg_ptr);
652 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %-8s", decode_type(q_type));
653 
654 		switch (q_type)
655 		{
656 			case T_A:
657 				switch (q_class)
658 				{
659 					case C_IN:
660 					case C_HS:
661 						memcpy(&inaddr, msg_ptr, INADDRSZ);
662 						offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
663 								inet_ntoa(inaddr));
664 						break;
665 					default:
666 						;
667 				}
668 
669 				msg_ptr += q_len;
670 
671 				break;
672 			case T_AAAA:
673 				switch (q_class)
674 				{
675 					case C_IN:
676 					case C_HS:
677 						memcpy(&in6addr, msg_ptr, IN6ADDRSZ);
678 						offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s",
679 								inet_ntop(AF_INET6, &in6addr, tmp, sizeof(tmp)));
680 						break;
681 					default:
682 						;
683 				}
684 
685 				msg_ptr += q_len;
686 
687 				break;
688 			case T_NS:
689 			case T_CNAME:
690 			case T_MB:
691 			case T_MD:
692 			case T_MF:
693 			case T_MG:
694 			case T_MR:
695 			case T_PTR:
696 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))
697 				{
698 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
699 					return SYSINFO_RET_FAIL;
700 				}
701 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
702 				break;
703 			case T_MX:
704 				GETSHORT(value, msg_ptr);	/* preference */
705 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
706 
707 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* exchange */
708 				{
709 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
710 					return SYSINFO_RET_FAIL;
711 				}
712 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
713 
714 				break;
715 			case T_SOA:
716 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* source host */
717 				{
718 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
719 					return SYSINFO_RET_FAIL;
720 				}
721 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
722 
723 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* administrator */
724 				{
725 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
726 					return SYSINFO_RET_FAIL;
727 				}
728 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
729 
730 				GETLONG(value, msg_ptr);	/* serial number */
731 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
732 
733 				GETLONG(value, msg_ptr);	/* refresh time */
734 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
735 
736 				GETLONG(value, msg_ptr);	/* retry time */
737 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
738 
739 				GETLONG(value, msg_ptr);	/* expire time */
740 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
741 
742 				GETLONG(value, msg_ptr);	/* minimum TTL */
743 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
744 
745 				break;
746 			case T_NULL:
747 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " len:%d", q_len);
748 				msg_ptr += q_len;
749 				break;
750 			case T_WKS:
751 				if (INT32SZ + 1 > q_len)
752 				{
753 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
754 					return SYSINFO_RET_FAIL;
755 				}
756 
757 				p = msg_ptr + q_len;
758 
759 				memcpy(&inaddr, msg_ptr, INADDRSZ);
760 				msg_ptr += INT32SZ;
761 
762 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", inet_ntoa(inaddr));
763 
764 				if (NULL != (pr = getprotobynumber(*msg_ptr)))
765 					offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", pr->p_name);
766 				else
767 					offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", (int)*msg_ptr);
768 
769 				msg_ptr++;
770 				n = 0;
771 
772 				while (msg_ptr < p)
773 				{
774 					c = *msg_ptr++;
775 
776 					do
777 					{
778 						if (0 != (c & 0200))
779 						{
780 							s = getservbyport((int)htons(n), pr ? pr->p_name : NULL);
781 
782 							if (NULL != s)
783 								offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", s->s_name);
784 							else
785 								offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " #%d", n);
786 						}
787 
788 						c <<= 1;
789 					}
790 					while (0 != (++n & 07));
791 				}
792 
793 				break;
794 			case T_HINFO:
795 				p = msg_ptr + q_len;
796 				c = *msg_ptr++;
797 
798 				if (0 != c)
799 				{
800 					offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " \"%.*s\"", c, msg_ptr);
801 					msg_ptr += c;
802 				}
803 
804 				if (msg_ptr < p)
805 				{
806 					c = *msg_ptr++;
807 
808 					if (0 != c)
809 					{
810 						offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " \"%.*s\"", c, msg_ptr);
811 						msg_ptr += c;
812 					}
813 				}
814 
815 				break;
816 			case T_MINFO:
817 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* mailbox responsible for mailing lists */
818 				{
819 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
820 					return SYSINFO_RET_FAIL;
821 				}
822 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
823 
824 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* mailbox for error messages */
825 				{
826 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
827 					return SYSINFO_RET_FAIL;
828 				}
829 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
830 
831 				break;
832 			case T_TXT:
833 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " \"");
834 				p = msg_ptr + q_len;
835 
836 				while (msg_ptr < p)
837 				{
838 					for (c = *msg_ptr++; 0 < c && msg_ptr < p; c--)
839 						offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%c", *msg_ptr++);
840 				}
841 
842 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "\"");
843 
844 				break;
845 			case T_SRV:
846 				GETSHORT(value, msg_ptr);       /* priority */
847 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
848 
849 				GETSHORT(value, msg_ptr);       /* weight */
850 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
851 
852 				GETSHORT(value, msg_ptr);       /* port */
853 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %d", value);
854 
855 				if (NULL == (name = get_name(answer.buffer, msg_end, &msg_ptr)))	/* target */
856 				{
857 					SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot decode DNS response."));
858 					return SYSINFO_RET_FAIL;
859 				}
860 				offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, " %s", name);
861 
862 				break;
863 			default:
864 				msg_ptr += q_len;
865 				break;
866 		}
867 
868 		zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "\n");
869 
870 		zbx_vector_str_append(&answers, zbx_strdup(NULL, buffer));
871 		offset = 0;
872 		*buffer = '\0';
873 	}
874 #endif	/* defined(_WINDOWS) || defined(__MINGW32__) */
875 
876 	zbx_vector_str_sort(&answers, ZBX_DEFAULT_STR_COMPARE_FUNC);
877 
878 	for (i = 0; i < answers.values_num; i++)
879 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%s", answers.values[i]);
880 
881 	if (0 != offset)
882 		buffer[--offset] = '\0';
883 
884 	SET_TEXT_RESULT(result, zbx_strdup(NULL, buffer));
885 	ret = SYSINFO_RET_OK;
886 
887 clean:
888 	zbx_vector_str_clear_ext(&answers, zbx_str_free);
889 	zbx_vector_str_destroy(&answers);
890 #if defined(_WINDOWS) || defined(__MINGW32__)
891 clean_dns:
892 	if (DNS_RCODE_NOERROR == res)
893 		DnsRecordListFree(pQueryResults, DnsFreeRecordList);
894 #endif
895 
896 	return ret;
897 
898 #else	/* all HAVE_RES_QUERY and _WINDOWS and __MINGW32__not defined */
899 
900 	return SYSINFO_RET_FAIL;
901 
902 #endif	/* defined(HAVE_RES_QUERY) || defined(_WINDOWS) || defined(__MINGW32__)*/
903 }
904 
NET_DNS(AGENT_REQUEST * request,AGENT_RESULT * result)905 int	NET_DNS(AGENT_REQUEST *request, AGENT_RESULT *result)
906 {
907 	return dns_query(request, result, 1);
908 }
909 
NET_DNS_RECORD(AGENT_REQUEST * request,AGENT_RESULT * result)910 int	NET_DNS_RECORD(AGENT_REQUEST *request, AGENT_RESULT *result)
911 {
912 	return dns_query(request, result, 0);
913 }
914