1/*  Copyright (C) 1996 N.M. Maclaren
2    Copyright (C) 1996 The University of Cambridge
3
4This includes all of the code needed to handle Internet addressing.  It is way
5outside current POSIX, unfortunately.  It should be easy to convert to a system
6that uses another mechanism.  The signal handling is not necessary for its
7function, but is an attempt to avoid the program hanging when the name server
8is inaccessible. */
9
10
11
12#include "header.h"
13#include "internet.h"
14
15#include <netdb.h>
16#include <arpa/inet.h>
17
18#define INTERNET
19#include "kludges.h"
20#undef INTERNET
21
22
23/* Used to force dns resolving to ipv4 or ipv6 addresses. */
24static int pref_family;
25
26/* There needs to be some disgusting grobble for handling timeouts, which is
27identical to the grobble in socket.c. */
28
29static jmp_buf jump_buffer;
30
31static void jump_handler (int sig) {
32    longjmp(jump_buffer,1);
33}
34
35static void clear_alarm (void) {
36    int k;
37
38    k = errno;
39    alarm(0);
40    errno = 0;
41    if (signal(SIGALRM,SIG_DFL) == SIG_ERR)
42        fatal(1,"unable to reset signal handler",NULL);
43    errno = k;
44}
45
46void preferred_family(int fam) {
47	switch(fam) {
48	case PREF_FAM_INET:
49	    pref_family = AF_INET;
50	    break;
51#ifdef HAVE_IPV6
52	case PREF_FAM_INET6:
53	    pref_family = AF_INET6;
54	    break;
55#endif
56	default:
57	    fatal(0,"unable to set the preferred family", NULL);
58	    break;
59	}
60}
61
62#ifdef HAVE_IPV6
63
64void find_address (struct sockaddr_storage *address,
65    struct sockaddr_storage *anywhere,
66    int *port, char *hostname, int timespan) {
67
68/* Locate the specified NTP server and return its Internet address and port
69number. */
70
71    int family, rval;
72    struct addrinfo hints;
73    struct addrinfo *res;
74
75    res = NULL;
76    memset(address, 0, sizeof(struct sockaddr_storage));
77    memset(anywhere, 0, sizeof(struct sockaddr_storage));
78
79    if (setjmp(jump_buffer))
80        fatal(0,"unable to set up access to NTP server %s",hostname);
81    errno = 0;
82    if (signal(SIGALRM,jump_handler) == SIG_ERR)
83        fatal(1,"unable to set up signal handler",NULL);
84    alarm((unsigned int)timespan);
85
86/* Look up the Internet name or IP number. */
87    memset(&hints, 0, sizeof(hints));
88    hints.ai_socktype = SOCK_DGRAM;
89    hints.ai_family = pref_family;
90    rval = getaddrinfo(hostname, "ntp", &hints, &res);
91    if (rval != 0)
92	fatal(0, "getaddrinfo(hostname, ntp)  failed with %s",
93	    gai_strerror(rval));
94
95/* Now clear the timer and check the result. */
96
97    clear_alarm();
98    /* There can be more than one address in the list, but for now only
99    use the first. */
100    memcpy(address, res->ai_addr, res->ai_addrlen);
101    family = res->ai_family;
102    freeaddrinfo(res);
103
104    switch(family) {
105    case AF_INET:
106	hints.ai_family = AF_INET;
107	hints.ai_flags = AI_PASSIVE;
108	rval = getaddrinfo(NULL, "ntp", &hints, &res);
109	if (rval != 0)
110	    fatal(0, "getaddrinfo(NULL, ntp) failed with %s",
111		gai_strerror(rval));
112	memcpy(anywhere, res->ai_addr, res->ai_addrlen);
113	freeaddrinfo(res);
114	break;
115    case AF_INET6:
116	hints.ai_family = AF_INET6;
117	hints.ai_flags = AI_PASSIVE;
118	rval = getaddrinfo(NULL, "ntp", &hints, &res);
119	if (rval != 0)
120	    fatal(0, "getaddrinfo(NULL, ntp, INET6, AI_PASSIVE) failed with %s",
121		gai_strerror(rval));
122	memcpy(anywhere, res->ai_addr, res->ai_addrlen);
123	freeaddrinfo(res);
124	break;
125    }
126}
127
128#else
129
130void find_address (struct in_addr *address, struct in_addr *anywhere,
131    int *port, char *hostname, int timespan) {
132
133/* Locate the specified NTP server and return its Internet address and port
134number. */
135
136    unsigned long ipaddr;
137    struct in_addr nowhere[1];
138    struct hostent *host;
139    struct servent *service;
140
141/* Set up the reserved Internet addresses, attempting not to assume that
142addresses are 32 bits. */
143
144    local_to_address(nowhere,INADDR_LOOPBACK);
145    local_to_address(anywhere,INADDR_ANY);
146
147/* Check the address, if any.  This assumes that the DNS is reliable, or is at
148least checked by someone else.  But it doesn't assume that it is accessible, so
149it needs to set up a timeout. */
150
151    if (hostname == NULL)
152        *address = *anywhere;
153    else {
154        if (setjmp(jump_buffer))
155            fatal(0,"unable to set up access to NTP server %s",hostname);
156        errno = 0;
157        if (signal(SIGALRM,jump_handler) == SIG_ERR)
158            fatal(1,"unable to set up signal handler",NULL);
159        alarm((unsigned int)timespan);
160
161/* Look up the Internet name or IP number. */
162
163        if (! isdigit(hostname[0])) {
164            errno = 0;
165            host = gethostbyname(hostname);
166        } else {
167            if ((ipaddr = inet_addr(hostname)) == (unsigned long)-1)
168                fatal(0,"invalid IP number %s",hostname);
169            network_to_address(address,ipaddr);
170            errno = 0;
171            host = gethostbyaddr((void *)address,sizeof(struct in_addr),
172                AF_INET);
173        }
174
175/* Now clear the timer and check the result. */
176
177        clear_alarm();
178        if (host == NULL) fatal(1,"unable to locate IP address/number",NULL);
179        if (host->h_length != sizeof(struct in_addr))
180            fatal(0,"the address does not seem to be an Internet one",NULL);
181        *address = *((struct in_addr **)host->h_addr_list)[0];
182        if (memcmp(address,nowhere,sizeof(struct in_addr)) == 0
183	    || memcmp(address,anywhere,sizeof(struct in_addr)) == 0)
184            fatal(0,"reserved IP numbers cannot be used",NULL);
185        if (verbose)
186            fprintf(stderr,
187                "%s: using NTP server %s (%s)\n",
188                argv0,host->h_name,inet_ntoa(*address));
189    }
190
191/* Find out the port number (usually from /etc/services), and leave it in
192network format.  This is assumed not to be obtained from a network service!
193Note that a port number is not assumed to be 16 bits. */
194
195    if ((service = getservbyname("ntp","udp")) != NULL) {
196        *port = service->s_port;
197        if (verbose > 2)
198            fprintf(stderr,"Using port %d for NTP\n",port_to_integer(*port));
199    } else {
200        *port = NTP_PORT;
201        if (verbose)
202            fprintf(stderr,
203                "%s: assuming port %d for NTP - check /etc/services\n",
204                argv0,port_to_integer(*port));
205    }
206}
207#endif
208