/* * getresponse - get a (series of) response packet(s) and return the data */ static int getresponse( int implcode, int reqcode, int *ritems, int *rsize, char **rdata, int esize ) { struct resp_pkt rpkt; struct sock_timeval tvo; int items; int i; int size; int datasize; char *datap; char *tmp_data; char haveseq[MAXSEQ+1]; int firstpkt; int lastseq; int numrecv; int seq; fd_set fds; int n; int pad; /* * This is pretty tricky. We may get between 1 and many packets * back in response to the request. We peel the data out of * each packet and collect it in one long block. When the last * packet in the sequence is received we'll know how many we * should have had. Note we use one long time out, should reconsider. */ *ritems = 0; *rsize = 0; firstpkt = 1; numrecv = 0; *rdata = datap = pktdata; lastseq = 999; /* too big to be a sequence number */ memset(haveseq, 0, sizeof(haveseq)); FD_ZERO(&fds); again: if (firstpkt) tvo = tvout; else tvo = tvsout; FD_SET(sockfd, &fds); n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo); if (n == -1) { warning("select fails", "", ""); return -1; } if (n == 0) { /* * Timed out. Return what we have */ if (firstpkt) { (void) fprintf(stderr, "%s: timed out, nothing received\n", currenthost); return ERR_TIMEOUT; } else { (void) fprintf(stderr, "%s: timed out with incomplete data\n", currenthost); if (debug) { printf("Received sequence numbers"); for (n = 0; n <= MAXSEQ; n++) if (haveseq[n]) printf(" %d,", n); if (lastseq != 999) printf(" last frame received\n"); else printf(" last frame not received\n"); } return ERR_INCOMPLETE; } } n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); if (n == -1) { warning("read", "", ""); return -1; } /* * Check for format errors. Bug proofing. */ if (n < RESP_HEADER_SIZE) { if (debug) printf("Short (%d byte) packet received\n", n); goto again; } if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION || INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) { if (debug) printf("Packet received with version %d\n", INFO_VERSION(rpkt.rm_vn_mode)); goto again; } if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) { if (debug) printf("Packet received with mode %d\n", INFO_MODE(rpkt.rm_vn_mode)); goto again; } if (INFO_IS_AUTH(rpkt.auth_seq)) { if (debug) printf("Encrypted packet received\n"); goto again; } if (!ISRESPONSE(rpkt.rm_vn_mode)) { if (debug) printf("Received request packet, wanted response\n"); goto again; } if (INFO_MBZ(rpkt.mbz_itemsize) != 0) { if (debug) printf("Received packet with nonzero MBZ field!\n"); goto again; } /* * Check implementation/request. Could be old data getting to us. */ if (rpkt.implementation != implcode || rpkt.request != reqcode) { if (debug) printf( "Received implementation/request of %d/%d, wanted %d/%d", rpkt.implementation, rpkt.request, implcode, reqcode); goto again; } /* * Check the error code. If non-zero, return it. */ if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) { if (debug && ISMORE(rpkt.rm_vn_mode)) { printf("Error code %d received on not-final packet\n", INFO_ERR(rpkt.err_nitems)); } return (int)INFO_ERR(rpkt.err_nitems); } /* * Collect items and size. Make sure they make sense. */ items = INFO_NITEMS(rpkt.err_nitems); size = INFO_ITEMSIZE(rpkt.mbz_itemsize); if (esize > size) pad = esize - size; else pad = 0; datasize = items * size; if ((size_t)datasize > (n-RESP_HEADER_SIZE)) { if (debug) printf( "Received items %d, size %d (total %d), data in packet is %d\n", items, size, datasize, n-RESP_HEADER_SIZE); goto again; } /* * If this isn't our first packet, make sure the size matches * the other ones. */ if (!firstpkt && esize != *rsize) { if (debug) printf("Received itemsize %d, previous %d\n", size, *rsize); goto again; } /* * If we've received this before, +toss it */ seq = INFO_SEQ(rpkt.auth_seq); if (haveseq[seq]) { if (debug) printf("Received duplicate sequence number %d\n", seq); goto again; } haveseq[seq] = 1; /* * If this is the last in the sequence, record that. */ if (!ISMORE(rpkt.rm_vn_mode)) { if (lastseq != 999) { printf("Received second end sequence packet\n"); goto again; } lastseq = seq; } /* * So far, so good. Copy this data into the output array. */ if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) { int offset = datap - pktdata; growpktdata(); *rdata = pktdata; /* might have been realloced ! */ datap = pktdata + offset; } /* * We now move the pointer along according to size and number of * items. This is so we can play nice with older implementations */ tmp_data = rpkt.data; for (i = 0; i < items; i++) { memcpy(datap, tmp_data, (unsigned)size); tmp_data += size; memset(datap + size, 0, pad); datap += size + pad; } if (firstpkt) { firstpkt = 0; *rsize = size + pad; } *ritems += items; /* * Finally, check the count of received packets. If we've got them * all, return */ ++numrecv; if (numrecv <= lastseq) goto again; return INFO_OKAY; }
/* * request - send a configuration request to the server, wait for a response */ static int request( struct conf_peer *conf ) { struct sock_timeval tvout; struct req_pkt reqpkt; size_t req_len; size_t total_len; /* req_len plus keyid & digest */ fd_set fdset; l_fp ts; char * pch; char * pchEnd; l_fp * pts; keyid_t *pkeyid; int n; #ifdef SYS_WINNT HANDLE hReadWriteEvent = NULL; BOOL ret; DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait; OVERLAPPED overlap; #endif /* SYS_WINNT */ checkparent(); /* make sure our guy is still running */ if (sockfd == INVALID_SOCKET) openntp(); #ifdef SYS_WINNT hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); #endif /* SYS_WINNT */ /* * Try to clear out any previously received traffic so it * doesn't fool us. Note the socket is nonblocking. */ tvout.tv_sec = 0; tvout.tv_usec = 0; FD_ZERO(&fdset); FD_SET(sockfd, &fdset); while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) > 0) { recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0); FD_ZERO(&fdset); FD_SET(sockfd, &fdset); } /* * Make up a request packet with the configuration info */ memset(&reqpkt, 0, sizeof(reqpkt)); reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0); reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */ reqpkt.implementation = IMPL_XNTPD; /* local implementation */ reqpkt.request = REQ_CONFIG; /* configure a new peer */ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(*conf)); /* Make sure mbz_itemsize <= sizeof reqpkt.data */ if (sizeof(*conf) > sizeof(reqpkt.data)) { msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!"); resolver_exit(1); } memcpy(reqpkt.data, conf, sizeof(*conf)); if (sys_authenticate && req_hashlen > 16) { pch = reqpkt.data; /* 32-bit alignment */ pch += (sizeof(*conf) + 3) & ~3; pts = (void *)pch; pkeyid = (void *)(pts + 1); pchEnd = (void *)pkeyid; req_len = pchEnd - (char *)&reqpkt; pchEnd = (void *)(pkeyid + 1); pchEnd += req_hashlen; total_len = pchEnd - (char *)&reqpkt; if (total_len > sizeof(reqpkt)) { msyslog(LOG_ERR, "intres total_len %lu limit is %lu (%lu octet digest)\n", (u_long)total_len, (u_long)sizeof(reqpkt), (u_long)req_hashlen); resolver_exit(1); } } else { pts = &reqpkt.tstamp; pkeyid = &reqpkt.keyid; req_len = REQ_LEN_NOMAC; } *pkeyid = htonl(req_keyid); get_systime(&ts); L_ADDUF(&ts, SKEWTIME); HTONL_FP(&ts, pts); if (sys_authenticate) { n = authencrypt(req_keyid, (void *)&reqpkt, req_len); if ((size_t)n != req_hashlen + sizeof(reqpkt.keyid)) { msyslog(LOG_ERR, "intres maclen %d expected %u\n", n, (u_long)(req_hashlen + sizeof(reqpkt.keyid))); resolver_exit(1); } req_len += n; } /* * Done. Send it. */ #ifndef SYS_WINNT n = send(sockfd, (char *)&reqpkt, req_len, 0); if (n < 0) { msyslog(LOG_ERR, "send to NTP server failed: %m"); return 0; /* maybe should exit */ } #else /* In the NT world, documentation seems to indicate that there * exist _write and _read routines that can be used to do blocking * I/O on sockets. Problem is these routines require a socket * handle obtained through the _open_osf_handle C run-time API * of which there is no explanation in the documentation. We need * nonblocking write's and read's anyway for our purpose here. * We're therefore forced to deviate a little bit from the Unix * model here and use the ReadFile and WriteFile Win32 I/O API's * on the socket */ overlap.Offset = overlap.OffsetHigh = (DWORD)0; overlap.hEvent = hReadWriteEvent; ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, req_len, NULL, (LPOVERLAPPED)&overlap); if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) { msyslog(LOG_ERR, "send to NTP server failed: %m"); return 0; } dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000); if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) { if (dwWait == WAIT_FAILED) msyslog(LOG_ERR, "WaitForSingleObject failed: %m"); return 0; } if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap, (LPDWORD)&NumberOfBytesWritten, FALSE)) { msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m"); return 0; } #endif /* SYS_WINNT */ /* * Wait for a response. A weakness of the mode 7 protocol used * is that there is no way to associate a response with a * particular request, i.e. the response to this configuration * request is indistinguishable from that to any other. I should * fix this some day. In any event, the time out is fairly * pessimistic to make sure that if an answer is coming back * at all, we get it. */ for (;;) { FD_ZERO(&fdset); FD_SET(sockfd, &fdset); tvout.tv_sec = TIMEOUT_SEC; tvout.tv_usec = TIMEOUT_USEC; n = select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout); if (n < 0) { if (errno != EINTR) msyslog(LOG_ERR, "select() fails: %m"); return 0; } else if (n == 0) { #ifdef DEBUG if (debug) msyslog(LOG_INFO, "ntp_intres select() returned 0."); #endif return 0; } #ifndef SYS_WINNT n = recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0); if (n <= 0) { if (n < 0) { msyslog(LOG_ERR, "recv() fails: %m"); return 0; } continue; } #else /* Overlapped I/O used on non-blocking sockets on Windows NT */ ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, sizeof(reqpkt), NULL, (LPOVERLAPPED)&overlap); if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) { msyslog(LOG_ERR, "ReadFile() fails: %m"); return 0; } dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000); if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) { if (dwWait == WAIT_FAILED) { msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m"); return 0; } continue; } if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap, (LPDWORD)&NumberOfBytesRead, FALSE)) { msyslog(LOG_ERR, "GetOverlappedResult fails: %m"); return 0; } n = NumberOfBytesRead; #endif /* SYS_WINNT */ /* * Got one. Check through to make sure it is what * we expect. */ if (n < RESP_HEADER_SIZE) { msyslog(LOG_ERR, "received runt response (%d octets)", n); continue; } if (!ISRESPONSE(reqpkt.rm_vn_mode)) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "received non-response packet"); #endif continue; } if (ISMORE(reqpkt.rm_vn_mode)) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "received fragmented packet"); #endif continue; } if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2) || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION)) || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "version (%d/%d) or mode (%d/%d) incorrect", INFO_VERSION(reqpkt.rm_vn_mode), NTP_VERSION, INFO_MODE(reqpkt.rm_vn_mode), MODE_PRIVATE); #endif continue; } if (INFO_SEQ(reqpkt.auth_seq) != 0) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "nonzero sequence number (%d)", INFO_SEQ(reqpkt.auth_seq)); #endif continue; } if (reqpkt.implementation != IMPL_XNTPD || reqpkt.request != REQ_CONFIG) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "implementation (%d) or request (%d) incorrect", reqpkt.implementation, reqpkt.request); #endif continue; } if (INFO_NITEMS(reqpkt.err_nitems) != 0 || INFO_MBZ(reqpkt.mbz_itemsize) != 0 || INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "nitems (%d) mbz (%d) or itemsize (%d) nonzero", INFO_NITEMS(reqpkt.err_nitems), INFO_MBZ(reqpkt.mbz_itemsize), INFO_ITEMSIZE(reqpkt.mbz_itemsize)); #endif continue; } n = INFO_ERR(reqpkt.err_nitems); switch (n) { case INFO_OKAY: /* success */ return 1; case INFO_ERR_NODATA: /* * newpeer() refused duplicate association, no * point in retrying so call it success. */ return 1; case INFO_ERR_IMPL: msyslog(LOG_ERR, "ntp_intres.request: implementation mismatch"); return 0; case INFO_ERR_REQ: msyslog(LOG_ERR, "ntp_intres.request: request unknown"); return 0; case INFO_ERR_FMT: msyslog(LOG_ERR, "ntp_intres.request: format error"); return 0; case INFO_ERR_AUTH: msyslog(LOG_ERR, "ntp_intres.request: permission denied"); return 0; default: msyslog(LOG_ERR, "ntp_intres.request: unknown error code %d", n); return 0; } } }