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