/* * ntp_peer.c - management of data maintained for peer associations */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "ntpd.h" #include "ntp_lists.h" #include "ntp_stdlib.h" #include "ntp_control.h" #include #ifdef OPENSSL #include "openssl/rand.h" #endif /* OPENSSL */ #ifdef SYS_WINNT extern int accept_wildcard_if_for_winnt; #endif /* * Table of valid association combinations * --------------------------------------- * * packet->mode * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST * ---------- | --------------------------------------------- * NO_PEER | e 1 0 1 1 1 * ACTIVE | e 1 1 0 0 0 * PASSIVE | e 1 e 0 0 0 * CLIENT | e 0 0 0 1 0 * SERVER | e 0 0 0 0 0 * BCAST | e 0 0 0 0 0 * BCLIENT | e 0 0 0 e 1 * * One point to note here: a packet in BCAST mode can potentially match * a peer in CLIENT mode, but we that is a special case and we check for * that early in the decision process. This avoids having to keep track * of what kind of associations are possible etc... We actually * circumvent that problem by requiring that the first b(m)roadcast * received after the change back to BCLIENT mode sets the clock. */ #define AM_MODES 7 /* number of rows and columns */ #define NO_PEER 0 /* action when no peer is found */ int AM[AM_MODES][AM_MODES] = { /* { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ /*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, /*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH}, /*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT}, }; #define MATCH_ASSOC(x, y) AM[(x)][(y)] /* * These routines manage the allocation of memory to peer structures * and the maintenance of the peer hash table. The three main entry * points are findpeer(), which looks for matching peer structures in * the peer list, newpeer(), which allocates a new peer structure and * adds it to the list, and unpeer(), which demobilizes the association * and deallocates the structure. */ /* * Peer hash tables */ struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */ int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */ int assoc_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ static struct peer *peer_free; /* peer structures free list */ int peer_free_count; /* count of free structures */ /* * Association ID. We initialize this value randomly, then assign a new * value every time the peer structure is incremented. */ static associd_t current_association_ID; /* association ID */ /* * Memory allocation watermarks. */ #define INIT_PEER_ALLOC 15 /* initialize for 15 peers */ #define INC_PEER_ALLOC 5 /* when run out, add 5 more */ /* * Miscellaneous statistic counters which may be queried. */ u_long peer_timereset; /* time stat counters zeroed */ u_long findpeer_calls; /* calls to findpeer */ u_long assocpeer_calls; /* calls to findpeerbyassoc */ u_long peer_allocations; /* allocations from free list */ u_long peer_demobilizations; /* structs freed to free list */ int total_peer_structs; /* peer structs */ int peer_associations; /* mobilized associations */ int peer_preempt; /* preemptable associations */ static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */ static void getmorepeermem (void); static struct interface *select_peerinterface (struct peer *, sockaddr_u *, struct interface *, u_char); static int score(struct peer *); /* * init_peer - initialize peer data structures and counters * * N.B. We use the random number routine in here. It had better be * initialized prior to getting here. */ void init_peer(void) { register int i; /* * Clear hash tables and counters. */ memset(peer_hash, 0, sizeof(peer_hash)); memset(peer_hash_count, 0, sizeof(peer_hash_count)); memset(assoc_hash, 0, sizeof(assoc_hash)); memset(assoc_hash_count, 0, sizeof(assoc_hash_count)); /* * Clear stat counters */ findpeer_calls = peer_allocations = 0; assocpeer_calls = peer_demobilizations = 0; /* * Initialize peer memory. */ peer_free = NULL; for (i = 0; i < INIT_PEER_ALLOC; i++) LINK_SLIST(peer_free, &init_peer_alloc[i], next); total_peer_structs = INIT_PEER_ALLOC; peer_free_count = INIT_PEER_ALLOC; /* * Initialize our first association ID */ while ((current_association_ID = ntp_random() & 0xffff) == 0); } /* * getmorepeermem - add more peer structures to the free list */ static void getmorepeermem(void) { register int i; register struct peer *peer; peer = (struct peer *)emalloc(INC_PEER_ALLOC * sizeof(struct peer)); for (i = 0; i < INC_PEER_ALLOC; i++) { LINK_SLIST(peer_free, peer, next); peer++; } total_peer_structs += INC_PEER_ALLOC; peer_free_count += INC_PEER_ALLOC; } /* * findexistingpeer - return a pointer to a peer in the hash table */ struct peer * findexistingpeer( sockaddr_u *addr, struct peer *start_peer, int mode ) { register struct peer *peer; /* * start_peer is included so we can locate instances of the * same peer through different interfaces in the hash table. */ if (NULL == start_peer) peer = peer_hash[NTP_HASH_ADDR(addr)]; else peer = start_peer->next; while (peer != NULL) { if (SOCK_EQ(addr, &peer->srcadr) && NSRCPORT(addr) == NSRCPORT(&peer->srcadr) && (-1 == mode || peer->hmode == mode)) break; peer = peer->next; } return (peer); } /* * findpeer - find and return a peer in the hash table. */ struct peer * findpeer( sockaddr_u *srcadr, struct interface *dstadr, int pkt_mode, int *action ) { register struct peer *peer; u_int hash; findpeer_calls++; hash = NTP_HASH_ADDR(srcadr); for (peer = peer_hash[hash]; peer != NULL; peer = peer->next) { if (SOCK_EQ(srcadr, &peer->srcadr) && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) { /* * if the association matching rules determine * that this is not a valid combination, then * look for the next valid peer association. */ *action = MATCH_ASSOC(peer->hmode, pkt_mode); /* * if an error was returned, exit back right * here. */ if (*action == AM_ERR) return ((struct peer *)0); /* * if a match is found, we stop our search. */ if (*action != AM_NOMATCH) break; } } /* * If no matching association is found */ if (peer == 0) { *action = MATCH_ASSOC(NO_PEER, pkt_mode); return ((struct peer *)0); } set_peerdstadr(peer, dstadr); return (peer); } /* * findpeerbyassocid - find and return a peer using his association ID */ struct peer * findpeerbyassoc( u_int assoc ) { register struct peer *peer; u_int hash; assocpeer_calls++; hash = assoc & NTP_HASH_MASK; for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) { if (assoc == peer->associd) return (peer); } return (NULL); } /* * clear_all - flush all time values for all associations */ void clear_all(void) { struct peer *peer, *next_peer; int n; /* * This routine is called when the clock is stepped, and so all * previously saved time values are untrusted. */ for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != 0; peer = next_peer) { next_peer = peer->next; if (!(peer->cast_flags & (MDF_ACAST | MDF_MCAST | MDF_BCAST))) { peer_clear(peer, "STEP"); } } } #ifdef DEBUG if (debug) printf("clear_all: at %lu\n", current_time); #endif } /* * score_all() - determine if an association can be demobilized */ int score_all( struct peer *peer /* peer structure pointer */ ) { struct peer *speer, *next_peer; int n; int temp, tamp; /* * This routine finds the minimum score for all ephemeral * assocations and returns > 0 if the association can be * demobilized. */ tamp = score(peer); temp = 100; for (n = 0; n < NTP_HASH_SIZE; n++) { for (speer = peer_hash[n]; speer != 0; speer = next_peer) { int x; next_peer = speer->next; if ((x = score(speer)) < temp && (peer->flags & FLAG_PREEMPT)) temp = x; } } #ifdef DEBUG if (debug) printf("score_all: at %lu score %d min %d\n", current_time, tamp, temp); #endif if (tamp != temp) temp = 0; return (temp); } /* * score() - calculate preemption score */ static int score( struct peer *peer /* peer structure pointer */ ) { int temp; /* * This routine calculates the premption score from the peer * error bits and status. Increasing values are more cherished. */ temp = 0; if (!(peer->flash & TEST10)) temp++; /* 1 good synch and stratum */ if (!(peer->flash & TEST13)) temp++; /* 2 reachable */ if (!(peer->flash & TEST12)) temp++; /* 3 no loop */ if (!(peer->flash & TEST11)) temp++; /* 4 good distance */ if (peer->status >= CTL_PST_SEL_SELCAND) temp++; /* 5 in the hunt */ if (peer->status != CTL_PST_SEL_EXCESS) temp++; /* 6 not spare tire */ return (temp); /* selection status */ } /* * unpeer - remove peer structure from hash table and free structure */ void unpeer( struct peer *peer_to_remove ) { register struct peer *unlinked; int hash; char tbuf[80]; snprintf(tbuf, sizeof(tbuf), "assoc %d", peer_to_remove->associd); report_event(PEVNT_DEMOBIL, peer_to_remove, tbuf); set_peerdstadr(peer_to_remove, NULL); hash = NTP_HASH_ADDR(&peer_to_remove->srcadr); peer_hash_count[hash]--; peer_demobilizations++; peer_associations--; if (peer_to_remove->flags & FLAG_PREEMPT) peer_preempt--; #ifdef REFCLOCK /* * If this peer is actually a clock, shut it down first */ if (peer_to_remove->flags & FLAG_REFCLOCK) refclock_unpeer(peer_to_remove); #endif peer_to_remove->action = 0; /* disable timeout actions */ UNLINK_SLIST(unlinked, peer_hash[hash], peer_to_remove, next, struct peer); if (NULL == unlinked) { peer_hash_count[hash]++; msyslog(LOG_ERR, "peer struct for %s not in table!", stoa(&peer_to_remove->srcadr)); } /* * Remove him from the association hash as well. */ hash = peer_to_remove->associd & NTP_HASH_MASK; assoc_hash_count[hash]--; UNLINK_SLIST(unlinked, assoc_hash[hash], peer_to_remove, ass_next, struct peer); if (NULL == unlinked) { assoc_hash_count[hash]++; msyslog(LOG_ERR, "peer struct for %s not in association table!", stoa(&peer_to_remove->srcadr)); } LINK_SLIST(peer_free, peer_to_remove, next); peer_free_count++; } /* * peer_config - configure a new association */ struct peer * peer_config( sockaddr_u *srcadr, struct interface *dstadr, int hmode, int version, int minpoll, int maxpoll, u_int flags, int ttl, keyid_t key, u_char *keystr, char *dns_name ) { u_char cast_flags; u_long next_update; struct peer *peer = NULL; /* * We do a dirty little jig to figure the cast flags. This is * probably not the best place to do this, at least until the * configure code is rebuilt. Note only one flag can be set. */ switch (hmode) { case MODE_BROADCAST: if (IS_MCAST(srcadr)) cast_flags = MDF_MCAST; else cast_flags = MDF_BCAST; break; case MODE_CLIENT: if (IS_MCAST(srcadr)) cast_flags = MDF_ACAST; else cast_flags = MDF_UCAST; break; default: cast_flags = MDF_UCAST; } /* * Mobilize the association and initialize its variables. If * emulating ntpdate, force iburst. */ if (mode_ntpdate) flags |= FLAG_IBURST; peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, flags | FLAG_CONFIG, cast_flags, ttl, key); if (peer) { peer->dns_name = strdup(dns_name); next_update = get_dns_flags(dns_name, peer); if (dns_timer == 0 || (dns_timer > next_update)) dns_timer = next_update; } return peer; } /* * setup peer dstadr field keeping it in sync with the interface * structures */ void set_peerdstadr( struct peer *peer, struct interface *interface ) { struct peer *unlinked; if (peer->dstadr != interface) { if (interface != NULL && (peer->cast_flags & MDF_BCLNT) && (interface->flags & INT_MCASTIF) && peer->burst) { /* * don't accept updates to a true multicast * reception interface while a BCLNT peer is * running it's unicast protocol */ return; } if (peer->dstadr != NULL) { peer->dstadr->peercnt--; UNLINK_SLIST(unlinked, peer->dstadr->peers, peer, ilink, struct peer); msyslog(LOG_INFO, "%s interface %s -> %s", stoa(&peer->srcadr), stoa(&peer->dstadr->sin), (interface != NULL) ? stoa(&interface->sin) : "(null)"); } peer->dstadr = interface; if (peer->dstadr != NULL) { LINK_SLIST(peer->dstadr->peers, peer, ilink); peer->dstadr->peercnt++; } } } /* * attempt to re-rebind interface if necessary */ static void peer_refresh_interface( struct peer *peer ) { struct interface *niface, *piface; niface = select_peerinterface(peer, &peer->srcadr, NULL, peer->cast_flags); #ifdef DEBUG if (debug > 3) { printf( "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x: new interface: ", peer->dstadr == NULL ? "" : stoa(&peer->dstadr->sin), stoa(&peer->srcadr), peer->hmode, peer->version, peer->minpoll, peer->maxpoll, peer->flags, peer->cast_flags, peer->ttl, peer->keyid); if (niface != NULL) { printf( "fd=%d, bfd=%d, name=%.16s, flags=0x%x, scope=%d, ", niface->fd, niface->bfd, niface->name, niface->flags, niface->scopeid); printf(", sin=%s", stoa((&niface->sin))); if (niface->flags & INT_BROADCAST) printf(", bcast=%s,", stoa((&niface->bcast))); printf(", mask=%s\n", stoa((&niface->mask))); } else { printf("\n"); } } #endif piface = peer->dstadr; set_peerdstadr(peer, niface); if (peer->dstadr) { /* * clear crypto if we change the local address */ if (peer->dstadr != piface && !(peer->cast_flags & MDF_ACAST) && peer->pmode != MODE_BROADCAST) peer_clear(peer, "XFAC"); /* * Broadcast needs the socket enabled for broadcast */ if (peer->cast_flags & MDF_BCAST) { enable_broadcast(peer->dstadr, &peer->srcadr); } /* * Multicast needs the socket interface enabled for * multicast */ if (peer->cast_flags & MDF_MCAST) { enable_multicast_if(peer->dstadr, &peer->srcadr); } } } /* * refresh_all_peerinterfaces - see that all interface bindings are up * to date */ void refresh_all_peerinterfaces(void) { struct peer *peer, *next_peer; int n; /* * this is called when the interface list has changed * give all peers a chance to find a better interface */ for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != 0; peer = next_peer) { next_peer = peer->next; peer_refresh_interface(peer); } } } /* * find an interface suitable for the src address */ static struct interface * select_peerinterface( struct peer * peer, sockaddr_u * srcadr, struct interface * dstadr, u_char cast_flags ) { struct interface *interface; /* * Initialize the peer structure and dance the interface jig. * Reference clocks step the loopback waltz, the others * squaredance around the interface list looking for a buddy. If * the dance peters out, there is always the wildcard interface. * This might happen in some systems and would preclude proper * operation with public key cryptography. */ if (ISREFCLOCKADR(srcadr)) interface = loopback_interface; else if (cast_flags & (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) { interface = findbcastinter(srcadr); #ifdef DEBUG if (debug > 3) { if (interface != NULL) printf("Found *-cast interface address %s, for address %s\n", stoa(&(interface)->sin), stoa(srcadr)); else printf("No *-cast local address found for address %s\n", stoa(srcadr)); } #endif /* * If it was a multicast packet, * findbcastinter() may not find it, so try a * little harder. */ if (interface == ANY_INTERFACE_CHOOSE(srcadr)) interface = findinterface(srcadr); } else if (dstadr != NULL && dstadr != ANY_INTERFACE_CHOOSE(srcadr)) interface = dstadr; else interface = findinterface(srcadr); /* * we do not bind to the wildcard interfaces for output * as our (network) source address would be undefined and * crypto will not work without knowing the own transmit address */ if (interface != NULL && interface->flags & INT_WILDCARD) #ifdef SYS_WINNT if ( !accept_wildcard_if_for_winnt ) #endif interface = NULL; return interface; } /* * newpeer - initialize a new peer association */ struct peer * newpeer( sockaddr_u *srcadr, struct interface *dstadr, int hmode, int version, int minpoll, int maxpoll, u_int flags, u_char cast_flags, int ttl, keyid_t key ) { struct peer *peer; u_int hash; char tbuf[80]; #ifdef OPENSSL /* * If Autokey is requested but not configured, complain loudly. */ if (!crypto_flags) { if (key > NTP_MAXKEY) { return (NULL); } else if (flags & FLAG_SKEY) { msyslog(LOG_ERR, "Autokey not configured"); return (NULL); } } #endif /* OPENSSL */ /* * First search from the beginning for an association with given * remote address and mode. If an interface is given, search * from there to find the association which matches that * destination. If the given interface is "any", track down the * actual interface, because that's what gets put into the peer * structure. */ peer = findexistingpeer(srcadr, NULL, hmode); if (dstadr != NULL) { while (peer != NULL) { if (peer->dstadr == dstadr) break; if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && peer->dstadr == findinterface(srcadr)) break; peer = findexistingpeer(srcadr, peer, hmode); } } /* * If a peer is found, this would be a duplicate and we don't * allow that. This is mostly to avoid duplicate pool * associations. */ if (peer != NULL) return (NULL); /* * Allocate a new peer structure. Some dirt here, since some of * the initialization requires knowlege of our system state. */ if (peer_free_count == 0) getmorepeermem(); UNLINK_HEAD_SLIST(peer, peer_free, next); peer_free_count--; peer_associations++; if (flags & FLAG_PREEMPT) peer_preempt++; memset(peer, 0, sizeof(*peer)); /* * Assign an association ID and increment the system variable. */ peer->associd = current_association_ID; if (++current_association_ID == 0) ++current_association_ID; DPRINTF(3, ("newpeer: cast flags: 0x%x for address: %s\n", cast_flags, stoa(srcadr))); peer->srcadr = *srcadr; set_peerdstadr(peer, select_peerinterface(peer, srcadr, dstadr, cast_flags)); peer->hmode = (u_char)hmode; peer->version = (u_char)version; peer->flags = flags; /* * It is an error to set minpoll less than NTP_MINPOLL or to * set maxpoll greater than NTP_MAXPOLL. However, minpoll is * clamped not greater than NTP_MAXPOLL and maxpoll is clamped * not less than NTP_MINPOLL without complaint. Finally, * minpoll is clamped not greater than maxpoll. */ if (minpoll == 0) peer->minpoll = NTP_MINDPOLL; else { peer->flags |= FLAG_UMINPOLL; peer->minpoll = (u_char)min(minpoll, NTP_MAXPOLL); } if (maxpoll == 0) peer->maxpoll = NTP_MAXDPOLL; else { peer->flags |= FLAG_UMAXPOLL; peer->maxpoll = (u_char)max(maxpoll, NTP_MINPOLL); } if (peer->minpoll > peer->maxpoll) peer->minpoll = peer->maxpoll; if (peer->dstadr) DPRINTF(3, ("newpeer: using fd %d and our addr %s\n", peer->dstadr->fd, stoa(&peer->dstadr->sin))); else DPRINTF(3, ("newpeer: local interface currently not bound\n")); /* * Broadcast needs the socket enabled for broadcast */ if ((cast_flags & MDF_BCAST) && peer->dstadr) enable_broadcast(peer->dstadr, srcadr); /* * Multicast needs the socket interface enabled for multicast */ if ((cast_flags & MDF_MCAST) && peer->dstadr) enable_multicast_if(peer->dstadr, srcadr); #ifdef OPENSSL if (key > NTP_MAXKEY) peer->flags |= FLAG_SKEY; #endif /* OPENSSL */ peer->cast_flags = cast_flags; peer->ttl = (u_char)ttl; peer->keyid = key; peer->precision = sys_precision; peer->hpoll = peer->minpoll; if (cast_flags & MDF_ACAST) peer_clear(peer, "ACST"); else if (cast_flags & MDF_MCAST) peer_clear(peer, "MCST"); else if (cast_flags & MDF_BCAST) peer_clear(peer, "BCST"); else peer_clear(peer, "INIT"); if (mode_ntpdate) peer_ntpdate++; /* * Note time on statistics timers. */ peer->timereset = current_time; peer->timereachable = current_time; peer->timereceived = current_time; #ifdef REFCLOCK if (ISREFCLOCKADR(&peer->srcadr)) { /* * We let the reference clock support do clock * dependent initialization. This includes setting * the peer timer, since the clock may have requirements * for this. */ if (maxpoll == 0) peer->maxpoll = peer->minpoll; if (!refclock_newpeer(peer)) { /* * Dump it, something screwed up */ set_peerdstadr(peer, NULL); LINK_SLIST(peer_free, peer, next); peer_free_count++; return (NULL); } } #endif /* * Put the new peer in the hash tables. */ hash = NTP_HASH_ADDR(&peer->srcadr); LINK_SLIST(peer_hash[hash], peer, next); peer_hash_count[hash]++; hash = peer->associd & NTP_HASH_MASK; LINK_SLIST(assoc_hash[hash], peer, ass_next); assoc_hash_count[hash]++; snprintf(tbuf, sizeof(tbuf), "assoc %d", peer->associd); report_event(PEVNT_MOBIL, peer, tbuf); DPRINTF(1, ("newpeer: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x\n", peer->dstadr == NULL ? "" : stoa(&peer->dstadr->sin), stoa(&peer->srcadr), peer->hmode, peer->version, peer->minpoll, peer->maxpoll, peer->flags, peer->cast_flags, peer->ttl, peer->keyid)); return (peer); } /* * peer_clr_stats - clear peer module statiistics counters */ void peer_clr_stats(void) { findpeer_calls = 0; assocpeer_calls = 0; peer_allocations = 0; peer_demobilizations = 0; peer_timereset = current_time; } /* * peer_reset - reset statistics counters */ void peer_reset( struct peer *peer ) { if (peer == NULL) return; peer->timereset = current_time; peer->sent = 0; peer->received = 0; peer->processed = 0; peer->badauth = 0; peer->bogusorg = 0; peer->oldpkt = 0; peer->seldisptoolarge = 0; peer->selbroken = 0; } /* * peer_all_reset - reset all peer statistics counters */ void peer_all_reset(void) { struct peer *peer; int hash; for (hash = 0; hash < NTP_HASH_SIZE; hash++) for (peer = peer_hash[hash]; peer != 0; peer = peer->next) peer_reset(peer); } /* * findmanycastpeer - find and return a manycast peer */ struct peer * findmanycastpeer( struct recvbuf *rbufp /* receive buffer pointer */ ) { register struct peer *peer; struct pkt *pkt; l_fp p_org; int i; /* * This routine is called upon arrival of a server-mode message * from a manycast client. Search the peer list for a manycast * client association where the last transmit timestamp matches * the originate timestamp. This assumes the transmit timestamps * for possibly more than one manycast association are unique. */ pkt = &rbufp->recv_pkt; for (i = 0; i < NTP_HASH_SIZE; i++) { if (peer_hash_count[i] == 0) continue; for (peer = peer_hash[i]; peer != 0; peer = peer->next) { if (peer->cast_flags & MDF_ACAST) { NTOHL_FP(&pkt->org, &p_org); if (L_ISEQU(&p_org, &peer->aorg)) return (peer); } } } return (NULL); } #include u_long get_dns_flags( char *dns_name, struct peer* peer) { dns_handle_t handle; dns_reply_t *reply; dns_TXT_record_t *txt; uint16_t klass, type; handle = dns_open(NULL); if (!handle) { return 0; } dns_class_number("IN", &klass); dns_type_number("TXT", &type); reply = dns_lookup(handle, dns_name, klass, type); if (reply) { int a; #if DEBUG if (debug > 1) { dns_print_reply(reply, stdout, 0xffff); printf("status: %d\n", reply->status); /* skip if != DNS_STATUS_OK */ printf("answer count: %d\n", reply->header->ancount); /* skip if <= 0 */ } #endif for (a = 0; a < reply->header->ancount; a++) { if ((reply->answer[a]->dnstype != type) || (reply->answer[a]->dnsclass != klass)) continue; /* ignore non-TXT */ txt = reply->answer[a]->data.TXT; if (txt) { int s; u_int old_flags = peer->flags & (FLAG_BURST | FLAG_IBURST); u_int new_flags = 0; u_char minpoll = NTP_MINDPOLL; u_char maxpoll = NTP_MAXDPOLL; for (s = 0; s < txt->string_count; s++) { #if DEBUG if (debug > 1) printf("%d: %s\n", s, txt->strings[s]); #endif if (0 == strncmp(txt->strings[s], "ntp ", 4)) { char *next = txt->strings[s]+4; char *arg, *p; long arg_val; if (peer->dns_ttl != reply->answer[a]->ttl) { peer->dns_ttl = reply->answer[a]->ttl; msyslog(LOG_INFO, "DNS %s ttl %d", dns_name, peer->dns_ttl); } if (peer->dns_ttl != 0) peer->dns_update = current_time + peer->dns_ttl; while (NULL != (p = strsep(&next, " \t"))) { switch (*p) { case 'b': /* burst */ if (strcmp(p, "burst") == 0) { new_flags |= FLAG_BURST; } else { msyslog(LOG_WARNING, "DNS %s unknown configuration [%s]", dns_name, p); } break; case 'i': /* iburst */ if (0 == strcmp(p, "iburst")) { new_flags |= FLAG_IBURST; } else { msyslog(LOG_WARNING, "DNS %s unknown configuration [%s]", dns_name, p); } break; case 'm': /* min|maxpoll */ if ((0 == (strcmp(p, "minpoll"))) || (0 == strcmp(p, "maxpoll"))) { if (next) { arg = strsep(&next, " \t"); arg_val = strtol(arg, NULL, 10); if (p[1] == 'i') { minpoll = (u_char)max(NTP_MINPOLL, arg_val); } else { maxpoll = (u_char)min(NTP_MAXPOLL, arg_val); } } else { msyslog(LOG_WARNING, "DNS %s option %s missing numeric argument", dns_name, p); } } else { msyslog(LOG_WARNING, "DNS %s unknown configuration [%s]", dns_name, p); } break; default: msyslog(LOG_WARNING, "DNS %s unknown configuration [%s]", dns_name, p); break; } } } /* ignore random txt records */ } if (0 == (peer->flags & FLAG_UMINPOLL) && (minpoll != peer->minpoll)) { peer->minpoll = minpoll; msyslog(LOG_INFO, "DNS %s minpoll %d", dns_name, peer->minpoll); } if (0 == (peer->flags & FLAG_UMAXPOLL) && (maxpoll != peer->maxpoll)) { peer->maxpoll = maxpoll; msyslog(LOG_INFO, "DNS %s maxpoll %d", dns_name, peer->maxpoll); } if (new_flags != old_flags) { u_int changes; if (peer->flags & FLAG_UIBURST) /* Don't override config setting */ new_flags |= FLAG_IBURST; if (peer->flags & FLAG_IBURST) new_flags |= FLAG_IBURST; changes = new_flags ^ old_flags; if (changes & FLAG_IBURST) { if (new_flags & FLAG_IBURST) peer->flags |= FLAG_IBURST; else peer->flags &= ~FLAG_IBURST; msyslog(LOG_INFO, "DNS %s %ciburst", dns_name, (new_flags & FLAG_IBURST) ? '+' : '-'); } if (changes & FLAG_BURST) { if (new_flags & FLAG_BURST) peer->flags |= FLAG_BURST; else peer->flags &= ~FLAG_BURST; msyslog(LOG_INFO, "DNS %s %cburst", dns_name, (new_flags & FLAG_BURST) ? '+' : '-'); } } } } dns_free_reply(reply); } dns_free(handle); return peer->dns_update; }