/* $NetBSD: ntp_io.c,v 1.7 1998/04/01 15:01:22 christos Exp $ */ /* * xntp_io.c - input/output routines for xntpd. The socket-opening code * was shamelessly stolen from ntpd. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #ifdef HAVE_SYS_PARAM_H # include #endif /* HAVE_SYS_PARAM_H */ #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_SOCKIO_H /* UXPV: SIOC* #defines (Frank Vance ) */ # include #endif #if _BSDI_VERSION >= 199510 # include #endif #include "ntpd.h" #include "ntp_select.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_if.h" #include "ntp_stdlib.h" #if defined(VMS) /* most likely UCX-specific */ #include /* "un*x"-compatible names for some items in UCX$INETDEF.H */ #define ifreq IFREQDEF #define ifr_name IFR$T_NAME #define ifr_addr IFR$R_DUMMY.IFR$T_ADDR #define ifr_broadaddr IFR$R_DUMMY.IFR$T_BROADADDR #define ifr_flags IFR$R_DUMMY.IFR$R_DUMMY_1_OVRL.IFR$W_FLAGS #define IFF_UP IFR$M_IFF_UP #define IFF_BROADCAST IFR$M_IFF_BROADCAST #define IFF_LOOPBACK IFR$M_IFF_LOOPBACK /* structure used in SIOCGIFCONF request (after [KSR] OSF/1) */ struct ifconf { int ifc_len; /* size of buffer */ union { caddr_t ifcu_buf; struct ifreq *ifcu_req; } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ #endif /* VMS */ #if defined(USE_TTY_SIGPOLL) || defined(USE_UDP_SIGPOLL) # if defined(SYS_AIX) && defined(_IO) /* XXX Identify AIX some other way */ # undef _IO # endif # include #endif /* * We do asynchronous input using the SIGIO facility. A number of * recvbuf buffers are preallocated for input. In the signal * handler we poll to see which sockets are ready and read the * packets from them into the recvbuf's along with a time stamp and * an indication of the source host and the interface it was received * through. This allows us to get as accurate receive time stamps * as possible independent of other processing going on. * * We watch the number of recvbufs available to the signal handler * and allocate more when this number drops below the low water * mark. If the signal handler should run out of buffers in the * interim it will drop incoming frames, the idea being that it is * better to drop a packet than to be inaccurate. */ /* * Block the interrupt, for critical sections. */ #if defined(HAVE_SIGNALED_IO) static int sigio_block_count = 0; # define BLOCKIO() ((void) block_sigio()) # define UNBLOCKIO() ((void) unblock_sigio()) #else # define BLOCKIO() # define UNBLOCKIO() #endif /* * recvbuf memory management */ #define RECV_INIT 10 /* 10 buffers initially */ #define RECV_LOWAT 3 /* when we're down to three buffers get more */ #define RECV_INC 5 /* get 5 more at a time */ #define RECV_TOOMANY 30 /* this is way too many buffers */ /* * Memory allocation */ volatile u_long full_recvbufs; /* number of recvbufs on fulllist */ volatile u_long free_recvbufs; /* number of recvbufs on freelist */ static struct recvbuf *volatile freelist; /* free buffers */ static struct recvbuf *volatile fulllist; /* lifo buffers with data */ static struct recvbuf *volatile beginlist; /* fifo buffers with data */ u_long total_recvbufs; /* total recvbufs currently in use */ u_long lowater_additions; /* number of times we have added memory */ static struct recvbuf initial_bufs[RECV_INIT]; /* initial allocation */ /* * Other statistics of possible interest */ volatile u_long packets_dropped; /* total number of packets dropped on reception */ volatile u_long packets_ignored; /* packets received on wild card interface */ volatile u_long packets_received; /* total number of packets received */ u_long packets_sent; /* total number of packets sent */ u_long packets_notsent; /* total number of packets which couldn't be sent */ volatile u_long handler_calls; /* number of calls to interrupt handler */ volatile u_long handler_pkts; /* number of pkts received by handler */ u_long io_timereset; /* time counters were reset */ /* * Interface stuff */ #define MAXINTERFACES 192 /* much better for big gateways with IP/X.25 and more ... */ struct interface *any_interface; /* pointer to default interface */ struct interface *loopback_interface; /* point to loopback interface */ static struct interface inter_list[MAXINTERFACES]; static int ninterfaces; #ifdef REFCLOCK /* * Refclock stuff. We keep a chain of structures with data concerning * the guys we are doing I/O for. */ static struct refclockio *refio; #endif /* REFCLOCK */ /* * File descriptor masks etc. for call to select */ fd_set activefds; int maxactivefd; /* * Imported from ntp_timer.c */ extern u_long current_time; #ifndef SYS_WINNT extern int errno; #endif /* SYS_WINNT */ extern int debug; static int create_sockets P((u_int)); static int open_socket P((struct sockaddr_in *, int, int)); static void close_socket P((int)); static void close_file P((int)); static char * fdbits P((int, fd_set *)); #ifdef HAVE_SIGNALED_IO static int init_clock_sig P((struct refclockio *)); static void init_socket_sig P((int)); static RETSIGTYPE sigio_handler P((int)); static void block_sigio P((void)); static void unblock_sigio P((void)); static void set_signal P((void)); #endif /* HAVE_SIGNALED_IO */ #ifndef STREAMS_TLI # ifndef SYS_WINNT extern char *inet_ntoa P((struct in_addr)); # endif /* SYS_WINNT */ #endif /* STREAMS_TLI */ /* * init_io - initialize I/O data structures and call socket creation routine */ void init_io() { register int i; #ifdef SYS_WINNT WORD wVersionRequested; WSADATA wsaData; #endif /* SYS_WINNT */ /* * Init buffer free list and stat counters */ freelist = 0; for (i = 0; i < RECV_INIT; i++) { initial_bufs[i].next = (struct recvbuf *) freelist; freelist = &initial_bufs[i]; } fulllist = 0; free_recvbufs = total_recvbufs = RECV_INIT; full_recvbufs = lowater_additions = 0; packets_dropped = packets_received = 0; packets_ignored = 0; packets_sent = packets_notsent = 0; handler_calls = handler_pkts = 0; io_timereset = 0; loopback_interface = 0; #ifdef REFCLOCK refio = 0; #endif #if defined(HAVE_SIGNALED_IO) (void) set_signal(); #endif #ifdef SYS_WINNT wVersionRequested = MAKEWORD(1,1); if (WSAStartup(wVersionRequested, &wsaData)) { msyslog(LOG_ERR, "No useable winsock.dll: %m"); exit(1); } #endif /* SYS_WINNT */ /* * Create the sockets */ BLOCKIO(); (void) create_sockets(htons(NTP_PORT)); UNBLOCKIO(); #ifdef DEBUG if (debug) printf("init_io: maxactivefd %d\n", maxactivefd); #endif } /* * create_sockets - create a socket for each interface plus a default * socket for when we don't know where to send */ static int create_sockets(port) u_int port; { #if _BSDI_VERSION >= 199510 int i, j; struct ifaddrs *ifaddrs, *ifap; struct sockaddr_in resmask; #if _BSDI_VERSION < 199701 struct ifaddrs *lp; int num_if; #endif #else /* _BSDI_VERSION >= 199510 */ # ifdef STREAMS_TLI struct strioctl ioc; # endif /* STREAMS_TLI */ char buf[MAXINTERFACES*sizeof(struct ifreq)]; struct ifconf ifc; struct ifreq ifreq, *ifr; int n, i, j, vs, size; struct sockaddr_in resmask; #endif /* _BSDI_VERSION >= 199510 */ #ifdef DEBUG if (debug) printf("create_sockets(%d)\n", ntohs( (u_short) port)); #endif /* * create pseudo-interface with wildcard address */ inter_list[0].sin.sin_family = AF_INET; inter_list[0].sin.sin_port = port; inter_list[0].sin.sin_addr.s_addr = htonl(INADDR_ANY); (void) strncpy(inter_list[0].name, "wildcard", sizeof(inter_list[0].name)); inter_list[0].mask.sin_addr.s_addr = htonl(~(u_int32)0); inter_list[0].received = 0; inter_list[0].sent = 0; inter_list[0].notsent = 0; inter_list[0].flags = INT_BROADCAST; #if _BSDI_VERSION >= 199510 #if _BSDI_VERSION >= 199701 if (getifaddrs(&ifaddrs) < 0) { msyslog(LOG_ERR, "getifaddrs: %m"); exit(1); } i = 1; for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) #else if (getifaddrs(&ifaddrs, &num_if) < 0) { msyslog(LOG_ERR, "create_sockets: getifaddrs() failed: %m"); exit(1); } i = 1; for (ifap = ifaddrs, lp = ifap + num_if; ifap < lp; ifap++) #endif { struct sockaddr_in *sin; if (!ifap->ifa_addr) continue; if (ifap->ifa_addr->sa_family != AF_INET) continue; if ((ifap->ifa_flags & IFF_UP) == 0) continue; inter_list[i].flags = 0; if (ifap->ifa_flags & IFF_BROADCAST) inter_list[i].flags |= INT_BROADCAST; (void)strcpy(inter_list[i].name, ifap->ifa_name); sin = (struct sockaddr_in *)ifap->ifa_addr; inter_list[i].sin = *sin; inter_list[i].sin.sin_port = port; if (ifap->ifa_flags & IFF_LOOPBACK) { inter_list[i].flags = INT_LOOPBACK; if (loopback_interface == NULL || ntohl(sin->sin_addr.s_addr) != 0x7f000001) loopback_interface = &inter_list[i]; } if (inter_list[i].flags & INT_BROADCAST) { sin = (struct sockaddr_in *)ifap->ifa_broadaddr; inter_list[i].bcast = *sin; inter_list[i].bcast.sin_port = port; } if (ifap->ifa_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) { inter_list[i].mask.sin_addr.s_addr = 0xffffffff; } else { sin = (struct sockaddr_in *)ifap->ifa_netmask; inter_list[i].mask = *sin; } inter_list[i].mask.sin_family = AF_INET; inter_list[i].mask.sin_len = sizeof *sin; /* * look for an already existing source interface address. If * the machine has multiple point to point interfaces, then * the local address may appear more than once. */ for (j=0; j < i; j++) if (inter_list[j].sin.sin_addr.s_addr == inter_list[i].sin.sin_addr.s_addr) { if (inter_list[j].flags & INT_LOOPBACK) inter_list[j] = inter_list[i]; break; } if (j == i) i++; } free(ifaddrs); #else /* _BSDI_VERSION >= 199510 */ # ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG if ((vs = open("/dev/ip", O_RDONLY)) < 0) { msyslog(LOG_ERR, "create_sockets: open(/dev/ip) failed: %m"); exit(1); } # else /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */ if ( (vs = socket(AF_INET, SOCK_DGRAM, 0)) # ifndef SYS_WINNT < 0 # else /* SYS_WINNT */ == INVALID_SOCKET # endif /* SYS_WINNT */ ) { msyslog(LOG_ERR, "create_sockets: socket(AF_INET, SOCK_DGRAM) failed: %m"); exit(1); } # endif /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */ i = 1; ifc.ifc_len = sizeof(buf); # ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFCONF; ioc.ic_timout = 0; ioc.ic_dp = (caddr_t)buf; ioc.ic_len = sizeof(buf); if(ioctl(vs, I_STR, &ioc) < 0 || ioc.ic_len < sizeof(struct ifreq)) { msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFCONF) failed: %m - exiting"); exit(1); } # ifdef SIZE_RETURNED_IN_BUFFER ifc.ifc_len = ioc.ic_len - sizeof(int); ifc.ifc_buf = buf + sizeof(int); # else /* not SIZE_RETURNED_IN_BUFFER */ ifc.ifc_len = ioc.ic_len; ifc.ifc_buf = buf; # endif /* not SIZE_RETURNED_IN_BUFFER */ # else /* not STREAMS_TLI */ ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; # ifndef SYS_WINNT if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) # else if (get_winnt_interfaces(&ifc) < 0) # endif /* SYS_WINNT */ { msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFCONF) failed: %m - exiting"); exit(1); } # endif /* not STREAMS_TLI */ for(n = ifc.ifc_len, ifr = ifc.ifc_req; n > 0; ifr = (struct ifreq *)((char *)ifr + size)) { size = sizeof(*ifr); # ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr)) size += ifr->ifr_addr.sa_len - sizeof(struct sockaddr); # endif n -= size; if # ifdef VMS /* VMS+UCX */ (((struct sockaddr *)&(ifr->ifr_addr))->sa_family != AF_INET) # else (ifr->ifr_addr.sa_family != AF_INET) # endif /* VMS+UCX */ continue; ifreq = *ifr; # ifndef SYS_WINNT /* no interface flags on NT */ # ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFFLAGS; ioc.ic_timout = 0; ioc.ic_dp = (caddr_t)&ifreq; ioc.ic_len = sizeof(struct ifreq); if(ioctl(vs, I_STR, &ioc)) { msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFFLAGS) failed: %m"); continue; } # else /* not STREAMS_TLI */ if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) { msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFFLAGS) failed: %m"); continue; } # endif /* not STREAMS_TLI */ if ((ifreq.ifr_flags & IFF_UP) == 0) continue; inter_list[i].flags = 0; if (ifreq.ifr_flags & IFF_BROADCAST) inter_list[i].flags |= INT_BROADCAST; # endif /* not SYS_WINNT */ # if !defined(SUN_3_3_STINKS) if ( # if defined(IFF_LOCAL_LOOPBACK) /* defined(SYS_HPUX) && (SYS_HPUX < 8) */ (ifreq.ifr_flags & IFF_LOCAL_LOOPBACK) # elif defined(IFF_LOOPBACK) (ifreq.ifr_flags & IFF_LOOPBACK) # else /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */ /* test against 127.0.0.1 (yuck!!) */ (inter_list[i].sin.sin_addr.s_addr == inet_addr("127.0.0.1")) # endif /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */ ) { # ifndef SYS_WINNT inter_list[i].flags |= INT_LOOPBACK; # endif /* not SYS_WINNT */ if (loopback_interface == 0) { loopback_interface = &inter_list[i]; } } # endif /* not SUN_3_3_STINKS */ #if 0 # ifndef SYS_WINNT # ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFADDR; ioc.ic_timout = 0; ioc.ic_dp = (caddr_t)&ifreq; ioc.ic_len = sizeof(struct ifreq); if (ioctl(vs, I_STR, &ioc)) { msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFADDR) failed: %m"); continue; } # else /* not STREAMS_TLI */ if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) { msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFADDR) failed: %m"); continue; } # endif /* not STREAMS_TLI */ # endif /* not SYS_WINNT */ #endif 0 (void)strncpy(inter_list[i].name, ifreq.ifr_name, sizeof(inter_list[i].name)); inter_list[i].sin = *(struct sockaddr_in *)&ifr->ifr_addr; inter_list[i].sin.sin_family = AF_INET; inter_list[i].sin.sin_port = port; # if defined(SUN_3_3_STINKS) /* * Oh, barf! I'm too disgusted to even explain this */ if (SRCADR(&inter_list[i].sin) == 0x7f000001) { inter_list[i].flags |= INT_LOOPBACK; if (loopback_interface == 0) loopback_interface = &inter_list[i]; } # endif /* SUN_3_3_STINKS */ # ifndef SYS_WINNT /* no interface flags on NT */ if (inter_list[i].flags & INT_BROADCAST) { # ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFBRDADDR; ioc.ic_timout = 0; ioc.ic_dp = (caddr_t)&ifreq; ioc.ic_len = sizeof(struct ifreq); if(ioctl(vs, I_STR, &ioc)) { msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFBRDADDR) failed: %m"); exit(1); } # else /* not STREAMS_TLI */ if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFBRDADDR) failed: %m"); exit(1); } # endif /* not STREAMS_TLI */ # ifndef ifr_broadaddr inter_list[i].bcast = *(struct sockaddr_in *)&ifreq.ifr_addr; # else inter_list[i].bcast = *(struct sockaddr_in *)&ifreq.ifr_broadaddr; # endif /* ifr_broadaddr */ inter_list[i].bcast.sin_family = AF_INET; inter_list[i].bcast.sin_port = port; } # ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFNETMASK; ioc.ic_timout = 0; ioc.ic_dp = (caddr_t)&ifreq; ioc.ic_len = sizeof(struct ifreq); if(ioctl(vs, I_STR, &ioc)) { msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFNETMASK) failed: %m"); exit(1); } # else /* not STREAMS_TLI */ if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) { msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFNETMASK) failed: %m"); exit(1); } # endif /* not STREAMS_TLI */ # endif /* not SYS_WINNT */ inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr; /* * look for an already existing source interface address. If * the machine has multiple point to point interfaces, then * the local address may appear more than once. */ for (j=0; j < i; j++) if (inter_list[j].sin.sin_addr.s_addr == inter_list[i].sin.sin_addr.s_addr) { break; } if (j == i) i++; } closesocket(vs); #endif /* _BSDI_VERSION >= 199510 */ ninterfaces = i; maxactivefd = 0; FD_ZERO(&activefds); for (i = 0; i < ninterfaces; i++) { inter_list[i].fd = open_socket(&inter_list[i].sin, inter_list[i].flags & INT_BROADCAST, 0); } /* * Now that we have opened all the sockets, turn off the reuse flag for * security. */ for (i = 0; i < ninterfaces; i++) { int off = 0; if (setsockopt(inter_list[i].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&off, sizeof(off))) { msyslog(LOG_ERR, "create_sockets: setsockopt(SO_REUSEADDR,off) failed: %m"); } } #if defined(MCAST) /* * enable possible multicast reception on the broadcast socket */ inter_list[0].bcast.sin_addr.s_addr = htonl(INADDR_ANY); inter_list[0].bcast.sin_family = AF_INET; inter_list[0].bcast.sin_port = port; #endif /* MCAST */ /* * Blacklist all bound interface addresses */ resmask.sin_addr.s_addr = ~ (u_int32)0; for (i = 1; i < ninterfaces; i++) restrict_addr(RESTRICT_FLAGS, &inter_list[i].sin, &resmask, RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); any_interface = &inter_list[0]; #ifdef DEBUG if (debug > 2) { printf("create_sockets: ninterfaces=%d\n", ninterfaces); for (i = 0; i < ninterfaces; i++) { printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n", i, inter_list[i].fd, inter_list[i].bfd, inter_list[i].name, inter_list[i].flags); /* Leave these as three printf calls. */ printf(" sin=%s", inet_ntoa((inter_list[i].sin.sin_addr))); if(inter_list[i].flags & INT_BROADCAST) printf(" bcast=%s,", inet_ntoa((inter_list[i].bcast.sin_addr))); printf(" mask=%s\n", inet_ntoa((inter_list[i].mask.sin_addr))); } } #endif return ninterfaces; } /* * io_setbclient - open the broadcast client sockets */ void io_setbclient() { int i; for (i = 1; i < ninterfaces; i++) { if (!(inter_list[i].flags & INT_BROADCAST)) continue; if (inter_list[i].flags & INT_BCASTOPEN) continue; #ifdef SYS_SOLARIS inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY); #endif #ifdef OPEN_BCAST_SOCKET /* Was: !SYS_DOMAINOS && !SYS_LINUX */ inter_list[i].bfd = open_socket(&inter_list[i].bcast, 0, 1); inter_list[i].flags |= INT_BCASTOPEN; #endif } } /* * io_multicast_add() - add multicast group address */ void io_multicast_add(addr) u_int32 addr; { #ifdef MCAST struct ip_mreq mreq; int i = ninterfaces; /* Use the next interface */ u_int32 haddr = ntohl(addr); struct in_addr iaddr; int s; struct sockaddr_in *sinp; iaddr.s_addr = addr; if (!IN_CLASSD(haddr)) { msyslog(LOG_ERR, "cannot add multicast address %s as it is not class D", inet_ntoa(iaddr)); return; } for (i = 0; i < ninterfaces; i++) { /* Already have this address */ if (inter_list[i].sin.sin_addr.s_addr == addr) return; /* found a free slot */ if (inter_list[i].sin.sin_addr.s_addr == 0 && inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 && inter_list[i].flags == 0) break; } sinp = &(inter_list[i].sin); memset((char *)&mreq, 0, sizeof(mreq)); memset((char *)&inter_list[i], 0, sizeof inter_list[0]); sinp->sin_family = AF_INET; sinp->sin_addr = iaddr; sinp->sin_port = htons(123); s = open_socket(sinp, 0, 1); /* Try opening a socket for the specified class D address */ /* This works under SunOS 4.x, but not OSF1 .. :-( */ if (s < 0) { memset((char *)&inter_list[i], 0, sizeof inter_list[0]); i = 0; /* HACK ! -- stuff in an address */ inter_list[i].bcast.sin_addr.s_addr = addr; msyslog(LOG_ERR, "...multicast address %s using wildcard socket", inet_ntoa(iaddr)); } else { inter_list[i].fd = s; inter_list[i].bfd = -1; (void) strncpy(inter_list[i].name, "multicast", sizeof(inter_list[i].name)); inter_list[i].mask.sin_addr.s_addr = htonl(~(u_int32)0); } /* * enable reception of multicast packets */ mreq.imr_multiaddr = iaddr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) msyslog(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)", mreq.imr_multiaddr, mreq.imr_interface.s_addr, inet_ntoa(iaddr)); inter_list[i].flags |= INT_MULTICAST; if (i >= ninterfaces) ninterfaces = i+1; #else /* MCAST */ struct in_addr iaddr; iaddr.s_addr = addr; msyslog(LOG_ERR, "cannot add multicast address %s as no MCAST support", inet_ntoa(iaddr)); #endif /* MCAST */ } /* * io_unsetbclient - close the broadcast client sockets */ void io_unsetbclient() { int i; for (i = 1; i < ninterfaces; i++) { if (!(inter_list[i].flags & INT_BCASTOPEN)) continue; close_socket(inter_list[i].bfd); inter_list[i].bfd = -1; inter_list[i].flags &= ~INT_BCASTOPEN; } } /* * io_multicast_del() - delete multicast group address */ void io_multicast_del(addr) u_int32 addr; { #ifdef MCAST int i; struct ip_mreq mreq; struct sockaddr_in sinaddr; if (!IN_CLASSD(addr)) { sinaddr.sin_addr.s_addr = addr; msyslog(LOG_ERR, "invalid multicast address %s", ntoa(&sinaddr)); return; } /* * Disable reception of multicast packets */ mreq.imr_multiaddr.s_addr = addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); for (i = 0; i < ninterfaces; i++) { if (!(inter_list[i].flags & INT_MULTICAST)) continue; if (!(inter_list[i].fd < 0)) continue; if (addr != inter_list[i].sin.sin_addr.s_addr) continue; if (i != 0) { /* we have an explicit fd, so we can close it */ close_socket(inter_list[i].fd); memset((char *)&inter_list[i], 0, sizeof inter_list[0]); inter_list[i].fd = -1; inter_list[i].bfd = -1; } else { /* We are sharing "any address" port :-( Don't close it! */ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) msyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m"); /* This is **WRONG** -- there may be others ! */ /* There should be a count of users ... */ inter_list[i].flags &= ~INT_MULTICAST; } } #else /* not MCAST */ msyslog(LOG_ERR, "this function requires multicast kernel"); #endif /* not MCAST */ } /* * open_socket - open a socket, returning the file descriptor */ static int open_socket(addr, flags, turn_off_reuse) struct sockaddr_in *addr; int flags; int turn_off_reuse; { int fd; int on = 1, off = 0; /* create a datagram (UDP) socket */ if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) #ifndef SYS_WINNT < 0 #else == INVALID_SOCKET #endif /* SYS_WINNT */ ) { msyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed: %m"); exit(1); /*NOTREACHED*/ } /* set SO_REUSEADDR since we will be binding the same port number on each interface */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { msyslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m"); } /* * bind the local address. */ if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { char buff[160]; sprintf(buff, "bind() fd %d, family %d, port %d, addr %08lx, in_classd=%d flags=%d fails: %%m", fd, addr->sin_family, (int)ntohs(addr->sin_port), (u_long)ntohl(addr->sin_addr.s_addr), IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags); msyslog(LOG_ERR, buff); closesocket(fd); /* * soft fail if opening a class D address */ if (IN_CLASSD(ntohl(addr->sin_addr.s_addr))) return -1; exit(1); } #ifdef DEBUG if (debug) printf("bind() fd %d, family %d, port %d, addr %08lx, flags=%d\n", fd, addr->sin_family, (int)ntohs(addr->sin_port), (u_long)ntohl(addr->sin_addr.s_addr), flags); #endif if (fd > maxactivefd) maxactivefd = fd; FD_SET(fd, &activefds); /* * set non-blocking, */ #ifdef USE_FIONBIO /* in vxWorks we use FIONBIO, but the others are defined for old systems, so * all hell breaks loose if we leave them defined */ #undef O_NONBLOCK #undef FNDELAY #undef O_NDELAY #endif #if defined(O_NONBLOCK) /* POSIX */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { msyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m"); exit(1); /*NOTREACHED*/ } #elif defined(FNDELAY) if (fcntl(fd, F_SETFL, FNDELAY) < 0) { msyslog(LOG_ERR, "fcntl(FNDELAY) fails: %m"); exit(1); /*NOTREACHED*/ } #elif defined(O_NDELAY) /* generally the same as FNDELAY */ if (fcntl(fd, F_SETFL, O_NDELAY) < 0) { msyslog(LOG_ERR, "fcntl(O_NDELAY) fails: %m"); exit(1); /*NOTREACHED*/ } #elif defined(FIONBIO) # if defined(VMS) if (ioctl(fd,FIONBIO,&1) < 0) # elif defined(SYS_WINNT) if (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) # else if (ioctl(fd,FIONBIO,&on) < 0) # endif { msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m"); exit(1); /*NOTREACHED*/ } #elif defined(FIOSNBIO) if (ioctl(fd,FIOSNBIO,&on) < 0) { msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m"); exit(1); /*NOTREACHED*/ } #else # include "Bletch: Need non blocking I/O!" #endif #ifdef HAVE_SIGNALED_IO init_socket_sig(fd); #endif /* not HAVE_SIGNALED_IO */ /* * Turn off the SO_REUSEADDR socket option. It apparently * causes heartburn on systems with multicast IP installed. * On normal systems it only gets looked at when the address * is being bound anyway.. */ if (turn_off_reuse) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&off, sizeof(off))) { msyslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m"); } #ifdef SO_BROADCAST /* if this interface can support broadcast, set SO_BROADCAST */ if (flags & INT_BROADCAST) { if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on))) { msyslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m"); } } #endif /* SO_BROADCAST */ #if !defined(SYS_WINNT) && !defined(VMS) # ifdef DEBUG if (debug > 1) printf("flags for fd %d: 0%o\n", fd, fcntl(fd, F_GETFL, 0)); # endif #endif /* SYS_WINNT || VMS */ return fd; } /* * close_socket - close a socket and remove from the activefd list */ static void close_socket(fd) int fd; { int i, newmax; (void) closesocket(fd); FD_CLR( (u_int) fd, &activefds); if (fd >= maxactivefd) { newmax = 0; for (i = 0; i < maxactivefd; i++) if (FD_ISSET(i, &activefds)) newmax = i; maxactivefd = newmax; } } /* * close_file - close a file and remove from the activefd list * added 1/31/1997 Greg Schueman for Windows NT portability */ static void close_file(fd) int fd; { int i, newmax; (void) close(fd); FD_CLR( (u_int) fd, &activefds); if (fd >= maxactivefd) { newmax = 0; for (i = 0; i < maxactivefd; i++) if (FD_ISSET(i, &activefds)) newmax = i; maxactivefd = newmax; } } /* * findbcastinter - find broadcast interface corresponding to address */ struct interface * findbcastinter(addr) struct sockaddr_in *addr; { #ifdef SIOCGIFCONF register int i; register u_int32 netnum; netnum = NSRCADR(addr); for (i = 1; i < ninterfaces; i++) { if (!(inter_list[i].flags & INT_BROADCAST)) continue; if (NSRCADR(&inter_list[i].bcast) == netnum) return &inter_list[i]; if ((NSRCADR(&inter_list[i].sin) & NSRCADR(&inter_list[i].mask)) == (netnum & NSRCADR(&inter_list[i].mask))) return &inter_list[i]; } #endif /* SIOCGIFCONF */ return any_interface; } /* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */ /* * getrecvbufs - get receive buffers which have data in them * * ***N.B. must be called with SIGIO blocked*** */ struct recvbuf * getrecvbufs() { struct recvbuf *rb; #ifdef DEBUG if (debug > 4) printf("getrecvbufs: %ld handler interrupts, %ld frames\n", handler_calls, handler_pkts); #endif if (full_recvbufs == 0) { #ifdef DEBUG if (debug > 4) printf("getrecvbufs called, no action here\n"); #endif return (struct recvbuf *)0; /* nothing has arrived */ } /* * Get the fulllist chain and mark it empty */ #ifdef DEBUG if (debug > 4) printf("getrecvbufs returning %ld buffers\n", full_recvbufs); #endif rb = (struct recvbuf *) beginlist; fulllist = 0; full_recvbufs = 0; /* * Check to see if we're below the low water mark. */ if (free_recvbufs <= RECV_LOWAT) { register struct recvbuf *buf; register int i; if (total_recvbufs >= RECV_TOOMANY) msyslog(LOG_ERR, "too many recvbufs allocated (%d)", total_recvbufs); else { buf = (struct recvbuf *) emalloc(RECV_INC*sizeof(struct recvbuf)); for (i = 0; i < RECV_INC; i++) { buf->next = (struct recvbuf *) freelist; freelist = buf; buf++; } free_recvbufs += RECV_INC; total_recvbufs += RECV_INC; lowater_additions++; } } /* * Return the chain */ return rb; } /* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */ /* * freerecvbuf - make a single recvbuf available for reuse */ void freerecvbuf(rb) struct recvbuf *rb; { BLOCKIO(); rb->next = (struct recvbuf *) freelist; freelist = rb; free_recvbufs++; UNBLOCKIO(); } /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ /* * sendpkt - send a packet to the specified destination. Maintain a * send error cache so that only the first consecutive error for a * destination is logged. */ void sendpkt(dest, inter, ttl, pkt, len) struct sockaddr_in *dest; struct interface *inter; int ttl; struct pkt *pkt; int len; { int cc, slot; #ifdef SYS_WINNT DWORD err; #endif /* SYS_WINNT */ /* * Send error cache. Empty slots have port == 0 * Set ERRORCACHESIZE to 0 to disable */ struct cache { u_short port; struct in_addr addr; }; #ifndef ERRORCACHESIZE #define ERRORCACHESIZE 8 #endif #if ERRORCACHESIZE > 0 static struct cache badaddrs[ERRORCACHESIZE]; #else #define badaddrs ((struct cache *)0) /* Only used in empty loops! */ #endif #ifdef DEBUG if (debug) printf("%ssendpkt(fd=%d %s, %s, ttl=%d, %d)\n", (ttl >= 0) ? "\tMCAST\t*****" : "", inter->fd, ntoa(dest), ntoa(&inter->sin), ttl, len); #endif #ifdef MCAST /* for the moment we use the bcast option to set multicast ttl */ if (ttl >= 0 && ttl != inter->last_ttl) { char mttl = ttl; /* set the multicast ttl for outgoing packets */ if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, sizeof(mttl)) == -1) { msyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m"); } else inter->last_ttl = ttl; } #endif /* MCAST */ for (slot = ERRORCACHESIZE; --slot >= 0; ) if (badaddrs[slot].port == dest->sin_port && badaddrs[slot].addr.s_addr == dest->sin_addr.s_addr) break; cc = sendto(inter->fd, (char *)pkt, len, 0, (struct sockaddr *)dest, sizeof(struct sockaddr_in)); if (cc == -1) { inter->notsent++; packets_notsent++; #ifndef SYS_WINNT if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0) #else err = WSAGetLastError(); if (err != WSAEWOULDBLOCK && err != WSAENOBUFS && slot < 0) #endif /* SYS_WINNT */ { /* * Remember this, if there's an empty slot */ for (slot = ERRORCACHESIZE; --slot >= 0; ) if (badaddrs[slot].port == 0) { badaddrs[slot].port = dest->sin_port; badaddrs[slot].addr = dest->sin_addr; break; } msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest)); } } else { inter->sent++; packets_sent++; /* * He's not bad any more */ if (slot >= 0) { msyslog(LOG_INFO, "Connection re-established to %s", ntoa(dest)); badaddrs[slot].port = 0; } } } /* * fdbits - generate ascii representation of fd_set (FAU debug support) * HFDF format - highest fd first. */ static char * fdbits(count, set) int count; fd_set *set; { static char buffer[256]; char * buf = buffer; count = (count < 256) ? count : 255; while (count >= 0) { *buf++ = FD_ISSET(count, set) ? '#' : '-'; count--; } *buf = '\0'; return buffer; } /* * input_handler - receive packets asynchronously */ void input_handler(cts) l_fp *cts; { register int i, n; register struct recvbuf *rb; register int doing; register int fd; struct timeval tvzero; int fromlen; l_fp ts; /* Timestamp at BOselect() gob */ l_fp ts_e; /* Timestamp at EOselect() gob */ fd_set fds; int select_count = 0; #if 0 int first = 1; #endif static int handler_count = 0; ++handler_count; if (handler_count != 1) msyslog(LOG_ERR, "input_handler: handler_count is %d!", handler_count); handler_calls++; ts = *cts; for (;;) { /* * Do a poll to see who has data */ fds = activefds; tvzero.tv_sec = tvzero.tv_usec = 0; /* * If we have something to do, freeze a timestamp. * See below for the other cases (nothing (left) to do or error) */ while (0 < (n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero))) { #if 0 if (!first) get_systime(&ts); first = 0; #endif ++select_count; #if 1 ++handler_pkts; #else handler_pkts += n; #endif #ifdef REFCLOCK /* * Check out the reference clocks first, if any */ if (refio != 0) { register struct refclockio *rp; for (rp = refio; rp != 0 && n > 0; rp = rp->next) { fd = rp->fd; if (FD_ISSET(fd, &fds)) { n--; if (free_recvbufs == 0) { char buf[RX_BUFF_SIZE]; #ifndef SYS_WINNT (void) read(fd, buf, sizeof buf); #else (void) ReadFile((HANDLE)fd, buf, (DWORD)sizeof buf, NULL, NULL); #endif /* SYS_WINNT */ packets_dropped++; #if 1 goto select_again; #else continue; #endif } rb = (struct recvbuf *) freelist; freelist = rb->next; free_recvbufs--; i = (rp->datalen == 0 || rp->datalen > sizeof(rb->recv_space)) ? sizeof(rb->recv_space) : rp->datalen; #ifndef SYS_WINNT rb->recv_length = read(fd, (char *)&rb->recv_space, i) #else /* SYS_WINNT */ ReadFile((HANDLE)fd, (char *)&rb->recv_space, (DWORD)i, (LPDWORD)&(rb->recv_length), NULL) #endif /* SYS_WINNT */ ; if (rb->recv_length == -1) { msyslog(LOG_ERR, "clock read fd %d: %m", fd); rb->next = (struct recvbuf *) freelist; freelist = rb; free_recvbufs++; #if 1 goto select_again; #else continue; #endif } /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->recv_srcclock = rp->srcclock; rb->dstadr = 0; rb->fd = fd; rb->recv_time = ts; rb->receiver = rp->clock_recv; if (fulllist == 0) { beginlist = rb; rb->next = 0; } else { rb->next = fulllist->next; fulllist->next = rb; } fulllist = rb; full_recvbufs++; rp->recvcount++; packets_received++; } } } #endif /* REFCLOCK */ /* * Loop through the interfaces looking for data to read. */ for (i = ninterfaces - 1; (i >= 0) && (n > 0); i--) { for (doing = 0; (doing < 2) && (n > 0); doing++) { if (doing == 0) { fd = inter_list[i].fd; } else { if (!(inter_list[i].flags & INT_BCASTOPEN)) break; fd = inter_list[i].bfd; } if (fd < 0) continue; if (FD_ISSET(fd, &fds)) { n--; /* * Get a buffer and read the frame. If we * haven't got a buffer, or this is received * on the wild card socket, just dump the * packet. */ if ( #ifndef SYS_WINNT (!(free_recvbufs && (i == 0) && (inter_list[i].flags & INT_MULTICAST))) #else /* For Win/NT we are creating just one socket, * i is 0, and INT_MULTICAST flag is not set. */ (!free_recvbufs) #endif /* SYS_WINNT */ ) { if ( #ifdef UDP_WILDCARD_DELIVERY /* * these guys manage to put properly addressed * packets into the wildcard queue */ (free_recvbufs == 0) #else ((i == 0) || (free_recvbufs == 0)) #endif ) { char buf[RX_BUFF_SIZE]; struct sockaddr from; fromlen = sizeof from; (void) recvfrom(fd, buf, sizeof(buf), 0, &from, &fromlen); #ifdef DEBUG if (debug) printf("%s on %d(%lu) fd=%d from %s\n", (i) ? "drop" : "ignore", i, free_recvbufs, fd, inet_ntoa(((struct sockaddr_in *) &from)->sin_addr)); #endif if (i == 0) packets_ignored++; else packets_dropped++; #if 1 goto select_again; #else continue; #endif } } rb = (struct recvbuf *) freelist; fromlen = sizeof(struct sockaddr_in); rb->recv_length = recvfrom(fd, (char *)&rb->recv_space, sizeof(rb->recv_space), 0, (struct sockaddr *)&rb->recv_srcadr, &fromlen); if (rb->recv_length > 0) { freelist = rb->next; free_recvbufs--; } else if (rb->recv_length == 0 #ifdef EWOULDBLOCK || errno==EWOULDBLOCK #endif #ifdef EAGAIN || errno==EAGAIN #endif ) continue; else { msyslog(LOG_ERR, "recvfrom() fd=%d: %m", fd); #ifdef DEBUG if (debug) printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd); #endif continue; } #ifdef DEBUG if (debug) printf("input_handler: fd=%d length %d from %08lx %s\n", fd, rb->recv_length, (u_long)ntohl(rb->recv_srcadr.sin_addr.s_addr) & 0x00000000ffffffff, inet_ntoa(rb->recv_srcadr.sin_addr)); #endif /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->dstadr = &inter_list[i]; rb->fd = fd; rb->recv_time = ts; rb->receiver = receive; if (fulllist == 0) { beginlist = rb; rb->next = 0; } else { rb->next = fulllist->next; fulllist->next = rb; } fulllist = rb; full_recvbufs++; inter_list[i].received++; packets_received++; goto select_again; } /* Check more interfaces */ } } select_again:; /* * Done everything from that select. Poll again. */ } /* * If nothing more to do, try again. * If nothing to do, just return. * If an error occurred, complain and return. */ if (n == 0) { if (select_count == 0) /* We really had nothing to do */ { if (debug) msyslog(LOG_DEBUG, "input_handler: select() returned 0"); --handler_count; return; } /* We've done our work */ get_systime(&ts_e); /* * (ts_e - ts) is the amount of time we spent processing * this gob of file descriptors. Log it. */ L_SUB(&ts_e, &ts); if (debug > 3) msyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6)); #if 0 /* * We'll re-start the for(;;) loop now. * Use the ending timestamp as the received timestamp */ ts = ts_e; #else /* No, for now just bail. */ --handler_count; return; #endif } else if (n == -1) { #ifndef SYS_WINNT int err = errno; #else DWORD err = WSAGetLastError(); #endif /* SYS_WINNT */ /* * extended FAU debugging output */ msyslog(LOG_ERR, "select(%d, %s, 0L, 0L, &0.000000) error: %m", maxactivefd+1, fdbits(maxactivefd, &activefds)); if ( #ifndef SYS_WINNT (err == EBADF) #else (err == WSAEBADF) #endif /* SYS_WINNT */ ) { int i, b; fds = activefds; for (i = 0; i <= maxactivefd; i++) if ( #ifndef SYS_WINNT (FD_ISSET(i, &fds) && (read(i, &b, 0) == -1)) #else (FD_ISSET(i, &fds) && (!ReadFile((HANDLE)i, &b, 0, NULL, NULL))) #endif /* SYS_WINNT */ ) msyslog(LOG_ERR, "Bad file descriptor %d", i); } --handler_count; return; } } msyslog(LOG_ERR, "input_handler: fell out of infinite for(;;) loop!"); --handler_count; return; } /* * findinterface - utility used by other modules to find an interface * given an address. */ struct interface * findinterface(addr) struct sockaddr_in *addr; { register int i; register u_int32 saddr; /* * Just match the address portion. */ saddr = addr->sin_addr.s_addr; for (i = 0; i < ninterfaces; i++) { if (inter_list[i].sin.sin_addr.s_addr == saddr) return &inter_list[i]; } return (struct interface *)0; } /* * io_clr_stats - clear I/O module statistics */ void io_clr_stats() { packets_dropped = 0; packets_ignored = 0; packets_received = 0; packets_sent = 0; packets_notsent = 0; handler_calls = 0; handler_pkts = 0; io_timereset = current_time; } #ifdef REFCLOCK /* * This is a hack so that I don't have to fool with these ioctls in the * pps driver ... we are already non-blocking and turn on SIGIO thru * another mechanisim */ int io_addclock_simple(rio) struct refclockio *rio; { BLOCKIO(); /* * Stuff the I/O structure in the list and mark the descriptor * in use. There is a harmless (I hope) race condition here. */ rio->next = refio; refio = rio; if (rio->fd > maxactivefd) maxactivefd = rio->fd; FD_SET(rio->fd, &activefds); UNBLOCKIO(); return 1; } /* * io_addclock - add a reference clock to the list and arrange that we * get SIGIO interrupts from it. */ int io_addclock(rio) struct refclockio *rio; { BLOCKIO(); /* * Stuff the I/O structure in the list and mark the descriptor * in use. There is a harmless (I hope) race condition here. */ rio->next = refio; refio = rio; # ifdef HAVE_SIGNALED_IO if (init_clock_sig(rio)) { refio = rio->next; UNBLOCKIO(); return 0; } # endif if (rio->fd > maxactivefd) maxactivefd = rio->fd; FD_SET(rio->fd, &activefds); UNBLOCKIO(); return 1; } /* * io_closeclock - close the clock in the I/O structure given */ void io_closeclock(rio) struct refclockio *rio; { /* * Remove structure from the list */ if (refio == rio) { refio = rio->next; } else { register struct refclockio *rp; for (rp = refio; rp != 0; rp = rp->next) if (rp->next == rio) { rp->next = rio->next; break; } if (rp == 0) { /* * Internal error. Report it. */ msyslog(LOG_ERR, "internal error: refclockio structure not found"); return; } } /* * Close the descriptor. */ close_file(rio->fd); } #endif /* REFCLOCK */ /* * SIGPOLL and SIGIO ROUTINES. */ #ifdef HAVE_SIGNALED_IO /* * Some systems (MOST) define SIGPOLL == SIGIO, others SIGIO == SIGPOLL, and * a few have separate SIGIO and SIGPOLL signals. This code checks for the * SIGIO == SIGPOLL case at compile time. * Do not defined USE_SIGPOLL or USE_SIGIO. * these are interal only to ntp_io.c! */ # if defined(USE_SIGPOLL) # undef USE_SIGPOLL # endif # if defined(USE_SIGIO) # undef USE_SIGIO # endif # if defined(USE_TTY_SIGPOLL) || defined(USE_UDP_SIGPOLL) # define USE_SIGPOLL # endif # if !defined(USE_TTY_SIGPOLL) || !defined(USE_UDP_SIGPOLL) # define USE_SIGIO # endif # if defined(USE_SIGIO) && defined(USE_SIGPOLL) # if SIGIO == SIGPOLL # define USE_SIGIO # undef USE_SIGPOLL # endif /* SIGIO == SIGPOLL */ # endif /* USE_SIGIO && USE_SIGIO */ /* * TTY initialization routines. */ static int init_clock_sig(rio) struct refclockio *rio; # ifdef USE_TTY_SIGPOLL { /* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */ if (ioctl(rio->fd, I_SETSIG, S_INPUT) < 0) { msyslog(LOG_ERR, "init_clock_sig: ioctl(I_SETSIG, S_INPUT) failed: %m"); return 1; } return 0; } # else /* * Special cases first! */ /* Was: defined(SYS_HPUX) */ # if defined(FIOSSAIOOWN) && defined(FIOSNBIO) && defined(FIOSSAIOSTAT) #define CLOCK_DONE { int pgrp, on = 1; /* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */ pgrp = getpid(); if (ioctl(rio->fd, FIOSSAIOOWN, (char *)&pgrp) == -1) { msyslog(LOG_ERR, "ioctl(FIOSSAIOOWN) fails for clock I/O: %m"); exit(1); /*NOTREACHED*/ } /* * set non-blocking, async I/O on the descriptor */ if (ioctl(rio->fd, FIOSNBIO, (char *)&on) == -1) { msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails for clock I/O: %m"); exit(1); /*NOTREACHED*/ } if (ioctl(rio->fd, FIOSSAIOSTAT, (char *)&on) == -1) { msyslog(LOG_ERR, "ioctl(FIOSSAIOSTAT) fails for clock I/O: %m"); exit(1); /*NOTREACHED*/ } return 0; } # endif /* SYS_HPUX: FIOSSAIOOWN && FIOSNBIO && FIOSSAIOSTAT */ /* Was: defined(SYS_AIX) && !defined(_BSD) */ # if !defined(_BSD) && defined(_AIX) && defined(FIOASYNC) && defined(FIOSETOWN) /* * SYSV compatibility mode under AIX. */ #define CLOCK_DONE { int pgrp, on = 1; /* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */ if (ioctl(rio->fd, FIOASYNC, (char *)&on) == -1) { msyslog(LOG_ERR, "ioctl(FIOASYNC) fails for clock I/O: %m"); return 1; } pgrp = -getpid(); if (ioctl(rio->fd, FIOSETOWN, (char*)&pgrp) == -1) { msyslog(LOG_ERR, "ioctl(FIOSETOWN) fails for clock I/O: %m"); return 1; } if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) { msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m"); return 1; } return 0; } # endif /* AIX && !BSD: !_BSD && FIOASYNC && FIOSETOWN */ # ifndef CLOCK_DONE { /* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */ # if defined(TIOCSCTTY) && defined(USE_FSETOWNCTTY) /* * there are, however, always exceptions to the rules * one is, that OSF accepts SETOWN on TTY fd's only, iff they are * CTTYs. SunOS and HPUX do not semm to have this restriction. * another question is: how can you do multiple SIGIO from several * ttys (as they all should be CTTYs), wondering... * * kd 95-07-16 */ if (ioctl(rio->fd, TIOCSCTTY, 0) == -1) { msyslog(LOG_ERR, "ioctl(TIOCSCTTY, 0) fails for clock I/O: %m"); return 1; } # endif /* TIOCSCTTY && USE_FSETOWNCTTY */ if (fcntl(rio->fd, F_SETOWN, getpid()) == -1) { msyslog(LOG_ERR, "fcntl(F_SETOWN) fails for clock I/O: %m"); return 1; } if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) { msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m"); return 1; } return 0; } # endif /* CLOCK_DONE */ # endif /* !USE_TTY_SIGPOLL */ static void init_socket_sig(fd) int fd; # ifdef USE_UDP_SIGPOLL { if (ioctl(fd, I_SETSIG, S_INPUT) < 0) { msyslog(LOG_ERR, "init_socket_sig: ioctl(I_SETSIG, S_INPUT) failed: %m"); exit(1); } } # else /* USE_UDP_SIGPOLL */ { int pgrp; # ifdef FIOASYNC int on = 1; # endif # if defined(FIOASYNC) if (ioctl(fd, FIOASYNC, (char *)&on) == -1) { msyslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m"); exit(1); /*NOTREACHED*/ } # elif defined(FASYNC) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) { msyslog(LOG_ERR, "fcntl(F_GETFL) fails: %m"); exit(1); /*NOTREACHED*/ } if (fcntl(fd, F_SETFL, flags|FASYNC) < 0) { msyslog(LOG_ERR, "fcntl(...|FASYNC) fails: %m"); exit(1); /*NOTREACHED*/ } } # else # include "Bletch: Need asynchronous I/O!" # endif # ifdef UDP_BACKWARDS_SETOWN pgrp = -getpid(); # else pgrp = getpid(); # endif # if defined(SIOCSPGRP) if (ioctl(fd, SIOCSPGRP, (char *)&pgrp) == -1) { msyslog(LOG_ERR, "ioctl(SIOCSPGRP) fails: %m"); exit(1); /*NOTREACHED*/ } # elif defined(FIOSETOWN) if (ioctl(fd, FIOSETOWN, (char*)&pgrp) == -1) { msyslog(LOG_ERR, "ioctl(FIOSETOWN) fails: %m"); exit(1); /*NOTREACHED*/ } # elif defined(F_SETOWN) if (fcntl(fd, F_SETOWN, pgrp) == -1) { msyslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m"); exit(1); /*NOTREACHED*/ } # else # include "Bletch: Need to set process(group) to receive SIG(IO|POLL)" # endif } # endif /* USE_UDP_SIGPOLL */ static RETSIGTYPE sigio_handler(sig) int sig; { int saved_errno = errno; l_fp ts; get_systime(&ts); (void)input_handler(&ts); errno = saved_errno; } /* * Signal support routines. */ # ifdef HAVE_SIGACTION static void set_signal() { # ifdef USE_SIGIO (void) signal_no_reset(SIGIO, sigio_handler); # endif # ifdef USE_SIGPOLL (void) signal_no_reset(SIGPOLL, sigio_handler); # endif } void block_io_and_alarm() { sigset_t set; if (sigemptyset(&set)) msyslog(LOG_ERR, "block_io_and_alarm: sigemptyset() failed: %m"); # if defined(USE_SIGIO) if (sigaddset(&set, SIGIO)) msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGIO) failed: %m"); # endif # if defined(USE_SIGPOLL) if (sigaddset(&set, SIGPOLL)) msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGPOLL) failed: %m"); # endif if (sigaddset(&set, SIGALRM)) msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGALRM) failed: %m"); if (sigprocmask(SIG_BLOCK, &set, NULL)) msyslog(LOG_ERR, "block_io_and_alarm: sigprocmask() failed: %m"); } static void block_sigio() { sigset_t set; ++sigio_block_count; if (sigio_block_count > 1) msyslog(LOG_INFO, "block_sigio: sigio_block_count > 1"); if (sigio_block_count < 1) msyslog(LOG_INFO, "block_sigio: sigio_block_count < 1"); if (sigemptyset(&set)) msyslog(LOG_ERR, "block_sigio: sigemptyset() failed: %m"); # if defined(USE_SIGIO) if (sigaddset(&set, SIGIO)) msyslog(LOG_ERR, "block_sigio: sigaddset(SIGIO) failed: %m"); # endif # if defined(USE_SIGPOLL) if (sigaddset(&set, SIGPOLL)) msyslog(LOG_ERR, "block_sigio: sigaddset(SIGPOLL) failed: %m"); # endif if (sigprocmask(SIG_BLOCK, &set, NULL)) msyslog(LOG_ERR, "block_sigio: sigprocmask() failed: %m"); } void unblock_io_and_alarm() { sigset_t unset; if (sigemptyset(&unset)) msyslog(LOG_ERR, "unblock_io_and_alarm: sigemptyset() failed: %m"); # if defined(USE_SIGIO) if (sigaddset(&unset, SIGIO)) msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGIO) failed: %m"); # endif # if defined(USE_SIGPOLL) if (sigaddset(&unset, SIGPOLL)) msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGPOLL) failed: %m"); # endif if (sigaddset(&unset, SIGALRM)) msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGALRM) failed: %m"); if (sigprocmask(SIG_UNBLOCK, &unset, NULL)) msyslog(LOG_ERR, "unblock_io_and_alarm: sigprocmask() failed: %m"); } static void unblock_sigio() { sigset_t unset; --sigio_block_count; if (sigio_block_count > 0) msyslog(LOG_INFO, "unblock_sigio: sigio_block_count > 0"); if (sigio_block_count < 0) msyslog(LOG_INFO, "unblock_sigio: sigio_block_count < 0"); if (sigemptyset(&unset)) msyslog(LOG_ERR, "unblock_sigio: sigemptyset() failed: %m"); # if defined(USE_SIGIO) if (sigaddset(&unset, SIGIO)) msyslog(LOG_ERR, "unblock_sigio: sigaddset(SIGIO) failed: %m"); # endif # if defined(USE_SIGPOLL) if (sigaddset(&unset, SIGPOLL)) msyslog(LOG_ERR, "unblock_sigio: sigaddset(SIGPOLL) failed: %m"); # endif if (sigprocmask(SIG_UNBLOCK, &unset, NULL)) msyslog(LOG_ERR, "unblock_sigio: sigprocmask() failed: %m"); } void wait_for_signal() { sigset_t old; if (sigprocmask(SIG_UNBLOCK, NULL, &old)) msyslog(LOG_ERR, "wait_for_signal: sigprocmask() failed: %m"); # if defined(USE_SIGIO) if (sigdelset(&old, SIGIO)) msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGIO) failed: %m"); # endif # if defined(USE_SIGPOLL) if (sigdelset(&old, SIGPOLL)) msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGPOLL) failed: %m"); # endif if (sigdelset(&old, SIGALRM)) msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGALRM) failed: %m"); if (sigsuspend(&old) && (errno != EINTR)) msyslog(LOG_ERR, "wait_for_signal: sigsuspend() failed: %m"); } # else /* !HAVE_SIGACTION */ /* * Must be an old bsd system. * We assume there is no SIGPOLL. */ void block_io_and_alarm() { int mask; mask = sigmask(SIGIO) | sigmask(SIGALRM); if (sigblock(mask)) msyslog(LOG_ERR, "block_io_and_alarm: sigblock() failed: %m"); } static void block_sigio() { int mask; ++sigio_block_count; if (sigio_block_count > 1) msyslog(LOG_INFO, "block_sigio: sigio_block_count > 1"); if (sigio_block_count < 1) msyslog(LOG_INFO, "block_sigio: sigio_block_count < 1"); mask = sigmask(SIGIO); if (sigblock(mask)) msyslog(LOG_ERR, "block_sigio: sigblock() failed: %m"); } static void set_signal() { (void) signal_no_reset(SIGIO, sigio_handler); } void unblock_io_and_alarm() { int mask, omask; mask = sigmask(SIGIO) | sigmask(SIGALRM); omask = sigblock(0); omask &= ~mask; (void) sigsetmask(omask); } static void unblock_sigio() { int mask, omask; --sigio_block_count; if (sigio_block_count > 0) msyslog(LOG_INFO, "unblock_sigio: sigio_block_count > 0"); if (sigio_block_count < 0) msyslog(LOG_INFO, "unblock_sigio: sigio_block_count < 0"); mask = sigmask(SIGIO); omask = sigblock(0); omask &= ~mask; (void) sigsetmask(omask); } void wait_for_signal() { int mask, omask; mask = sigmask(SIGIO) | sigmask(SIGALRM); omask = sigblock(0); omask &= ~mask; if (sigpause(omask) && (errno != EINTR)) msyslog(LOG_ERR, "wait_for_signal: sigspause() failed: %m"); } # endif /* HAVE_SIGACTION */ #endif /* HAVE_SIGNALED_IO */ void kill_asyncio() { int i; BLOCKIO(); for (i = 0; i <= maxactivefd; i++) (void)close_socket(i); } #ifdef SYS_WINNT /* ------------------------------------------------------------------------------------------------------------------ */ /* modified with suggestions from Kevin Dunlap so we only pick out netcards bound to tcpip */ int get_winnt_interfaces(ifc) struct ifconf *ifc; { char *ifc_buffer = ifc->ifc_buf; struct ifreq *ifr; int maxsize = sizeof(ifc_buffer); HKEY hk, hksub; /* registry key handle */ BOOL bSuccess; char newkey[200]; char servicename[50]; DWORD sizeofservicename = 50; int Done = 0; /* * these need to be big as they are multi_sz in type and hold all * ip addresses and subnet mask for a given interface */ char IpAddresses[10000]; char *ipptr = IpAddresses; DWORD sizeofipaddresses = 10000; char SubNetMasks[10000]; char *subptr = SubNetMasks; DWORD sizeofsubnetmasks = 10000; char bindservicenames[1000]; DWORD sizeofbindnames = 1000; char oneIpAddress[16]; char oneSubNetMask[16]; int count = 0; char *onenetcard; /* now get all the netcard values which are bound to tcpip */ strcpy(newkey,"SYSTEM\\Currentcontrolset\\Services\\"); strcat(newkey,"tcpip\\linkage"); bSuccess = RegOpenKey(HKEY_LOCAL_MACHINE,newkey,&hk); if(bSuccess != ERROR_SUCCESS) { msyslog(LOG_ERR, "failed to Open TCP/IP Linkage Registry key: %m"); #ifdef DEBUG if (debug) printf("Cannot get TCP/IP Linkage from registery.\n"); #endif return -1; } /* now get the bind value */ sizeofbindnames = 1000; bSuccess = RegQueryValueEx(hk, /* subkey handle */ "Bind", /* value name */ NULL, /* must be zero */ NULL, /* value type not required */ (LPBYTE) &bindservicenames, /* address of value data */ &sizeofbindnames); /* length of value data */ if(bSuccess != ERROR_SUCCESS) { msyslog(LOG_ERR, "Error in RegQueryValueEx fetching Bind Service names parameter: %m"); RegCloseKey(hk); return -1; } /* now loop through and get all the values which are bound to tcpip */ /* we can also close the key here as we have the values now */ RegCloseKey(hk); onenetcard = bindservicenames; while(1) { onenetcard = onenetcard + 8; if ((onenetcard < (bindservicenames + sizeofbindnames)) && (sscanf(onenetcard,"%s",servicename) != EOF)) { onenetcard+= strlen(servicename) + 1; } else { /* no more */ break; } /* * skip services that are NDISWAN... since these are temporary * interfaces like ras and if we bind to these we would have to * check if the socket is still ok everytime before using it as * when the link goes down and comes back up the socket is no * longer any good... and the server eventually crashes if we * don't check this.. and to check it entails a lot of overhead... * shouldn't be a problem with machines with only a RAS * interface anyway as we can bind to the loopback or 0.0.0.0 */ if ((strlen(servicename) >= 7) && (strncmp(strupr(servicename),"NDISWAN",7) == 0)) { /* skip it */ #ifdef DEBUG if (debug) printf("Skippping temporary interface [%s]\n",servicename); #endif } else { /* if opening this key fails we can assume it is not a network card ie digiboard and go on.. */ /* ok now that we have the service name parameter close the key and go get the ipaddress and subnet mask */ strcpy(newkey,"SYSTEM\\Currentcontrolset\\Services\\"); strcat(newkey,servicename); strcat(newkey,"\\parameters\\tcpip"); bSuccess = RegOpenKey(HKEY_LOCAL_MACHINE,newkey,&hksub); if(bSuccess != ERROR_SUCCESS) { #ifdef DEBUG if (debug) printf("Skipping interface [%s] ... It is not a network card.\n",servicename); #endif } else { /* ok it is a network card */ /* ok now get the ipaddress and subnetmask */ sizeofipaddresses = 10000; bSuccess = RegQueryValueEx(hksub, /* subkey handle */ "IpAddress", /* value name */ NULL, /* must be zero */ NULL, /* value type not required */ (LPBYTE)&IpAddresses, /* address of value data */ &sizeofipaddresses); /* length of value data */ if(bSuccess != ERROR_SUCCESS) { msyslog(LOG_ERR, "Error in RegQueryValueEx fetching IpAddress parameter: %m"); RegCloseKey(hksub); return -1; } /* ok now get the ipaddress and subnetmask */ sizeofsubnetmasks = 10000; bSuccess = RegQueryValueEx(hksub, /* subkey handle */ "SubNetMask", /* value name */ NULL, /* must be zero */ NULL, /* value type not required */ (LPBYTE)&SubNetMasks, /* address of value data */ &sizeofsubnetmasks); /* length of value data */ if(bSuccess != ERROR_SUCCESS) { msyslog(LOG_ERR, "Error in RegQueryValueEx fetching SubNetMask parameter: %m"); RegCloseKey(hksub); return -1; } RegCloseKey(hksub); /* ok now that we have some addresses and subnet masks go through each one and add to our structure... */ /* multi_sz strings are terminated by two \0 in a row */ ipptr = IpAddresses; subptr = SubNetMasks; Done = 0; while (!Done) { ifr = (struct ifreq *)ifc_buffer; if (sscanf(ipptr,"%s",oneIpAddress) != EOF) ipptr+= strlen(oneIpAddress) + 1; /* add one for terminator \0 */ else Done = 1; if (sscanf(subptr,"%s",oneSubNetMask) != EOF) subptr += strlen(oneSubNetMask) + 1; else Done = 1; /* now add to interface structure */ if (!Done) { ifr->ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr = inet_addr(oneIpAddress); strcpy(ifr->ifr_mask,oneSubNetMask); if (strlen(servicename) > 15) strncpy(ifr->ifr_name,servicename,15); else strcpy(ifr->ifr_name,servicename); /* now increment pointer */ ifc_buffer += sizeof (ifr->ifr_name) + sizeof(ifr->ifr_addr) + sizeof(ifr->ifr_mask); ++count; if (((char *)ipptr == '\0') || ((char *)subptr == '\0')) Done = 1; } } } /* it is a network card */ } /* it is/not a temporary ndiswan name */ } /* end of loop */ /* now reset the length */ ifc->ifc_len = count * (sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr) + sizeof(ifr->ifr_mask)); return 0; } #endif /* SYS_WINNT */