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