aboutsummaryrefslogtreecommitdiffstats
path: root/libntp
diff options
context:
space:
mode:
Diffstat (limited to 'libntp')
-rw-r--r--libntp/a_md5encrypt.c2
-rw-r--r--libntp/decodenetnum.c200
-rw-r--r--libntp/recvbuff.c204
-rw-r--r--libntp/strdup.c34
-rw-r--r--libntp/timexsup.c7
5 files changed, 288 insertions, 159 deletions
diff --git a/libntp/a_md5encrypt.c b/libntp/a_md5encrypt.c
index 8c046f4e93c9..57100de3a86e 100644
--- a/libntp/a_md5encrypt.c
+++ b/libntp/a_md5encrypt.c
@@ -93,7 +93,7 @@ make_mac(
}
cmac_fail:
if (ctx)
- CMAC_CTX_cleanup(ctx);
+ CMAC_CTX_free(ctx);
}
else
# endif /*ENABLE_CMAC*/
diff --git a/libntp/decodenetnum.c b/libntp/decodenetnum.c
index 35e839aafb09..8ff67625202c 100644
--- a/libntp/decodenetnum.c
+++ b/libntp/decodenetnum.c
@@ -13,106 +13,152 @@
#include "ntp.h"
#include "ntp_stdlib.h"
-#include "ntp_assert.h"
-#define PORTSTR(x) _PORTSTR(x)
-#define _PORTSTR(x) #x
-static int
-isnumstr(
- const char *s
+/* If the given string position points to a decimal digit, parse the
+ * number. If this is not possible, or the parsing did not consume the
+ * whole string, or if the result exceeds the maximum value, return the
+ * default value.
+ */
+static unsigned long
+_num_or_dflt(
+ char * sval,
+ unsigned long maxval,
+ unsigned long defval
)
{
- while (*s >= '0' && *s <= '9')
- ++s;
- return !*s;
+ char * ep;
+ unsigned long num;
+
+ if (!(sval && isdigit(*(unsigned char*)sval)))
+ return defval;
+
+ num = strtoul(sval, &ep, 10);
+ if (!*ep && num <= maxval)
+ return num;
+
+ return defval;
+}
+
+/* If the given string position is not NULL and does not point to the
+ * terminator, replace the character with NUL and advance the pointer.
+ * Return the resulting position.
+ */
+static inline char*
+_chop(
+ char * sp)
+{
+ if (sp && *sp)
+ *sp++ = '\0';
+ return sp;
+}
+
+/* If the given string position points to the given char, advance the
+ * pointer and return the result. Otherwise, return NULL.
+ */
+static inline char*
+_skip(
+ char * sp,
+ int ch)
+{
+ if (sp && *(unsigned char*)sp == ch)
+ return (sp + 1);
+ return NULL;
}
/*
* decodenetnum convert text IP address and port to sockaddr_u
*
- * Returns 0 for failure, 1 for success.
+ * Returns FALSE (->0) for failure, TRUE (->1) for success.
*/
int
decodenetnum(
const char *num,
- sockaddr_u *netnum
+ sockaddr_u *net
)
{
- static const char * const servicename = "ntp";
- static const char * const serviceport = PORTSTR(NTP_PORT);
+ /* Building a parser is more fun in Haskell, but here we go...
+ *
+ * This works through 'inet_pton()' taking the brunt of the
+ * work, after some string manipulations to split off URI
+ * brackets, ports and scope identifiers. The heuristics are
+ * simple but must hold for all _VALID_ addresses. inet_pton()
+ * will croak on bad ones later, but replicating the whole
+ * parser logic to detect errors is wasteful.
+ */
- struct addrinfo hints, *ai = NULL;
- int err;
- const char *host_str;
- const char *port_str;
- char *pp;
- char *np;
- char nbuf[80];
-
- REQUIRE(num != NULL);
-
- if (strlen(num) >= sizeof(nbuf)) {
- printf("length error\n");
+ sockaddr_u netnum;
+ char buf[64]; /* working copy of input */
+ char *haddr=buf;
+ unsigned int port=NTP_PORT, scope=0;
+ unsigned short afam=AF_UNSPEC;
+
+ /* copy input to working buffer with length check */
+ if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf))
return FALSE;
- }
- port_str = servicename;
- if ('[' != num[0]) {
- /*
- * to distinguish IPv6 embedded colons from a port
- * specification on an IPv4 address, assume all
- * legal IPv6 addresses have at least two colons.
- */
- pp = strchr(num, ':');
- if (NULL == pp)
- host_str = num; /* no colons */
- else if (NULL != strchr(pp + 1, ':'))
- host_str = num; /* two or more colons */
- else { /* one colon */
- strlcpy(nbuf, num, sizeof(nbuf));
- host_str = nbuf;
- pp = strchr(nbuf, ':');
- *pp = '\0';
- port_str = pp + 1;
+ /* Identify address family and possibly the port, if given. If
+ * this results in AF_UNSPEC, we will fail in the next step.
+ */
+ if (*haddr == '[') {
+ char * endp = strchr(++haddr, ']');
+ if (endp) {
+ port = _num_or_dflt(_skip(_chop(endp), ':'),
+ 0xFFFFu, port);
+ afam = strchr(haddr, ':') ? AF_INET6 : AF_INET;
}
} else {
- host_str = np = nbuf;
- while (*++num && ']' != *num)
- *np++ = *num;
- *np = 0;
- if (']' == num[0] && ':' == num[1] && '\0' != num[2])
- port_str = &num[2];
+ char *col = strchr(haddr, ':');
+ char *dot = strchr(haddr, '.');
+ if (col == dot) {
+ /* no dot, no colon: bad! */
+ afam = AF_UNSPEC;
+ } else if (!col) {
+ /* no colon, only dot: IPv4! */
+ afam = AF_INET;
+ } else if (!dot || col < dot) {
+ /* no dot or 1st colon before 1st dot: IPv6! */
+ afam = AF_INET6;
+ } else {
+ /* 1st dot before 1st colon: must be IPv4 with port */
+ afam = AF_INET;
+ port = _num_or_dflt(_chop(col), 0xFFFFu, port);
+ }
}
- if ( ! *host_str)
+
+ /* Since we don't know about additional members in the address
+ * structures, we wipe the result buffer thoroughly:
+ */
+ memset(&netnum, 0, sizeof(netnum));
+
+ /* For AF_INET6, evaluate and remove any scope suffix. Have
+ * inet_pton() do the real work for AF_INET and AF_INET6, bail
+ * out otherwise:
+ */
+ switch (afam) {
+ case AF_INET:
+ if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0)
+ return FALSE;
+ netnum.sa4.sin_port = htons((unsigned short)port);
+ break;
+
+ case AF_INET6:
+ scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope);
+ if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0)
+ return FALSE;
+ netnum.sa6.sin6_port = htons((unsigned short)port);
+ netnum.sa6.sin6_scope_id = scope;
+ break;
+
+ case AF_UNSPEC:
+ default:
return FALSE;
- if ( ! *port_str)
- port_str = servicename;
-
- ZERO(hints);
- hints.ai_flags |= Z_AI_NUMERICHOST;
- if (isnumstr(port_str))
- hints.ai_flags |= Z_AI_NUMERICSERV;
- err = getaddrinfo(host_str, port_str, &hints, &ai);
- /* retry with default service name if the service lookup failed */
- if (err == EAI_SERVICE && strcmp(port_str, servicename)) {
- hints.ai_flags &= ~Z_AI_NUMERICSERV;
- port_str = servicename;
- err = getaddrinfo(host_str, port_str, &hints, &ai);
}
- /* retry another time with default service port if the service lookup failed */
- if (err == EAI_SERVICE && strcmp(port_str, serviceport)) {
- hints.ai_flags |= Z_AI_NUMERICSERV;
- port_str = serviceport;
- err = getaddrinfo(host_str, port_str, &hints, &ai);
- }
- if (err != 0)
- return FALSE;
-
- INSIST(ai->ai_addrlen <= sizeof(*netnum));
- ZERO(*netnum);
- memcpy(netnum, ai->ai_addr, ai->ai_addrlen);
- freeaddrinfo(ai);
+ /* Collect the remaining pieces and feed the output, which was
+ * not touched so far:
+ */
+ netnum.sa.sa_family = afam;
+ memcpy(net, &netnum, sizeof(netnum));
return TRUE;
}
diff --git a/libntp/recvbuff.c b/libntp/recvbuff.c
index 573fdb2f9209..5855ec2147ac 100644
--- a/libntp/recvbuff.c
+++ b/libntp/recvbuff.c
@@ -11,6 +11,15 @@
#include "recvbuff.h"
#include "iosignal.h"
+#if (RECV_INC & (RECV_INC-1))
+# error RECV_INC not a power of 2!
+#endif
+#if (RECV_BATCH & (RECV_BATCH - 1))
+#error RECV_BATCH not a power of 2!
+#endif
+#if (RECV_BATCH < RECV_INC)
+#error RECV_BATCH must be >= RECV_INC!
+#endif
/*
* Memory allocation
@@ -21,6 +30,8 @@ static u_long volatile total_recvbufs; /* total recvbufs currently in use */
static u_long volatile lowater_adds; /* number of times we have added memory */
static u_long volatile buffer_shortfall;/* number of missed free receive buffers
between replenishments */
+static u_long limit_recvbufs; /* maximum total of receive buffers */
+static u_long emerg_recvbufs; /* emergency/urgent buffers to keep */
static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
static recvbuf_t * free_recv_list;
@@ -33,11 +44,16 @@ static recvbuf_t * free_recv_list;
* short a time as possible
*/
static CRITICAL_SECTION RecvLock;
-# define LOCK() EnterCriticalSection(&RecvLock)
-# define UNLOCK() LeaveCriticalSection(&RecvLock)
+static CRITICAL_SECTION FreeLock;
+# define LOCK_R() EnterCriticalSection(&RecvLock)
+# define UNLOCK_R() LeaveCriticalSection(&RecvLock)
+# define LOCK_F() EnterCriticalSection(&FreeLock)
+# define UNLOCK_F() LeaveCriticalSection(&FreeLock)
#else
-# define LOCK() do {} while (FALSE)
-# define UNLOCK() do {} while (FALSE)
+# define LOCK_R() do {} while (FALSE)
+# define UNLOCK_R() do {} while (FALSE)
+# define LOCK_F() do {} while (FALSE)
+# define UNLOCK_F() do {} while (FALSE)
#endif
#ifdef DEBUG
@@ -76,33 +92,52 @@ initialise_buffer(recvbuf_t *buff)
}
static void
-create_buffers(int nbufs)
+create_buffers(
+ size_t nbufs)
{
+# ifndef DEBUG
+ static const u_int chunk = RECV_INC;
+# else
+ /* Allocate each buffer individually so they can be free()d
+ * during ntpd shutdown on DEBUG builds to keep them out of heap
+ * leak reports.
+ */
+ static const u_int chunk = 1;
+# endif
+
register recvbuf_t *bufp;
- int i, abuf;
+ u_int i;
+ size_t abuf;
+ if (limit_recvbufs <= total_recvbufs)
+ return;
+
abuf = nbufs + buffer_shortfall;
buffer_shortfall = 0;
-#ifndef DEBUG
- bufp = eallocarray(abuf, sizeof(*bufp));
-#endif
-
- for (i = 0; i < abuf; i++) {
-#ifdef DEBUG
- /*
- * Allocate each buffer individually so they can be
- * free()d during ntpd shutdown on DEBUG builds to
- * keep them out of heap leak reports.
- */
- bufp = emalloc_zero(sizeof(*bufp));
-#endif
- LINK_SLIST(free_recv_list, bufp, link);
- bufp++;
- free_recvbufs++;
- total_recvbufs++;
+ if (abuf < nbufs || abuf > RECV_BATCH)
+ abuf = RECV_BATCH; /* clamp on overflow */
+ else
+ abuf += (~abuf + 1) & (RECV_INC - 1); /* round up */
+
+ if (abuf > (limit_recvbufs - total_recvbufs))
+ abuf = limit_recvbufs - total_recvbufs;
+ abuf += (~abuf + 1) & (chunk - 1); /* round up */
+
+ while (abuf) {
+ bufp = calloc(chunk, sizeof(*bufp));
+ if (!bufp) {
+ limit_recvbufs = total_recvbufs;
+ break;
+ }
+ for (i = chunk; i; --i,++bufp) {
+ LINK_SLIST(free_recv_list, bufp, link);
+ }
+ free_recvbufs += chunk;
+ total_recvbufs += chunk;
+ abuf -= chunk;
}
- lowater_adds++;
+ ++lowater_adds;
}
void
@@ -115,15 +150,19 @@ init_recvbuff(int nbufs)
free_recvbufs = total_recvbufs = 0;
full_recvbufs = lowater_adds = 0;
+ limit_recvbufs = RECV_TOOMANY;
+ emerg_recvbufs = RECV_CLOCK;
+
create_buffers(nbufs);
-#if defined(SYS_WINNT)
+# if defined(SYS_WINNT)
InitializeCriticalSection(&RecvLock);
-#endif
+ InitializeCriticalSection(&FreeLock);
+# endif
-#ifdef DEBUG
+# ifdef DEBUG
atexit(&uninit_recvbuff);
-#endif
+# endif
}
@@ -146,6 +185,10 @@ uninit_recvbuff(void)
break;
free(rbunlinked);
}
+# if defined(SYS_WINNT)
+ DeleteCriticalSection(&FreeLock);
+ DeleteCriticalSection(&RecvLock);
+# endif
}
#endif /* DEBUG */
@@ -157,13 +200,14 @@ void
freerecvbuf(recvbuf_t *rb)
{
if (rb) {
- LOCK();
- rb->used--;
- if (rb->used != 0)
+ if (--rb->used != 0) {
msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
+ rb->used = 0;
+ }
+ LOCK_F();
LINK_SLIST(free_recv_list, rb, link);
- free_recvbufs++;
- UNLOCK();
+ ++free_recvbufs;
+ UNLOCK_F();
}
}
@@ -175,28 +219,34 @@ add_full_recv_buffer(recvbuf_t *rb)
msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
return;
}
- LOCK();
+ LOCK_R();
LINK_FIFO(full_recv_fifo, rb, link);
- full_recvbufs++;
- UNLOCK();
+ ++full_recvbufs;
+ UNLOCK_R();
}
recvbuf_t *
-get_free_recv_buffer(void)
+get_free_recv_buffer(
+ int /*BOOL*/ urgent
+ )
{
- recvbuf_t *buffer;
+ recvbuf_t *buffer = NULL;
- LOCK();
- UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
+ LOCK_F();
+ if (free_recvbufs > (urgent ? emerg_recvbufs : 0)) {
+ UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
+ }
+
if (buffer != NULL) {
- free_recvbufs--;
+ if (free_recvbufs)
+ --free_recvbufs;
initialise_buffer(buffer);
- buffer->used++;
+ ++buffer->used;
} else {
- buffer_shortfall++;
+ ++buffer_shortfall;
}
- UNLOCK();
+ UNLOCK_F();
return buffer;
}
@@ -204,17 +254,15 @@ get_free_recv_buffer(void)
#ifdef HAVE_IO_COMPLETION_PORT
recvbuf_t *
-get_free_recv_buffer_alloc(void)
+get_free_recv_buffer_alloc(
+ int /*BOOL*/ urgent
+ )
{
- recvbuf_t *buffer;
-
- buffer = get_free_recv_buffer();
- if (NULL == buffer) {
+ LOCK_F();
+ if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
create_buffers(RECV_INC);
- buffer = get_free_recv_buffer();
- }
- ENSURE(buffer != NULL);
- return (buffer);
+ UNLOCK_F();
+ return get_free_recv_buffer(urgent);
}
#endif
@@ -224,30 +272,26 @@ get_full_recv_buffer(void)
{
recvbuf_t * rbuf;
- LOCK();
-
/*
- * make sure there are free buffers when we
- * wander off to do lengthy packet processing with
- * any buffer we grab from the full list.
+ * make sure there are free buffers when we wander off to do
+ * lengthy packet processing with any buffer we grab from the
+ * full list.
*
- * fixes malloc() interrupted by SIGIO risk
- * (Bug 889)
+ * fixes malloc() interrupted by SIGIO risk (Bug 889)
*/
- if (NULL == free_recv_list || buffer_shortfall > 0) {
- /*
- * try to get us some more buffers
- */
+ LOCK_F();
+ if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
create_buffers(RECV_INC);
- }
+ UNLOCK_F();
/*
* try to grab a full buffer
*/
+ LOCK_R();
UNLINK_FIFO(rbuf, full_recv_fifo, link);
- if (rbuf != NULL)
- full_recvbufs--;
- UNLOCK();
+ if (rbuf != NULL && full_recvbufs)
+ --full_recvbufs;
+ UNLOCK_R();
return rbuf;
}
@@ -265,12 +309,18 @@ purge_recv_buffers_for_fd(
recvbuf_t *rbufp;
recvbuf_t *next;
recvbuf_t *punlinked;
+ recvbuf_t *freelist = NULL;
- LOCK();
+ /* We want to hold only one lock at a time. So we do a scan on
+ * the full buffer queue, collecting items as we go, and when
+ * done we spool the the collected items to 'freerecvbuf()'.
+ */
+ LOCK_R();
for (rbufp = HEAD_FIFO(full_recv_fifo);
rbufp != NULL;
- rbufp = next) {
+ rbufp = next)
+ {
next = rbufp->link;
# ifdef HAVE_IO_COMPLETION_PORT
if (rbufp->dstadr == NULL && rbufp->fd == fd)
@@ -281,12 +331,20 @@ purge_recv_buffers_for_fd(
UNLINK_MID_FIFO(punlinked, full_recv_fifo,
rbufp, link, recvbuf_t);
INSIST(punlinked == rbufp);
- full_recvbufs--;
- freerecvbuf(rbufp);
+ if (full_recvbufs)
+ --full_recvbufs;
+ rbufp->link = freelist;
+ freelist = rbufp;
}
}
- UNLOCK();
+ UNLOCK_R();
+
+ while (freelist) {
+ next = freelist->link;
+ freerecvbuf(freelist);
+ freelist = next;
+ }
}
diff --git a/libntp/strdup.c b/libntp/strdup.c
index 62d5a16d433c..8af9ff81b39c 100644
--- a/libntp/strdup.c
+++ b/libntp/strdup.c
@@ -1,13 +1,15 @@
#include <config.h>
#include <ntp_assert.h>
-#include "ntp_malloc.h"
#include <string.h>
+#include "ntp_malloc.h"
+#include "l_stdlib.h"
-#ifndef HAVE_STRDUP
+#define STRDUP_EMPTY_UNIT
+#ifndef HAVE_STRDUP
+# undef STRDUP_EMPTY_UNIT
char *strdup(const char *s);
-
char *
strdup(
const char *s
@@ -24,6 +26,30 @@ strdup(
return cp;
}
-#else
+#endif
+
+#ifndef HAVE_MEMCHR
+# undef STRDUP_EMPTY_UNIT
+void *memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n && *p != c) {
+ --n;
+ ++p;
+ }
+ return n ? (char*)p : NULL;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+# undef STRDUP_EMPTY_UNIT
+size_t strnlen(const char *s, size_t n)
+{
+ const char *e = memchr(s, 0, n);
+ return e ? (size_t)(e - s) : n;
+}
+#endif
+
+#ifdef STRDUP_EMPTY_UNIT
int strdup_c_nonempty_compilation_unit;
#endif
diff --git a/libntp/timexsup.c b/libntp/timexsup.c
index 498961f3b3c7..979a7c4aea8e 100644
--- a/libntp/timexsup.c
+++ b/libntp/timexsup.c
@@ -27,13 +27,13 @@ clamp_rounded(
dval = floor(dval + 0.5);
/* clamp / saturate */
- if (dval >= LONG_MAX)
+ if (dval >= (double)LONG_MAX)
return LONG_MAX;
- if (dval <= LONG_MIN)
+ if (dval <= (double)LONG_MIN)
return LONG_MIN;
return (long)dval;
-
}
+
double
dbl_from_var_long(
long lval,
@@ -80,4 +80,3 @@ usec_long_from_dbl(
{
return clamp_rounded(dval * 1e+6);
}
-