/* $Revision: 1.2 $ */ /*+ ntpmon.c * * Called by main(). * Creates an NTP query packet, and sends it to the remote ntp daemon. * Then waits for a reply and analyses it for the stratum value. * * A C version of the ntpmon PERL monitor sent by mathias@singnet.com.sg * (Mathias Koerber). Just seems easier to manipulate bits and structures * in C rather than in Perl... * * Copyright 1997, Netplex Technologies Inc, info@netplex-tech.com */ /* * $Log: ntpmon.c,v $ * Revision 1.2 1998/08/13 11:41:51 vikas * Changed goto into a while() loop. * * Revision 1.1 1998/06/30 20:25:10 vikas * Initial revision * * */ #include "ntpmon.h" int readconf(); static int sendpkt(); static int get_inet_address(); extern int debug; /* Defined in main.c */ extern char *prognm; #ifndef NTP_PORT # define NTP_PORT 123 #endif /* * Create a control packet and send it to the ntp deamon. * The packet is a ntp_control structure with no data. * The return packet is also ntp_control, but the data field contains * amongst others the string "stratum=nn" where nn is the stratum number. * This number is the one we are interested in. * Returns 255 in case of error, else the stratum of the remote clock (1-16) */ ntpmon(host) char *host; /* In dotted decimal addr */ { struct ntp_control qpkt, rpkt; struct sockaddr_in hostaddr; int pktsize, n, stratum; int sockfd; int pktversion = 3; int sequence = time(NULL) % 65000; /* Set up the packet */ int opcode = CTL_OP_READVAR; qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL); qpkt.r_m_e_op = (u_char)opcode & CTL_OP_MASK; qpkt.sequence = htons(sequence); qpkt.status = 0; qpkt.associd = htons((u_short)0); qpkt.offset = 0; qpkt.count = 0; pktsize = CTL_HEADER_LEN; /* setup the socket */ if(!get_inet_address(&hostaddr, host)) return 255; /* indicate error */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); hostaddr.sin_family = AF_INET; hostaddr.sin_port = htons(NTP_PORT); /* we do a connect (instead of a sendto() )so that we can do a select * during the read() later on */ if (connect(sockfd, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0) { if (debug) perror("connect"); close(sockfd); return 255; } /* Send the packet */ if (write(sockfd, &qpkt, pktsize) == -1) { if (debug) perror("write"); close(sockfd); return 255; } /* Read the response packet and analyse it */ if((stratum = get_response(sockfd, sequence, TIMEOUT)) < 0 ) { if(debug) fprintf(stderr, "%s:ntp error for %s \n", prognm, host); stratum = 255; } else if(debug) fprintf(stderr,"(debug)%s: Stratum for %s = %d\n", prognm, host,stratum); close(sockfd); return stratum; } /* ntpmon() */ /* * Receives a packet from the ntp server, does some checking and returns * stratum. Returns -1 for error. */ int get_response(sockfd, sequence, timeout) int sockfd; int sequence; int timeout; { struct ntp_control rpkt; int n, fromlen, stratum; char buf[500]; struct sockaddr from; char *loc; fd_set fdvar; struct timeval tval ; bzero(&rpkt, sizeof(rpkt)); tval.tv_sec = timeout; tval.tv_usec = 0; /* * we loop until timeout to make sure that we dont get back an old * response. This is verified by the sequence number we sent... */ while (1) { FD_ZERO(&fdvar); FD_SET(sockfd, &fdvar); if ((n = select(sockfd+1, &fdvar, NULL, NULL, &tval)) <= 0) { if (n < 0 && errno != EINTR) { perror("select() "); return (-1); } else { if (debug > 2) fprintf(stderr, "select() timed out\n"); return (-1); } } /* we dont do an FD_ISSET() since only one fd was set... */ if ( (n = read(sockfd, &rpkt, sizeof(rpkt)) ) <= 0 ) { perror("read"); return -1; } /* * Check opcode and sequence number for a match. * Could be old data getting to us. */ if (ntohs(rpkt.sequence) == sequence) break; /* out of while */ if (debug > 2) fprintf(stderr,"ntpmon: Received sequence number %d, wanted %d\n", ntohs(rpkt.sequence), sequence); } /* while(1) */ /* * Check the error code. If non-zero, return it. */ if (CTL_ISERROR(rpkt.r_m_e_op)) { int errcode; errcode = (ntohs(rpkt.status) >> 8) & 0xff; if (debug && CTL_ISMORE(rpkt.r_m_e_op)) fprintf(stderr, "Error code %d received \n",errcode); return (-1); } *(rpkt.data + ntohs(rpkt.count)) = '\0'; /* put a terminating NULL */ if (debug > 3) { /* write(2, rpkt.data, ntohs(rpkt.count)); /* dump pkt to stderr */ } /* * Looking for the string : * "stratum=nn" */ if ( (loc = (char *)strstr(rpkt.data, "stratum=")) == NULL ) return (-1); sscanf(loc + strlen("stratum="), "%d", &stratum); /* */ /* *(loc + 10) = '\0'; /* End string after "stratum=nn" */ /* stratum = atoi(loc + strlen("stratum=")); /* */ return stratum; } /* get_response() */ /* * Instead of inet_addr() which can only handle IP addresses, this handles * hostnames as well... * Return 0 on error, 1 if okay. */ static int get_inet_address(addr, host) struct sockaddr_in *addr; /* must be a malloced structure */ char *host; { register struct hostent *hp; bzero((char *)addr, sizeof (struct sockaddr_in)); addr->sin_family = AF_INET; addr->sin_addr.s_addr = (u_long) inet_addr(host); if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) { if ((hp = gethostbyname(host)) == NULL) { fprintf(stderr, "%s is unknown host\n", host); return 0; } #ifdef h_addr /* in netdb.h */ bcopy((char *)hp->h_addr, (char *)&(addr->sin_addr), hp->h_length); #else bcopy((char *)hp->h_addr_list[0], (char *)&(addr->sin_addr), hp->h_length); #endif } return 1; }