1/*	$NetBSD: networking.c,v 1.5 2012/02/01 07:46:23 kardel Exp $	*/
2
3#include <config.h>
4#include "networking.h"
5
6char adr_buf[INET6_ADDRSTRLEN];
7
8
9/* resolve_hosts consumes an array of hostnames/addresses and its length, stores a pointer
10 * to the array with the resolved hosts in res and returns the size of the array res.
11 * pref_family enforces IPv4 or IPv6 depending on commandline options and system
12 * capability. If pref_family is NULL or PF_UNSPEC any compatible family will be accepted.
13 * Check here: Probably getaddrinfo() can do without ISC's IPv6 availability check?
14 */
15int
16resolve_hosts (
17		const char **hosts,
18		int hostc,
19		struct addrinfo ***res,
20		int pref_family
21		)
22{
23	register int a;
24	unsigned int resc;
25	struct addrinfo **tres;
26
27	if (hostc < 1 || NULL == res)
28		return 0;
29
30	tres = emalloc(sizeof(struct addrinfo *) * hostc);
31	for (a = 0, resc = 0; a < hostc; a++) {
32		struct addrinfo hints;
33		int error;
34
35		tres[resc] = NULL;
36#ifdef DEBUG
37		printf("sntp resolve_hosts: Starting host resolution for %s...\n", hosts[a]);
38#endif
39		memset(&hints, 0, sizeof(hints));
40		if (AF_UNSPEC == pref_family)
41			hints.ai_family = PF_UNSPEC;
42		else
43			hints.ai_family = pref_family;
44		hints.ai_socktype = SOCK_DGRAM;
45		error = getaddrinfo(hosts[a], "123", &hints, &tres[resc]);
46		if (error) {
47			msyslog(LOG_DEBUG, "Error looking up %s%s: %s",
48				(AF_UNSPEC == hints.ai_family)
49				    ? ""
50				    : (AF_INET == hints.ai_family)
51					  ? "(A) "
52					  : "(AAAA) ",
53				hosts[a], gai_strerror(error));
54		} else {
55#ifdef DEBUG
56			struct addrinfo *dres;
57
58			for (dres = tres[resc]; dres; dres = dres->ai_next) {
59				getnameinfo(dres->ai_addr, dres->ai_addrlen, adr_buf, sizeof(adr_buf), NULL, 0, NI_NUMERICHOST);
60				STDLINE
61				printf("Resolv No.: %i Result of getaddrinfo for %s:\n", resc, hosts[a]);
62				printf("socktype: %i ", dres->ai_socktype);
63				printf("protocol: %i ", dres->ai_protocol);
64				printf("Prefered socktype: %i IP: %s\n", dres->ai_socktype, adr_buf);
65				STDLINE
66			}
67#endif
68			resc++;
69		}
70	}
71
72	if (resc)
73		*res = realloc(tres, sizeof(struct addrinfo *) * resc);
74	else {
75		free(tres);
76		*res = NULL;
77	}
78	return resc;
79}
80
81/* Creates a socket and returns. */
82void
83create_socket (
84		SOCKET *rsock,
85		sockaddr_u *dest
86		)
87{
88	*rsock = socket(AF(dest), SOCK_DGRAM, 0);
89
90	if (-1 == *rsock && ENABLED_OPT(NORMALVERBOSE))
91		printf("Failed to create UDP socket with family %d\n", AF(dest));
92}
93
94/* Send a packet */
95void
96sendpkt (
97	SOCKET rsock,
98	sockaddr_u *dest,
99	struct pkt *pkt,
100	int len
101	)
102{
103	int cc;
104
105#ifdef DEBUG
106	printf("sntp sendpkt: Packet data:\n");
107	pkt_output(pkt, len, stdout);
108#endif
109
110	if (ENABLED_OPT(NORMALVERBOSE)) {
111		getnameinfo(&dest->sa, SOCKLEN(dest), adr_buf, sizeof(adr_buf), NULL, 0, NI_NUMERICHOST);
112		printf("sntp sendpkt: Sending packet to %s... ", adr_buf);
113	}
114
115	cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa, SOCKLEN(dest));
116	if (cc == SOCKET_ERROR) {
117#ifdef DEBUG
118		printf("\n sntp sendpkt: Socket error: %i. Couldn't send packet!\n", cc);
119#endif
120		if (errno != EWOULDBLOCK && errno != ENOBUFS) {
121			/* oh well */
122		}
123	} else if (ENABLED_OPT(NORMALVERBOSE)) {
124		printf("Packet sent.\n");
125	}
126}
127
128/* Receive raw data */
129int
130recvdata(
131	SOCKET rsock,
132	sockaddr_u *sender,
133	char *rdata,
134	int rdata_length
135	)
136{
137	GETSOCKNAME_SOCKLEN_TYPE slen;
138	int recvc;
139
140#ifdef DEBUG
141	printf("sntp recvdata: Trying to receive data from...\n");
142#endif
143	slen = sizeof(*sender);
144	recvc = recvfrom(rsock, rdata, rdata_length, 0,
145			 &sender->sa, &slen);
146#ifdef DEBUG
147	if (recvc > 0) {
148		printf("Received %d bytes from %s:\n", recvc, stoa(sender));
149		pkt_output((struct pkt *) rdata, recvc, stdout);
150	} else {
151		int saved_errno = errno;
152		printf("recvfrom error %d (%s)\n", errno, strerror(errno));
153		errno = saved_errno;
154	}
155#endif
156	return recvc;
157}
158
159/* Receive data from broadcast. Couldn't finish that. Need to do some digging
160 * here, especially for protocol independence and IPv6 multicast */
161int
162recv_bcst_data (
163	SOCKET rsock,
164	char *rdata,
165	int rdata_len,
166	sockaddr_u *sas,
167	sockaddr_u *ras
168	)
169{
170	char *buf;
171	int btrue = 1;
172	int recv_bytes = 0;
173	int rdy_socks;
174	GETSOCKNAME_SOCKLEN_TYPE ss_len;
175	struct timeval timeout_tv;
176	fd_set bcst_fd;
177#ifdef MCAST
178	struct ip_mreq mdevadr;
179	TYPEOF_IP_MULTICAST_LOOP mtrue = 1;
180#endif
181#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
182	struct ipv6_mreq mdevadr6;
183#endif
184
185	setsockopt(rsock, SOL_SOCKET, SO_REUSEADDR, &btrue, sizeof(btrue));
186	if (IS_IPV4(sas)) {
187		if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
188			if (ENABLED_OPT(NORMALVERBOSE))
189				printf("sntp recv_bcst_data: Couldn't bind() address %s:%d.\n",
190				       stoa(sas), SRCPORT(sas));
191		}
192
193#ifdef MCAST
194		if (setsockopt(rsock, IPPROTO_IP, IP_MULTICAST_LOOP, &mtrue, sizeof(mtrue)) < 0) {
195			/* some error message regarding setting up multicast loop */
196			return BROADCAST_FAILED;
197		}
198		mdevadr.imr_multiaddr.s_addr = NSRCADR(sas);
199		mdevadr.imr_interface.s_addr = htonl(INADDR_ANY);
200		if (mdevadr.imr_multiaddr.s_addr == ~(unsigned)0) {
201			if (ENABLED_OPT(NORMALVERBOSE)) {
202				printf("sntp recv_bcst_data: %s:%d is not a broad-/multicast address, aborting...\n",
203				       stoa(sas), SRCPORT(sas));
204			}
205			return BROADCAST_FAILED;
206		}
207		if (setsockopt(rsock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mdevadr, sizeof(mdevadr)) < 0) {
208			if (ENABLED_OPT(NORMALVERBOSE)) {
209				buf = ss_to_str(sas);
210				printf("sntp recv_bcst_data: Couldn't add IP membership for %s\n", buf);
211				free(buf);
212			}
213		}
214#endif	/* MCAST */
215	}
216#ifdef ISC_PLATFORM_HAVEIPV6
217	else if (IS_IPV6(sas)) {
218		if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
219			if (ENABLED_OPT(NORMALVERBOSE))
220				printf("sntp recv_bcst_data: Couldn't bind() address.\n");
221		}
222#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
223		if (setsockopt(rsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &btrue, sizeof (btrue)) < 0) {
224			/* some error message regarding setting up multicast loop */
225			return BROADCAST_FAILED;
226		}
227		memset(&mdevadr6, 0, sizeof(mdevadr6));
228		mdevadr6.ipv6mr_multiaddr = SOCK_ADDR6(sas);
229		if (!IN6_IS_ADDR_MULTICAST(&mdevadr6.ipv6mr_multiaddr)) {
230			if (ENABLED_OPT(NORMALVERBOSE)) {
231				buf = ss_to_str(sas);
232				printf("sntp recv_bcst_data: %s is not a broad-/multicast address, aborting...\n", buf);
233				free(buf);
234			}
235			return BROADCAST_FAILED;
236		}
237		if (setsockopt(rsock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
238			       &mdevadr6, sizeof(mdevadr6)) < 0) {
239			if (ENABLED_OPT(NORMALVERBOSE)) {
240				buf = ss_to_str(sas);
241				printf("sntp recv_bcst_data: Couldn't join group for %s\n", buf);
242				free(buf);
243			}
244		}
245#endif	/* INCLUDE_IPV6_MULTICAST_SUPPORT */
246	}
247#endif	/* ISC_PLATFORM_HAVEIPV6 */
248	FD_ZERO(&bcst_fd);
249	FD_SET(rsock, &bcst_fd);
250	if (ENABLED_OPT(TIMEOUT))
251		timeout_tv.tv_sec = (int) atol(OPT_ARG(TIMEOUT));
252	else
253		timeout_tv.tv_sec = 68; /* ntpd broadcasts every 64s */
254	timeout_tv.tv_usec = 0;
255	rdy_socks = select(rsock + 1, &bcst_fd, 0, 0, &timeout_tv);
256	switch (rdy_socks) {
257	case -1:
258		if (ENABLED_OPT(NORMALVERBOSE))
259			perror("sntp recv_bcst_data: select()");
260		return BROADCAST_FAILED;
261		break;
262	case 0:
263		if (ENABLED_OPT(NORMALVERBOSE))
264			printf("sntp recv_bcst_data: select() reached timeout (%u sec), aborting.\n",
265			       (unsigned)timeout_tv.tv_sec);
266		return BROADCAST_FAILED;
267		break;
268	default:
269		ss_len = sizeof(*ras);
270		recv_bytes = recvfrom(rsock, rdata, rdata_len, 0, &ras->sa, &ss_len);
271		break;
272	}
273	if (recv_bytes == -1) {
274		if (ENABLED_OPT(NORMALVERBOSE))
275			perror("sntp recv_bcst_data: recvfrom:");
276		recv_bytes = BROADCAST_FAILED;
277	}
278#ifdef MCAST
279	if (IS_IPV4(sas))
280		setsockopt(rsock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &btrue, sizeof(btrue));
281#endif
282#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
283	if (IS_IPV6(sas))
284		setsockopt(rsock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &btrue, sizeof(btrue));
285#endif
286	return recv_bytes;
287}
288
289int
290process_pkt (
291	struct pkt *rpkt,
292	sockaddr_u *sas,
293	int pkt_len,
294	int mode,
295	struct pkt *spkt,
296	const char * func_name
297	)
298{
299	unsigned int key_id = 0;
300	struct key *pkt_key = NULL;
301	int is_authentic = 0;
302	unsigned int exten_words, exten_words_used = 0;
303	int mac_size;
304	/*
305	 * Parse the extension field if present. We figure out whether
306	 * an extension field is present by measuring the MAC size. If
307	 * the number of words following the packet header is 0, no MAC
308	 * is present and the packet is not authenticated. If 1, the
309	 * packet is a crypto-NAK; if 3, the packet is authenticated
310	 * with DES; if 5, the packet is authenticated with MD5; if 6,
311	 * the packet is authenticated with SHA. If 2 or 4, the packet
312	 * is a runt and discarded forthwith. If greater than 6, an
313	 * extension field is present, so we subtract the length of the
314	 * field and go around again.
315	 */
316	if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
317unusable:
318		if (ENABLED_OPT(NORMALVERBOSE))
319			printf("sntp %s: Funny packet length: %i. Discarding package.\n", func_name, pkt_len);
320		return PACKET_UNUSEABLE;
321	}
322	/* skip past the extensions, if any */
323	exten_words = ((unsigned)pkt_len - LEN_PKT_NOMAC) >> 2;
324	while (exten_words > 6) {
325		unsigned int exten_len;
326		exten_len = ntohl(rpkt->exten[exten_words_used]) & 0xffff;
327		exten_len = (exten_len + 7) >> 2; /* convert to words, add 1 */
328		if (exten_len > exten_words || exten_len < 5)
329			goto unusable;
330		exten_words -= exten_len;
331		exten_words_used += exten_len;
332	}
333
334	switch (exten_words) {
335	case 1:
336		key_id = ntohl(rpkt->exten[exten_words_used]);
337		printf("Crypto NAK = 0x%08x\n", key_id);
338		break;
339	case 5:
340	case 6:
341		/* Look for the key used by the server in the specified keyfile
342		 * and if existent, fetch it or else leave the pointer untouched */
343		key_id = ntohl(rpkt->exten[exten_words_used]);
344		get_key(key_id, &pkt_key);
345		if (!pkt_key) {
346			printf("unrecognized key ID = 0x%08x\n", key_id);
347			break;
348		}
349		/* Seems like we've got a key with matching keyid */
350		/* Generate a md5sum of the packet with the key from our keyfile
351		 * and compare those md5sums */
352		mac_size = exten_words << 2;
353		if (!auth_md5((char *)rpkt, pkt_len - mac_size, mac_size - 4, pkt_key)) {
354			break;
355		}
356		/* Yay! Things worked out! */
357		if (ENABLED_OPT(NORMALVERBOSE)) {
358			char *hostname = ss_to_str(sas);
359			printf("sntp %s: packet received from %s successfully authenticated using key id %i.\n",
360				func_name, hostname, key_id);
361			free(hostname);
362		}
363		is_authentic = 1;
364		break;
365	case 0:
366		break;
367	default:
368		goto unusable;
369		break;
370	}
371	if (!is_authentic) {
372		if (ENABLED_OPT(AUTHENTICATION)) {
373			/* We want a authenticated packet */
374			if (ENABLED_OPT(NORMALVERBOSE)) {
375				char *hostname = ss_to_str(sas);
376				printf("sntp %s: packet received from %s is not authentic. Will discard it.\n",
377					func_name, hostname);
378				free(hostname);
379			}
380			return SERVER_AUTH_FAIL;
381		}
382		/* We don't know if the user wanted authentication so let's
383		 * use it anyways */
384		if (ENABLED_OPT(NORMALVERBOSE)) {
385			char *hostname = ss_to_str(sas);
386			printf("sntp %s: packet received from %s is not authentic. Authentication not enforced.\n",
387				func_name, hostname);
388			free(hostname);
389		}
390	}
391	/* Check for server's ntp version */
392	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
393		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
394		if (ENABLED_OPT(NORMALVERBOSE))
395			printf("sntp %s: Packet shows wrong version (%i)\n",
396				func_name, PKT_VERSION(rpkt->li_vn_mode));
397		return SERVER_UNUSEABLE;
398	}
399	/* We want a server to sync with */
400	if (PKT_MODE(rpkt->li_vn_mode) != mode &&
401	    PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
402		if (ENABLED_OPT(NORMALVERBOSE))
403			printf("sntp %s: mode %d stratum %i\n", func_name,
404			       PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
405		return SERVER_UNUSEABLE;
406	}
407	/* Stratum is unspecified (0) check what's going on */
408	if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
409		char *ref_char;
410		if (ENABLED_OPT(NORMALVERBOSE))
411			printf("sntp %s: Stratum unspecified, going to check for KOD (stratum: %i)\n",
412				func_name, rpkt->stratum);
413		ref_char = (char *) &rpkt->refid;
414		if (ENABLED_OPT(NORMALVERBOSE))
415			printf("sntp %s: Packet refid: %c%c%c%c\n", func_name,
416			       ref_char[0], ref_char[1], ref_char[2], ref_char[3]);
417		/* If it's a KOD packet we'll just use the KOD information */
418		if (ref_char[0] != 'X') {
419			if (strncmp(ref_char, "DENY", 4) == 0)
420				return KOD_DEMOBILIZE;
421			if (strncmp(ref_char, "RSTR", 4) == 0)
422				return KOD_DEMOBILIZE;
423			if (strncmp(ref_char, "RATE", 4) == 0)
424				return KOD_RATE;
425			/* There are other interesting kiss codes which might be interesting for authentication */
426		}
427	}
428	/* If the server is not synced it's not really useable for us */
429	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
430		if (ENABLED_OPT(NORMALVERBOSE))
431			printf("sntp %s: Server not in sync, skipping this server\n", func_name);
432		return SERVER_UNUSEABLE;
433	}
434
435	/*
436	 * Decode the org timestamp and make sure we're getting a response
437	 * to our last request, but only if we're not in broadcast mode.
438	 */
439#ifdef DEBUG
440	printf("rpkt->org:\n");
441	l_fp_output(&rpkt->org, stdout);
442	printf("spkt->xmt:\n");
443	l_fp_output(&spkt->xmt, stdout);
444#endif
445	if (mode != MODE_BROADCAST && !L_ISEQU(&rpkt->org, &spkt->xmt)) {
446		if (ENABLED_OPT(NORMALVERBOSE))
447			printf("sntp process_pkt: pkt.org and peer.xmt differ\n");
448		return PACKET_UNUSEABLE;
449	}
450
451	return pkt_len;
452}
453
454int
455recv_bcst_pkt (
456	SOCKET rsock,
457	struct pkt *rpkt,
458	unsigned int rsize,
459	sockaddr_u *sas
460	)
461{
462	sockaddr_u sender;
463	int pkt_len = recv_bcst_data(rsock, (char *)rpkt, rsize, sas, &sender);
464	if (pkt_len < 0) {
465		return BROADCAST_FAILED;
466	}
467	pkt_len = process_pkt(rpkt, sas, pkt_len, MODE_BROADCAST, NULL, "recv_bcst_pkt");
468	return pkt_len;
469}
470
471/* Fetch data, check if it's data for us and whether it's useable or not. If not, return
472 * a failure code so we can delete this server from our list and continue with another one.
473 */
474int
475recvpkt (
476	SOCKET rsock,
477	struct pkt *rpkt,    /* received packet (response) */
478	unsigned int rsize,  /* size of rpkt buffer */
479	struct pkt *spkt     /* sent     packet (request) */
480	)
481{
482	int rdy_socks;
483	int pkt_len;
484	sockaddr_u sender;
485	struct timeval timeout_tv;
486	fd_set recv_fd;
487
488	FD_ZERO(&recv_fd);
489	FD_SET(rsock, &recv_fd);
490	if (ENABLED_OPT(TIMEOUT))
491		timeout_tv.tv_sec = (int) atol(OPT_ARG(TIMEOUT));
492	else
493		timeout_tv.tv_sec = 68; /* ntpd broadcasts every 64s */
494	timeout_tv.tv_usec = 0;
495	rdy_socks = select(rsock + 1, &recv_fd, 0, 0, &timeout_tv);
496	switch (rdy_socks) {
497	case -1:
498		if (ENABLED_OPT(NORMALVERBOSE))
499			perror("sntp recvpkt: select()");
500		return PACKET_UNUSEABLE;
501		break;
502	case 0:
503		if (ENABLED_OPT(NORMALVERBOSE))
504			printf("sntp recvpkt: select() reached timeout (%u sec), aborting.\n",
505			       (unsigned)timeout_tv.tv_sec);
506		return PACKET_UNUSEABLE;
507		break;
508	default:
509		break;
510	}
511	pkt_len = recvdata(rsock, &sender, (char *)rpkt, rsize);
512	if (pkt_len > 0)
513		pkt_len = process_pkt(rpkt, &sender, pkt_len, MODE_SERVER, spkt, "recvpkt");
514
515	return pkt_len;
516}
517
518/*
519 * is_reachable - check to see if we have a route to given destination
520 */
521int
522is_reachable (
523	struct addrinfo *dst
524	)
525{
526	SOCKET sockfd = socket(dst->ai_family, SOCK_DGRAM, 0);
527
528	if (-1 == sockfd) {
529#ifdef DEBUG
530		printf("is_reachable: Couldn't create socket\n");
531#endif
532		return 0;
533	}
534	if (connect(sockfd, dst->ai_addr, SOCKLEN((sockaddr_u *)dst->ai_addr))) {
535		closesocket(sockfd);
536		return 0;
537	}
538	closesocket(sockfd);
539	return 1;
540}
541