diff options
author | roberto <roberto@FreeBSD.org> | 2004-07-20 15:01:56 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 2004-07-20 15:01:56 +0000 |
commit | 118e757284cbb8fc4f43a713e892b41504b50a5f (patch) | |
tree | 528d12dda44ebdc3ffcc38050f159ac553a69c17 /contrib/ntp/sntp/socket.c | |
parent | a85d9ae25e8e8696677bc30feb6eaf7fc150e529 (diff) | |
download | FreeBSD-src-118e757284cbb8fc4f43a713e892b41504b50a5f.zip FreeBSD-src-118e757284cbb8fc4f43a713e892b41504b50a5f.tar.gz |
Virgin import of ntpd 4.2.0
Diffstat (limited to 'contrib/ntp/sntp/socket.c')
-rw-r--r-- | contrib/ntp/sntp/socket.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/contrib/ntp/sntp/socket.c b/contrib/ntp/sntp/socket.c new file mode 100644 index 0000000..75fd461 --- /dev/null +++ b/contrib/ntp/sntp/socket.c @@ -0,0 +1,395 @@ +/* Copyright (C) 1996, 2000 N.M. Maclaren + Copyright (C) 1996, 2000 The University of Cambridge + +This includes all of the code needed to handle Berkeley sockets. It is way +outside current POSIX, unfortunately. It should be easy to convert to a system +that uses another mechanism. It does not currently use socklen_t, because +the only system that the author uses that has it is Linux. */ + + + +#include "header.h" +#include "internet.h" +#include <fcntl.h> + +#define SOCKET +#include "kludges.h" +#undef SOCKET + + + +/* The code needs to set some variables during the open, for use by later +functions. */ + +static int initial = 1, + descriptors[MAX_SOCKETS]; + +#ifdef HAVE_IPV6 +static struct sockaddr_storage here[MAX_SOCKETS], there[MAX_SOCKETS]; +#else +static struct sockaddr_in here[MAX_SOCKETS], there[MAX_SOCKETS]; +#endif + + +/* There needs to be some disgusting grobble for handling timeouts, that is +identical to the grobble in internet.c. */ + +static jmp_buf jump_buffer; + +static void jump_handler (int sig) { + longjmp(jump_buffer,1); +} + +static void clear_alarm (void) { + int k; + + k = errno; + alarm(0); + errno = 0; + if (signal(SIGALRM,SIG_DFL) == SIG_ERR) + fatal(1,"unable to reset signal handler",NULL); + errno = k; +} + + + +void display_in_hex (const void *data, int length) { + int i; + + for (i = 0; i < length; ++i) + fprintf(stderr,"%.2x",((const unsigned char *)data)[i]); +} + +#ifdef HAVE_IPV6 + +void display_sock_in_hex (struct sockaddr_storage *sock) { + int family, len; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + family = sock->ss_family; + switch(family) { + case AF_INET: + sin = (struct sockaddr_in *)sock; + display_in_hex(&sin->sin_addr, sizeof(struct in_addr)); + fprintf(stderr,"/"); + display_in_hex(&sin->sin_port, 2); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sock; + display_in_hex(&sin6->sin6_addr, sizeof(struct in6_addr)); + fprintf(stderr,"/"); + display_in_hex(&sin6->sin6_port, 2); + break; + } +} + +#else + +void display_sock_in_hex (struct sockaddr_in *sock) { + int family, len; + struct sockaddr_in *sin; + + family = sock->sin_family; + switch(family) { + case AF_INET: + sin = (struct sockaddr_in *)sock; + display_in_hex(&sin->sin_addr, sizeof(struct in_addr)); + fprintf(stderr,"/"); + display_in_hex(&sin->sin_port, 2); + break; + } +} +#endif + +#ifdef HAVE_IPV6 + +void open_socket (int which, char *hostname, int timespan) { + +/* Locate the specified NTP server, set up a couple of addresses and open a +socket. */ + + int port, k, sl; + struct sockaddr_storage address, anywhere, everywhere; + +/* Initialise and find out the server and port number. Note that the port +number is in network format. */ + + if (initial) + for (k = 0; k < MAX_SOCKETS; ++k) + descriptors[k] = -1; + initial = 0; + if (which < 0 || which >= MAX_SOCKETS || descriptors[which] >= 0) + fatal(0,"socket index out of range or already open",NULL); + if (verbose > 2) + fprintf(stderr,"Looking for the socket addresses\n"); + find_address(&address,&anywhere,&everywhere,&port,hostname,timespan); + if (verbose > 2) { + fprintf(stderr,"Internet address: address="); + display_sock_in_hex(&address); + fprintf(stderr," anywhere="); + display_sock_in_hex(&anywhere); + fprintf(stderr," everywhere="); + display_sock_in_hex(&everywhere); + fputc('\n',stderr); + } + +/* Set up our own and the target addresses. Note that the target address will +be reset before use in server mode. */ + + memset(&here[which], 0, sizeof(struct sockaddr_storage)); + here[which] = anywhere; + if (!(operation == op_listen || operation == op_server)) + ((struct sockaddr_in6 *)&here[which])->sin6_port = 0; + memset(&there[which], 0, sizeof(struct sockaddr_storage)); + there[which] = (operation == op_broadcast ? everywhere : address); + if (verbose > 2) { + fprintf(stderr,"Initial sockets: here="); + display_sock_in_hex(&here[which]); + fprintf(stderr," there="); + display_sock_in_hex(&there[which]); + fputc('\n',stderr); + } + +/* Allocate a local UDP socket and configure it. */ + + switch(((struct sockaddr_in *)&there[which])->sin_family) { + case AF_INET: + sl = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + sl = sizeof(struct sockaddr_in6); + break; +#endif + default: + sl = 0; + break; + } + errno = 0; + if ((descriptors[which] = socket(here[which].ss_family,SOCK_DGRAM,0)) < 0 + || bind(descriptors[which],(struct sockaddr *)&here[which], sl) < 0) + fatal(1,"unable to allocate socket for NTP",NULL); + if (operation == op_broadcast) { + errno = 0; + k = setsockopt(descriptors[which],SOL_SOCKET,SO_BROADCAST, + (void *)&k,sizeof(k)); + if (k != 0) fatal(1,"unable to set permission to broadcast",NULL); + } +} + +#else + +void open_socket (int which, char *hostname, int timespan) { + +/* Locate the specified NTP server, set up a couple of addresses and open a +socket. */ + + int port, k; + struct in_addr address, anywhere, everywhere; + +/* Initialise and find out the server and port number. Note that the port +number is in network format. */ + + if (initial) for (k = 0; k < MAX_SOCKETS; ++k) descriptors[k] = -1; + initial = 0; + if (which < 0 || which >= MAX_SOCKETS || descriptors[which] >= 0) + fatal(0,"socket index out of range or already open",NULL); + if (verbose > 2) fprintf(stderr,"Looking for the socket addresses\n"); + find_address(&address,&anywhere,&everywhere,&port,hostname,timespan); + if (verbose > 2) { + fprintf(stderr,"Internet address: address="); + display_in_hex(&address,sizeof(struct in_addr)); + fprintf(stderr," anywhere="); + display_in_hex(&anywhere,sizeof(struct in_addr)); + fprintf(stderr," everywhere="); + display_in_hex(&everywhere,sizeof(struct in_addr)); + fputc('\n',stderr); + } + +/* Set up our own and the target addresses. Note that the target address will +be reset before use in server mode. */ + + memset(&here[which],0,sizeof(struct sockaddr_in)); + here[which].sin_family = AF_INET; + here[which].sin_port = + (operation == op_listen || operation == op_server ? port : 0); + here[which].sin_addr = anywhere; + memset(&there[which],0,sizeof(struct sockaddr_in)); + there[which].sin_family = AF_INET; + there[which].sin_port = port; + there[which].sin_addr = (operation == op_broadcast ? everywhere : address); + if (verbose > 2) { + fprintf(stderr,"Initial sockets: here="); + display_in_hex(&here[which].sin_addr,sizeof(struct in_addr)); + fputc('/',stderr); + display_in_hex(&here[which].sin_port,sizeof(here[which].sin_port)); + fprintf(stderr," there="); + display_in_hex(&there[which].sin_addr,sizeof(struct in_addr)); + fputc('/',stderr); + display_in_hex(&there[which].sin_port,sizeof(there[which].sin_port)); + fputc('\n',stderr); + } + +/* Allocate a local UDP socket and configure it. */ + + errno = 0; + if ((descriptors[which] = socket(AF_INET,SOCK_DGRAM,0)) < 0 || + bind(descriptors[which],(struct sockaddr *)&here[which], + sizeof(here[which])) < 0) + fatal(1,"unable to allocate socket for NTP",NULL); + if (operation == op_broadcast) { + errno = 0; + k = setsockopt(descriptors[which],SOL_SOCKET,SO_BROADCAST, + (void *)&k,sizeof(k)); + if (k != 0) fatal(1,"unable to set permission to broadcast",NULL); + } +} + +#endif + +extern void write_socket (int which, void *packet, int length) { + +/* Any errors in doing this are fatal - including blocking. Yes, this leaves a +server vulnerable to a denial of service attack. */ + + int k, sl; + + switch(((struct sockaddr_in *)&there[which])->sin_family) { + case AF_INET: + sl = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + sl = sizeof(struct sockaddr_in6); + break; +#endif + default: + sl = 0; + break; + } + if (which < 0 || which >= MAX_SOCKETS || descriptors[which] < 0) + fatal(0,"socket index out of range or not open",NULL); + errno = 0; + k = sendto(descriptors[which],packet,(size_t)length,0, + (struct sockaddr *)&there[which],sl); + if (k != length) fatal(1,"unable to send NTP packet",NULL); +} + + + +extern int read_socket (int which, void *packet, int length, int waiting) { + +/* Read a packet and return its length or -1 for failure. Only incorrect +length and timeout are not fatal. */ + +#ifdef HAVE_IPV6 + struct sockaddr_storage scratch, *ptr; +#else + struct sockaddr_in scratch, *ptr; +#endif + int n; + int k; + +/* Under normal circumstances, set up a timeout. */ + + if (which < 0 || which >= MAX_SOCKETS || descriptors[which] < 0) + fatal(0,"socket index out of range or not open",NULL); + if (waiting > 0) { + if (setjmp(jump_buffer)) { + if (verbose > 2) + fprintf(stderr,"Receive timed out\n"); + else if (verbose > 1) + fprintf(stderr,"%s: receive timed out after %d seconds\n", + argv0,waiting); + return -1; + } + errno = 0; + if (signal(SIGALRM,jump_handler) == SIG_ERR) + fatal(1,"unable to set up signal handler",NULL); + alarm((unsigned int)waiting); + } + +/* Get the packet and clear the timeout, if any. */ + + if (operation == op_server) + memcpy(ptr = &there[which],&here[which],sizeof(scratch)); + else + memcpy(ptr = &scratch,&there[which],sizeof(scratch)); + n = sizeof(scratch); + errno = 0; + k = recvfrom(descriptors[which],packet,(size_t)length,0, + (struct sockaddr *)ptr,&n); + if (waiting > 0) clear_alarm(); + +/* Now issue some low-level diagnostics. */ + + if (k <= 0) fatal(1,"unable to receive NTP packet from server",NULL); + if (verbose > 2) { + fprintf(stderr,"Packet of length %d received from ",k); + display_sock_in_hex(ptr); + fputc('\n',stderr); + } + return k; +} + + + +extern int flush_socket (int which) { + +/* Get rid of any outstanding input, because it may have been hanging around +for a while. Ignore packet length oddities and return the number of packets +skipped. */ + +#ifdef HAVE_IPV6 + struct sockaddr_storage scratch; +#else + struct sockaddr_in scratch; +#endif + int n; + char buffer[256]; + int flags, count = 0, total = 0, k; + +/* The code is the obvious. */ + + if (which < 0 || which >= MAX_SOCKETS || descriptors[which] < 0) + fatal(0,"socket index out of range or not open",NULL); + if (verbose > 2) fprintf(stderr,"Flushing outstanding packets\n"); + errno = 0; + if ((flags = fcntl(descriptors[which],F_GETFL,0)) < 0 || + fcntl(descriptors[which],F_SETFL,flags|O_NONBLOCK) == -1) + fatal(1,"unable to set non-blocking mode",NULL); + while (1) { + n = sizeof(scratch); + errno = 0; + k = recvfrom(descriptors[which],buffer,256,0, + (struct sockaddr *)&scratch,&n); + if (k < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) break; + fatal(1,"unable to flush socket",NULL); + } + ++count; + total += k; + } + errno = 0; + if (fcntl(descriptors[which],F_SETFL,flags) == -1) + fatal(1,"unable to restore blocking mode",NULL); + if (verbose > 2) + fprintf(stderr,"Flushed %d packets totalling %d bytes\n",count,total); + return count; +} + + + +extern void close_socket (int which) { + +/* There is little point in shielding this with a timeout, because any hangs +are unlikely to be interruptible. It can get called when the sockets haven't +been opened, so ignore that case. */ + + if (which < 0 || which >= MAX_SOCKETS) + fatal(0,"socket index out of range",NULL); + if (descriptors[which] < 0) return; + errno = 0; + if (close(descriptors[which])) fatal(1,"unable to close NTP socket",NULL); +} |