Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / ntp / dist / sntp / networking.c
blobd5173519efdedf3098b810e6c35c788919315f5b
1 /* $NetBSD$ */
3 #include "networking.h"
5 char adr_buf[INET6_ADDRSTRLEN];
8 /* resolve_hosts consumes an array of hostnames/addresses and its length, stores a pointer
9 * to the array with the resolved hosts in res and returns the size of the array res.
10 * pref_family enforces IPv4 or IPv6 depending on commandline options and system
11 * capability. If pref_family is NULL or PF_UNSPEC any compatible family will be accepted.
12 * Check here: Probably getaddrinfo() can do without ISC's IPv6 availability check?
14 int
15 resolve_hosts (
16 char **hosts,
17 int hostc,
18 struct addrinfo ***res,
19 int pref_family
22 register unsigned int a;
23 unsigned int resc;
24 struct addrinfo **tres;
26 if (hostc < 1 || NULL == res)
27 return 0;
29 tres = emalloc(sizeof(struct addrinfo *) * hostc);
31 for (a = 0, resc = 0; a < hostc; a++) {
32 struct addrinfo hints;
33 int error;
35 tres[resc] = NULL;
37 #ifdef DEBUG
38 printf("sntp resolve_hosts: Starting host resolution for %s...\n", hosts[a]);
39 #endif
41 memset(&hints, 0, sizeof(hints));
43 if (AF_UNSPEC == pref_family)
44 hints.ai_family = PF_UNSPEC;
45 else
46 hints.ai_family = pref_family;
48 hints.ai_socktype = SOCK_DGRAM;
50 error = getaddrinfo(hosts[a], "123", &hints, &tres[resc]);
52 if (error) {
53 size_t msg_length = strlen(hosts[a]) + 21;
54 char *logmsg = (char *) emalloc(sizeof(char) * msg_length);
56 snprintf(logmsg, msg_length, "Error looking up %s", hosts[a]);
57 #ifdef DEBUG
58 printf("%s\n", logmsg);
59 #endif
61 log_msg(logmsg, 1);
62 free(logmsg);
63 } else {
64 #ifdef DEBUG
65 for (dres = tres[resc]; dres; dres = dres->ai_next) {
66 getnameinfo(dres->ai_addr, dres->ai_addrlen, adr_buf, sizeof(adr_buf), NULL, 0, NI_NUMERICHOST);
67 STDLINE
68 printf("Resolv No.: %i Result of getaddrinfo for %s:\n", resc, hosts[a]);
69 printf("socktype: %i ", dres->ai_socktype);
70 printf("protocol: %i ", dres->ai_protocol);
71 printf("Prefered socktype: %i IP: %s\n", dres->ai_socktype, adr_buf);
72 STDLINE
74 #endif
75 resc++;
79 if (resc)
80 *res = realloc(tres, sizeof(struct addrinfo *) * resc);
81 else {
82 free(tres);
83 *res = NULL;
86 return resc;
89 /* Creates a socket and returns. */
90 void
91 create_socket (
92 SOCKET *rsock,
93 sockaddr_u *dest
96 *rsock = socket(AF(dest), SOCK_DGRAM, 0);
98 if (-1 == *rsock && ENABLED_OPT(NORMALVERBOSE))
99 printf("Failed to create UDP socket with family %d\n", AF(dest));
102 /* Send a packet */
103 void
104 sendpkt (
105 SOCKET rsock,
106 sockaddr_u *dest,
107 struct pkt *pkt,
108 int len
111 int cc;
113 #ifdef DEBUG
114 printf("sntp sendpkt: Packet data:\n");
115 pkt_output(pkt, len, stdout);
116 #endif
118 if (ENABLED_OPT(NORMALVERBOSE)) {
119 getnameinfo(&dest->sa, SOCKLEN(dest), adr_buf, sizeof(adr_buf), NULL, 0, NI_NUMERICHOST);
121 printf("sntp sendpkt: Sending packet to %s... ", adr_buf);
124 cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa, SOCKLEN(dest));
126 if (cc == SOCKET_ERROR) {
127 #ifdef DEBUG
128 printf("\n sntp sendpkt: Socket error: %i. Couldn't send packet!\n", cc);
129 #endif
131 if (errno != EWOULDBLOCK && errno != ENOBUFS) {
134 } else if (ENABLED_OPT(NORMALVERBOSE))
135 printf("Packet sent.\n");
138 /* Receive raw data */
140 recvdata (
141 SOCKET rsock,
142 sockaddr_u *sender,
143 char *rdata,
144 int rdata_length
147 GETSOCKNAME_SOCKLEN_TYPE slen;
148 int recvc;
150 #ifdef DEBUG
151 printf("sntp recvdata: Trying to receive data from...\n");
152 #endif
153 slen = sizeof(sender->sas);
154 recvc = recvfrom(rsock, rdata, rdata_length, 0,
155 &sender->sa, &slen);
156 #ifdef DEBUG
157 if (recvc > 0) {
158 printf("Received %d bytes from %s:\n", recvc, stoa(sender));
160 pkt_output((struct pkt *) rdata, recvc, stdout);
162 else {
163 saved_errno = errno;
164 printf("recvfrom error %d (%s)\n", errno, strerror(errno));
165 errno = saved_errno;
167 #endif
169 return recvc;
172 /* Receive data from broadcast. Couldn't finish that. Need to do some digging
173 * here, especially for protocol independence and IPv6 multicast */
174 int
175 recv_bcst_data (
176 SOCKET rsock,
177 char *rdata,
178 int rdata_len,
179 sockaddr_u *sas,
180 sockaddr_u *ras
183 struct timeval timeout_tv;
184 fd_set bcst_fd;
185 char *buf;
186 int btrue = 1;
187 int recv_bytes = 0;
190 setsockopt(rsock, SOL_SOCKET, SO_REUSEADDR, &btrue, sizeof(btrue));
192 if (IS_IPV4(sas)) {
193 struct ip_mreq mdevadr;
195 if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
196 if (ENABLED_OPT(NORMALVERBOSE))
197 printf("sntp recv_bcst_data: Couldn't bind() address.\n");
201 if (setsockopt(rsock, IPPROTO_IP, IP_MULTICAST_LOOP, &btrue, sizeof(btrue)) < 0) {
202 /* some error message regarding setting up multicast loop */
203 return BROADCAST_FAILED;
206 mdevadr.imr_multiaddr.s_addr = NSRCADR(sas);
207 mdevadr.imr_interface.s_addr = htonl(INADDR_ANY);
209 if (mdevadr.imr_multiaddr.s_addr == -1) {
210 if (ENABLED_OPT(NORMALVERBOSE)) {
211 printf("sntp recv_bcst_data: %s is not a broad-/multicast address, aborting...\n",
212 stoa(sas));
215 return BROADCAST_FAILED;
218 if (setsockopt(rsock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mdevadr, sizeof(mdevadr)) < 0) {
219 if (ENABLED_OPT(NORMALVERBOSE)) {
220 buf = ss_to_str(sas);
222 printf("sntp recv_bcst_data: Couldn't add IP membership for %s\n", buf);
224 free(buf);
226 return BROADCAST_FAILED;
230 #ifdef ISC_PLATFORM_HAVEIPV6
231 else if (IS_IPV6(sas)) {
232 #ifndef INCLUDE_IPV6_MULTICAST_SUPPORT
233 return BROADCAST_FAILED;
234 #else
235 struct ipv6_mreq mdevadr;
237 if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
238 if (ENABLED_OPT(NORMALVERBOSE))
239 printf("sntp recv_bcst_data: Couldn't bind() address.\n");
242 if (setsockopt(rsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &btrue, sizeof (btrue)) < 0) {
243 /* some error message regarding setting up multicast loop */
244 return BROADCAST_FAILED;
247 memset(&mdevadr, 0, sizeof(mdevadr));
248 mdevadr.ipv6mr_multiaddr = SOCK_ADDR6(sas);
250 if(!IN6_IS_ADDR_MULTICAST(&mdevadr.ipv6mr_multiaddr)) {
251 if(ENABLED_OPT(NORMALVERBOSE)) {
252 buf = ss_to_str(sas);
254 printf("sntp recv_bcst_data: %s is not a broad-/multicast address, aborting...\n", buf);
256 free(buf);
259 return BROADCAST_FAILED;
262 if (setsockopt(rsock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mdevadr, sizeof(mdevadr)) < 0) {
263 if(ENABLED_OPT(NORMALVERBOSE)) {
264 buf = ss_to_str(sas);
266 printf("sntp recv_bcst_data: Couldn't join group for %s\n", buf);
268 free(buf);
270 return BROADCAST_FAILED;
273 #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
275 #endif /* ISC_PLATFORM_HAVEIPV6 */
277 FD_ZERO(&bcst_fd);
278 FD_SET(rsock, &bcst_fd);
280 if(ENABLED_OPT(TIMEOUT))
281 timeout_tv.tv_sec = (int) OPT_ARG(TIMEOUT);
282 else
283 timeout_tv.tv_sec = 68; /* ntpd broadcasts every 64s */
285 switch(select(rsock + 1, &bcst_fd, 0, 0, &timeout_tv)) {
286 FD_CLR(rsock, &bcst_fd);
288 case -1:
289 if(ENABLED_OPT(NORMALVERBOSE))
290 printf("sntp recv_bcst_data: select() returned -1, an error occured, aborting.\n");
292 return BROADCAST_FAILED;
293 break;
295 case 0:
296 if(ENABLED_OPT(NORMALVERBOSE))
297 printf("sntp recv_bcst_data: select() reached timeout (%u sec), aborting.\n",
298 (unsigned)timeout_tv.tv_sec);
300 return BROADCAST_FAILED;
301 break;
303 default:
305 GETSOCKNAME_SOCKLEN_TYPE ss_len = sizeof(ras->sas);
307 recv_bytes = recvfrom(rsock, rdata, rdata_len, 0, &ras->sa, &ss_len);
311 if (recv_bytes == -1) {
312 if(ENABLED_OPT(NORMALVERBOSE))
313 printf("sntp recv_bcst_data: Failed to receive from broad-/multicast\n");
315 return BROADCAST_FAILED;
318 if (IS_IPV4(sas))
319 setsockopt(rsock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &btrue, sizeof(btrue));
320 #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
321 else if (IS_IPV6(sas))
322 setsockopt(rsock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &btrue, sizeof(btrue));
323 #endif
325 return recv_bytes;
328 int
329 recv_bcst_pkt (
330 SOCKET rsock,
331 struct pkt *rpkt,
332 sockaddr_u *sas
335 sockaddr_u sender;
336 register int a;
337 int is_authentic, has_mac = 0, orig_pkt_len;
339 char *rdata = emalloc(sizeof(char) * 256);
341 int pkt_len = recv_bcst_data(rsock, rdata, 256, sas, &sender);
344 if (pkt_len < 0) {
345 free(rdata);
347 return BROADCAST_FAILED;
350 /* No MAC, no authentication */
351 if (LEN_PKT_NOMAC == pkt_len)
352 has_mac = 0;
354 /* If there's more than just the NTP packet it should be a MAC */
355 else if(pkt_len > LEN_PKT_NOMAC)
356 has_mac = pkt_len - LEN_PKT_NOMAC;
357 else
358 if(ENABLED_OPT(NORMALVERBOSE)) {
359 printf("sntp recv_bcst_pkt: Funny packet length: %i. Discarding package.\n", pkt_len);
360 free(rdata);
362 return PACKET_UNUSEABLE;
365 /* Packet too big */
366 if(pkt_len > LEN_PKT_NOMAC + MAX_MAC_LEN) {
367 if(ENABLED_OPT(NORMALVERBOSE))
368 printf("sntp recv_bcst_pkt: Received packet is too big (%i bytes), trying again to get a useFable packet\n",
369 pkt_len);
370 free(rdata);
372 return PACKET_UNUSEABLE;
375 orig_pkt_len = pkt_len;
376 pkt_len = min(pkt_len, sizeof(struct pkt));
378 /* Let's copy the received data to the packet structure */
379 for (a = 0; a < pkt_len; a++)
380 if (a < orig_pkt_len)
381 ((char *)rpkt)[a] = rdata[a];
382 else
383 ((char *)rpkt)[a] = 0;
385 free(rdata);
387 /* MAC could be useable for us */
388 if (has_mac) {
389 /* Two more things that the MAC must conform to */
390 if (has_mac > MAX_MAC_LEN || has_mac % 4 != 0) {
391 is_authentic = 0; /* Or should we discard this packet? */
393 else {
394 if (MAX_MAC_LEN == has_mac) {
395 struct key *pkt_key = NULL;
397 /* Look for the key used by the server in the specified keyfile
398 * and if existent, fetch it or else leave the pointer untouched */
399 get_key(rpkt->mac[0], &pkt_key);
401 /* Seems like we've got a key with matching keyid */
402 if (pkt_key != NULL) {
403 /* Generate a md5sum of the packet with the key from our keyfile
404 * and compare those md5sums */
405 if (!auth_md5((char *) rpkt, has_mac, pkt_key)) {
406 if (ENABLED_OPT(AUTHENTICATION)) {
407 /* We want a authenticated packet */
408 if (ENABLED_OPT(NORMALVERBOSE)) {
409 char *hostname = ss_to_str(sas);
410 printf("sntp recv_bcst_pkt: Broadcast packet received from %s is not authentic. Will discard this packet.\n",
411 hostname);
413 free(hostname);
415 return SERVER_AUTH_FAIL;
417 else {
418 /* We don't know if the user wanted authentication so let's
419 * use it anyways */
420 if (ENABLED_OPT(NORMALVERBOSE)) {
421 char *hostname = ss_to_str(sas);
422 printf("sntp recv_bcst_pkt: Broadcast packet received from %s is not authentic. Authentication not enforced.\n",
423 hostname);
425 free(hostname);
428 is_authentic = 0;
431 else {
432 /* Yay! Things worked out! */
433 if (ENABLED_OPT(NORMALVERBOSE)) {
434 char *hostname = ss_to_str(sas);
435 printf("sntp recv_bcst_pkt: Broadcast packet received from %s successfully authenticated using key id %i.\n",
436 hostname, rpkt->mac[0]);
438 free(hostname);
441 is_authentic = 1;
448 /* Check for server's ntp version */
449 if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
450 PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
451 if (ENABLED_OPT(NORMALVERBOSE))
452 printf("sntp recv_bcst_pkt: Packet shows wrong version (%i)\n",
453 PKT_VERSION(rpkt->li_vn_mode));
455 return SERVER_UNUSEABLE;
458 /* We want a server to sync with */
459 if (PKT_MODE(rpkt->li_vn_mode) != MODE_BROADCAST
460 && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
461 if (ENABLED_OPT(NORMALVERBOSE))
462 printf("sntp recv_bcst_pkt: mode %d stratum %i\n",
463 PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
465 return SERVER_UNUSEABLE;
468 if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
469 char *ref_char;
471 if (ENABLED_OPT(NORMALVERBOSE))
472 printf("sntp recv_bcst_pkt: Stratum unspecified, going to check for KOD (stratum: %i)\n", rpkt->stratum);
474 ref_char = (char *) &rpkt->refid;
476 /* If it's a KOD packet we'll just use the KOD information */
477 if (ref_char[0] != 'X') {
478 if (strncmp(ref_char, "DENY", 4))
479 return KOD_DEMOBILIZE;
481 if (strncmp(ref_char, "RSTR", 4))
482 return KOD_DEMOBILIZE;
484 if (strncmp(ref_char, "RATE", 4))
485 return KOD_RATE;
487 /* There are other interesting kiss codes which might be interesting for authentication */
491 /* If the server is not synced it's not really useable for us */
492 if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
493 if (ENABLED_OPT(NORMALVERBOSE))
494 printf("recv_bcst_pkt: Server not in sync, skipping this server\n");
496 return SERVER_UNUSEABLE;
499 return pkt_len;
504 /* Fetch data, check if it's data for us and whether it's useable or not. If not, return
505 * a failure code so we can delete this server from our list and continue with another one.
507 int
508 recvpkt (
509 SOCKET rsock,
510 struct pkt *rpkt,
511 struct pkt *spkt
514 sockaddr_u sender;
515 char *rdata /* , done */;
517 register int a;
518 int has_mac, is_authentic, pkt_len, orig_pkt_len;
521 /* Much space, just to be sure */
522 rdata = emalloc(sizeof(char) * 256);
524 pkt_len = recvdata(rsock, &sender, rdata, 256);
526 #if 0 /* done uninitialized */
527 if (!done) {
528 /* Do something about it, first check for a maximum length of ntp packets,
529 * probably that's something we can avoid
532 #endif
534 /* Some checks to see if that packet is intended for us */
536 /* No MAC, no authentication */
537 if (LEN_PKT_NOMAC == pkt_len)
538 has_mac = 0;
540 /* If there's more than just the NTP packet it should be a MAC */
541 else if (pkt_len > LEN_PKT_NOMAC)
542 has_mac = pkt_len - LEN_PKT_NOMAC;
544 else {
545 if (ENABLED_OPT(NORMALVERBOSE))
546 printf("sntp recvpkt: Funny packet length: %i. Discarding package.\n", pkt_len);
547 free(rdata);
549 return PACKET_UNUSEABLE;
552 /* Packet too big */
553 if (pkt_len > LEN_PKT_MAC) {
554 if (ENABLED_OPT(NORMALVERBOSE))
555 printf("sntp recvpkt: Received packet is too big (%i bytes), trying again to get a useable packet\n",
556 pkt_len);
557 free(rdata);
559 return PACKET_UNUSEABLE;
562 orig_pkt_len = pkt_len;
563 pkt_len = min(pkt_len, sizeof(struct pkt));
565 for (a = 0; a < pkt_len; a++)
566 /* FIXME! */
567 if (a < orig_pkt_len)
568 ((char *) rpkt)[a] = rdata[a];
569 else
570 ((char *) rpkt)[a] = 0;
572 free(rdata);
573 rdata = NULL;
575 /* MAC could be useable for us */
576 if (has_mac) {
577 /* Two more things that the MAC must conform to */
578 if(has_mac > MAX_MAC_LEN || has_mac % 4 != 0) {
579 is_authentic = 0; /* Or should we discard this packet? */
581 else {
582 if (MAX_MAC_LEN == has_mac) {
583 struct key *pkt_key = NULL;
586 * Look for the key used by the server in the specified keyfile
587 * and if existent, fetch it or else leave the pointer untouched
589 get_key(rpkt->mac[0], &pkt_key);
591 /* Seems like we've got a key with matching keyid */
592 if (pkt_key != NULL) {
594 * Generate a md5sum of the packet with the key from our keyfile
595 * and compare those md5sums
597 if (!auth_md5((char *) rpkt, has_mac, pkt_key)) {
598 if (ENABLED_OPT(AUTHENTICATION)) {
599 /* We want a authenticated packet */
600 if (ENABLED_OPT(NORMALVERBOSE)) {
601 char *hostname = ss_to_str(&sender);
602 printf("sntp recvpkt: Broadcast packet received from %s is not authentic. Will discard this packet.\n",
603 hostname);
605 free(hostname);
607 return SERVER_AUTH_FAIL;
609 else {
611 * We don't know if the user wanted authentication so let's
612 * use it anyways
614 if (ENABLED_OPT(NORMALVERBOSE)) {
615 char *hostname = ss_to_str(&sender);
616 printf("sntp recvpkt: Broadcast packet received from %s is not authentic. Authentication not enforced.\n",
617 hostname);
619 free(hostname);
622 is_authentic = 0;
625 else {
626 /* Yay! Things worked out! */
627 if (ENABLED_OPT(NORMALVERBOSE)) {
628 char *hostname = ss_to_str(&sender);
629 printf("sntp recvpkt: Broadcast packet received from %s successfully authenticated using key id %i.\n",
630 hostname, rpkt->mac[0]);
632 free(hostname);
635 is_authentic = 1;
642 /* Check for server's ntp version */
643 if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
644 PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
645 if (ENABLED_OPT(NORMALVERBOSE))
646 printf("sntp recvpkt: Packet got wrong version (%i)\n", PKT_VERSION(rpkt->li_vn_mode));
648 return SERVER_UNUSEABLE;
651 /* We want a server to sync with */
652 if (PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER &&
653 PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
654 if (ENABLED_OPT(NORMALVERBOSE))
655 printf("sntp recvpkt: mode %d stratum %i\n",
656 PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
658 return SERVER_UNUSEABLE;
661 /* Stratum is unspecified (0) check what's going on */
662 if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
663 char *ref_char;
665 if (ENABLED_OPT(NORMALVERBOSE))
666 printf("sntp recvpkt: Stratum unspecified, going to check for KOD (stratum: %i)\n", rpkt->stratum);
669 ref_char = (char *) &rpkt->refid;
671 if (ENABLED_OPT(NORMALVERBOSE))
672 printf("sntp recvpkt: Packet refid: %c%c%c%c\n", ref_char[0], ref_char[1], ref_char[2], ref_char[3]);
674 /* If it's a KOD packet we'll just use the KOD information */
675 if (ref_char[0] != 'X') {
676 if (!strncmp(ref_char, "DENY", 4))
677 return KOD_DEMOBILIZE;
679 if (!strncmp(ref_char, "RSTR", 4))
680 return KOD_DEMOBILIZE;
682 if (!strncmp(ref_char, "RATE", 4))
683 return KOD_RATE;
685 /* There are other interesting kiss codes which might be interesting for authentication */
689 /* If the server is not synced it's not really useable for us */
690 if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
691 if (ENABLED_OPT(NORMALVERBOSE))
692 printf("sntp recvpkt: Server not in sync, skipping this server\n");
694 return SERVER_UNUSEABLE;
698 * Decode the org timestamp and make sure we're getting a response
699 * to our last request.
702 #ifdef DEBUG
703 printf("rpkt->org:\n");
704 l_fp_output(&rpkt->org, stdout);
705 printf("spkt->xmt:\n");
706 l_fp_output(&spkt->xmt, stdout);
707 #endif
709 if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
710 if (ENABLED_OPT(NORMALVERBOSE))
711 printf("sntp recvpkt: pkt.org and peer.xmt differ\n");
713 return PACKET_UNUSEABLE;
716 return pkt_len;
720 * is_reachable - check to see if we have a route to given destination
723 is_reachable (
724 struct addrinfo *dst
727 SOCKET sockfd;
729 sockfd = socket(dst->ai_family, SOCK_DGRAM, 0);
731 if (-1 == sockfd) {
732 #ifdef DEBUG
733 printf("is_reachable: Couldn't create socket\n");
734 #endif
735 return 0;
738 if (connect(sockfd, dst->ai_addr, SOCKLEN((sockaddr_u *)dst->ai_addr))) {
739 closesocket(sockfd);
740 return 0;
743 closesocket(sockfd);
744 return 1;