#include <config.h>
#include "networking.h"
#include "ntp_debug.h"
int
sendpkt (
SOCKET rsock,
sockaddr_u *dest,
struct pkt *pkt,
int len
)
{
int cc;
#ifdef DEBUG
if (debug > 2) {
printf("sntp sendpkt: Packet data:\n");
pkt_output(pkt, len, stdout);
}
#endif
TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
sptoa(dest)));
cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
SOCKLEN(dest));
if (cc == SOCKET_ERROR) {
msyslog(LOG_ERR, "Send to %s failed, %m",
sptoa(dest));
return FALSE;
}
TRACE(1, ("Packet sent.\n"));
return TRUE;
}
int
recvdata(
SOCKET rsock,
struct timeval timeout_tv,
sockaddr_u * sender,
void * rdata,
int rdata_length
)
{
fd_set recv_fd;
GETSOCKNAME_SOCKLEN_TYPE slen;
int recvc;
FD_ZERO(&recv_fd);
FD_SET(rsock, &recv_fd);
switch(select(rsock + 1, &recv_fd, 0, 0, &timeout_tv)) {
case 0:
TRACE(1, ("sntp recvdata: select() reached timeout (%u sec), aborting.\n", (unsigned)timeout_tv.tv_sec));
case -1:
return SERVER_UNUSEABLE;
default:
slen = sizeof(*sender);
recvc = recvfrom(rsock, rdata, rdata_length, 0,
&sender->sa, &slen);
if (recvc < 0)
return recvc;
#ifdef DEBUG
if (debug > 2) {
printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
pkt_output((struct pkt *)rdata, recvc, stdout);
}
#endif
}
return recvc;
}
static void*
skip_efields(
u_int32 *head,
u_int32 *tail
)
{
u_int nlen;
while ((tail - head) > 6) {
nlen = ntohl(*head++) & 0xffff;
nlen = (nlen + 3) >> 2;
if (nlen > (u_int)(tail - head) || nlen < 4)
return NULL;
head += nlen;
}
return head;
}
int
process_pkt (
struct pkt *rpkt,
sockaddr_u *sender,
int pkt_len,
int mode,
struct pkt *spkt,
const char * func_name
)
{
u_int key_id;
struct key * pkt_key;
int is_authentic;
int mac_size;
u_int exten_len;
u_int32 * exten_end;
u_int32 * packet_end;
l_fp sent_xmt;
l_fp resp_org;
pkt_key = NULL;
is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
msyslog(LOG_ERR,
"%s: Incredible packet length: %d. Discarding.",
func_name, pkt_len);
return PACKET_UNUSEABLE;
}
packet_end = (void*)((char*)rpkt + pkt_len);
exten_end = skip_efields(rpkt->exten, packet_end);
if (NULL == exten_end) {
msyslog(LOG_ERR,
"%s: Missing extension field. Discarding.",
func_name);
return PACKET_UNUSEABLE;
}
exten_len = (u_int)(packet_end - exten_end);
switch (exten_len) {
case 0:
break;
case 1:
key_id = ntohl(*exten_end);
printf("Crypto NAK = 0x%08x\n", key_id);
break;
case 3:
msyslog(LOG_ERR,
"%s: Key ID + 3DES MAC is unsupported. Discarding.",
func_name);
return PACKET_UNUSEABLE;
case 5:
case 6:
key_id = ntohl(*exten_end);
get_key(key_id, &pkt_key);
if (!pkt_key) {
printf("unrecognized key ID = 0x%08x\n", key_id);
break;
}
mac_size = exten_len << 2;
if (!auth_md5(rpkt, pkt_len - mac_size,
mac_size - 4, pkt_key)) {
is_authentic = FALSE;
break;
}
is_authentic = TRUE;
TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
func_name, stoa(sender), key_id));
break;
default:
msyslog(LOG_ERR,
"%s: Unexpected extension length: %d. Discarding.",
func_name, exten_len);
return PACKET_UNUSEABLE;
}
switch (is_authentic) {
case -1:
break;
case 0:
return SERVER_AUTH_FAIL;
break;
case 1:
break;
default:
break;
}
if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
msyslog(LOG_ERR,
"%s: Packet shows wrong version (%d)",
func_name, PKT_VERSION(rpkt->li_vn_mode));
return SERVER_UNUSEABLE;
}
if (PKT_MODE(rpkt->li_vn_mode) != mode &&
PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
msyslog(LOG_ERR,
"%s: mode %d stratum %d", func_name,
PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
return SERVER_UNUSEABLE;
}
if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
char *ref_char;
TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
func_name, rpkt->stratum));
ref_char = (char *) &rpkt->refid;
TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
if (ref_char[0] != 'X') {
if (strncmp(ref_char, "DENY", 4) == 0)
return KOD_DEMOBILIZE;
if (strncmp(ref_char, "RSTR", 4) == 0)
return KOD_DEMOBILIZE;
if (strncmp(ref_char, "RATE", 4) == 0)
return KOD_RATE;
}
}
if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
msyslog(LOG_ERR,
"%s: %s not in sync, skipping this server",
func_name, stoa(sender));
return SERVER_UNUSEABLE;
}
if (MODE_BROADCAST == mode)
return pkt_len;
if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
NTOHL_FP(&rpkt->org, &resp_org);
NTOHL_FP(&spkt->xmt, &sent_xmt);
msyslog(LOG_ERR,
"%s response org expected to match sent xmt",
stoa(sender));
msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
return PACKET_UNUSEABLE;
}
return pkt_len;
}