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