/* $OpenBSD: server.c,v 1.38 2013/10/21 08:48:22 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * Copyright (c) 2004 Alexander Guy * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "ntpd.h" int setup_listeners(struct servent *se, struct ntpd_conf *lconf, u_int *cnt) { struct listen_addr *la, *nla, *lap; struct ifaddrs *ifa, *ifap; struct sockaddr *sa; struct ifreq ifr; u_int8_t *a6; size_t sa6len = sizeof(struct in6_addr); u_int new_cnt = 0; int tos = IPTOS_LOWDELAY, rdomain, fd; TAILQ_FOREACH(lap, &lconf->listen_addrs, entry) { switch (lap->sa.ss_family) { case AF_UNSPEC: if (getifaddrs(&ifa) == -1) fatal("getifaddrs"); for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { sa = ifap->ifa_addr; if (sa == NULL || (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)) continue; if (SA_LEN(sa) == 0) continue; strlcpy(ifr.ifr_name, ifap->ifa_name, sizeof(ifr.ifr_name)); fd = socket(AF_INET, SOCK_DGRAM, 0); if (ioctl(fd, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) rdomain = 0; else rdomain = ifr.ifr_rdomainid; close(fd); if (lap->rtable != -1 && rdomain != lap->rtable) continue; if (sa->sa_family == AF_INET && ((struct sockaddr_in *)sa)->sin_addr.s_addr == INADDR_ANY) continue; if (sa->sa_family == AF_INET6) { a6 = ((struct sockaddr_in6 *)sa)-> sin6_addr.s6_addr; if (memcmp(a6, &in6addr_any, sa6len) == 0) continue; } if ((la = calloc(1, sizeof(struct listen_addr))) == NULL) fatal("setup_listeners calloc"); memcpy(&la->sa, sa, SA_LEN(sa)); la->rtable = rdomain; TAILQ_INSERT_TAIL(&lconf->listen_addrs, la, entry); } freeifaddrs(ifa); default: continue; } } for (la = TAILQ_FIRST(&lconf->listen_addrs); la; ) { switch (la->sa.ss_family) { case AF_INET: if (((struct sockaddr_in *)&la->sa)->sin_port == 0) ((struct sockaddr_in *)&la->sa)->sin_port = se->s_port; break; case AF_INET6: if (((struct sockaddr_in6 *)&la->sa)->sin6_port == 0) ((struct sockaddr_in6 *)&la->sa)->sin6_port = se->s_port; break; case AF_UNSPEC: nla = TAILQ_NEXT(la, entry); TAILQ_REMOVE(&lconf->listen_addrs, la, entry); free(la); la = nla; continue; default: fatalx("king bula sez: af borked"); } log_info("listening on %s %s", log_sockaddr((struct sockaddr *)&la->sa), print_rtable(la->rtable)); if ((la->fd = socket(la->sa.ss_family, SOCK_DGRAM, 0)) == -1) fatal("socket"); if (la->sa.ss_family == AF_INET && setsockopt(la->fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) log_warn("setsockopt IPTOS_LOWDELAY"); if (la->rtable != -1 && setsockopt(la->fd, SOL_SOCKET, SO_RTABLE, &la->rtable, sizeof(la->rtable)) == -1) fatal("setup_listeners setsockopt SO_RTABLE"); if (bind(la->fd, (struct sockaddr *)&la->sa, SA_LEN((struct sockaddr *)&la->sa)) == -1) { log_warn("bind on %s failed, skipping", log_sockaddr((struct sockaddr *)&la->sa)); close(la->fd); nla = TAILQ_NEXT(la, entry); TAILQ_REMOVE(&lconf->listen_addrs, la, entry); free(la); la = nla; continue; } new_cnt++; la = TAILQ_NEXT(la, entry); } *cnt = new_cnt; return (0); } int server_dispatch(int fd, struct ntpd_conf *lconf) { ssize_t size; u_int8_t version; double rectime; struct sockaddr_storage fsa; socklen_t fsa_len; struct ntp_msg query, reply; char buf[NTP_MSGSIZE]; fsa_len = sizeof(fsa); if ((size = recvfrom(fd, &buf, sizeof(buf), 0, (struct sockaddr *)&fsa, &fsa_len)) == -1) { if (errno == EHOSTUNREACH || errno == EHOSTDOWN || errno == ENETUNREACH || errno == ENETDOWN) { log_warn("recvfrom %s", log_sockaddr((struct sockaddr *)&fsa)); return (0); } else fatal("recvfrom"); } rectime = gettime_corrected(); if (ntp_getmsg((struct sockaddr *)&fsa, buf, size, &query) == -1) return (0); version = (query.status & VERSIONMASK) >> 3; bzero(&reply, sizeof(reply)); if (lconf->status.synced) reply.status = lconf->status.leap; else reply.status = LI_ALARM; reply.status |= (query.status & VERSIONMASK); if ((query.status & MODEMASK) == MODE_CLIENT) reply.status |= MODE_SERVER; else if ((query.status & MODEMASK) == MODE_SYM_ACT) reply.status |= MODE_SYM_PAS; else /* ignore packets of different type (e.g. bcast) */ return (0); reply.stratum = lconf->status.stratum; reply.ppoll = query.ppoll; reply.precision = lconf->status.precision; reply.rectime = d_to_lfp(rectime); reply.reftime = d_to_lfp(lconf->status.reftime); reply.xmttime = d_to_lfp(gettime_corrected()); reply.orgtime = query.xmttime; reply.rootdelay = d_to_sfp(lconf->status.rootdelay); reply.refid = lconf->status.refid; ntp_sendmsg(fd, (struct sockaddr *)&fsa, &reply); return (0); }