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