Index: ./lib/stream.h =================================================================== RCS file: /var/cvsroot/quagga/lib/stream.h,v retrieving revision 1.2 diff -u -r1.2 stream.h --- ./lib/stream.h 11 Jun 2004 11:27:03 -0000 1.2 +++ ./lib/stream.h 29 Nov 2004 14:44:20 -0000 @@ -32,65 +32,69 @@ unsigned char *data; - /* Put pointer. */ - unsigned long putp; - - /* Get pointer. */ - unsigned long getp; - - /* End of pointer. */ - unsigned long endp; - - /* Data size. */ - unsigned long size; + /* ***__Private data__*** + * Do not access these other than via the functions/macros below! + */ + size_t __getp; /* Next get position */ + size_t __endp; /* End of valid data position */ + size_t size; /* Size of data buffer */ }; /* First in first out queue structure. */ struct stream_fifo { - unsigned long count; + size_t count; struct stream *head; struct stream *tail; }; /* Utility macros. */ -#define STREAM_PNT(S) ((S)->data + (S)->getp) #define STREAM_SIZE(S) ((S)->size) -#define STREAM_REMAIN(S) ((S)->size - (S)->putp) -#define STREAM_DATA(S) ((S)->data) +#define STREAM_WRITEABLE(S) ((S)->size - (S)->__endp) +#define STREAM_READABLE(S) ((S)->__endp - (S)->__getp) +#define STREAM_IS_READABLE_TO(S, SIZE) \ + ( STREAM_READABLE(S) >= SIZE ) +#define STREAM_IS_WRITEABLE_TO(S,SIZE) \ + ( STREAM_WRITEABLE(S) >= (SIZE) ) +#define STREAM_IS_READABLE_TO_T(S, TYPE) \ + ( STREAM_IS_READABLE_TO ((S), sizeof (TYPE)) ) +#define STREAM_IS_WRITEABLE_TO_T(S,TYPE) \ + ( STREAM_IS_WRITEABLE_TO((S), sizeof (TYPE)) ) +/* backwards-compatible */ +#define STREAM_REMAIN(S) STREAM_WRITEABLE(S) /* Stream prototypes. */ struct stream *stream_new (size_t); void stream_free (struct stream *); +struct stream *stream_copy (struct stream *new, struct stream *src); +struct stream *stream_dup (struct stream *s); -unsigned long stream_get_getp (struct stream *); -unsigned long stream_get_putp (struct stream *); -unsigned long stream_get_endp (struct stream *); -unsigned long stream_get_size (struct stream *); -u_char *stream_get_data (struct stream *); - -void stream_set_getp (struct stream *, unsigned long); -void stream_set_putp (struct stream *, unsigned long); - -void stream_forward (struct stream *, int); +size_t stream_get_getp (struct stream *); +size_t stream_get_endp (struct stream *); +size_t stream_get_size (struct stream *); +u_char *stream_get_pnt (struct stream *, size_t); + +void stream_set_getp (struct stream *, size_t); +void stream_forward_getp (struct stream *, size_t); +void stream_forward_endp (struct stream *, size_t); void stream_put (struct stream *, void *, size_t); int stream_putc (struct stream *, u_char); -int stream_putc_at (struct stream *, unsigned long, u_char); +int stream_putc_at (struct stream *, size_t, u_char); int stream_putw (struct stream *, u_int16_t); -int stream_putw_at (struct stream *, unsigned long, u_int16_t); +int stream_putw_at (struct stream *, size_t, u_int16_t); int stream_putl (struct stream *, u_int32_t); -int stream_putl_at (struct stream *, unsigned long, u_int32_t); +int stream_putl_at (struct stream *, size_t, u_int32_t); int stream_put_ipv4 (struct stream *, u_int32_t); int stream_put_in_addr (struct stream *, struct in_addr *); int stream_put_prefix (struct stream *, struct prefix *); void stream_get (void *, struct stream *, size_t); u_char stream_getc (struct stream *); -u_char stream_getc_from (struct stream *, unsigned long); +u_char stream_getc_from (struct stream *, size_t); u_int16_t stream_getw (struct stream *); -u_int16_t stream_getw_from (struct stream *, unsigned long); +u_int16_t stream_getw_from (struct stream *, size_t); u_int32_t stream_getl (struct stream *); u_int32_t stream_get_ipv4 (struct stream *); @@ -98,9 +102,10 @@ #undef stream_write int stream_read (struct stream *, int, size_t); int stream_read_unblock (struct stream *, int, size_t); +int stream_recvmsg (struct stream *s, int fd, struct msghdr *, + int flags, size_t size); int stream_write (struct stream *, u_char *, size_t); -u_char *stream_pnt (struct stream *); void stream_reset (struct stream *); int stream_flush (struct stream *, int); int stream_empty (struct stream *); Index: ./lib/stream.c =================================================================== RCS file: /var/cvsroot/quagga/lib/stream.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 stream.c --- ./lib/stream.c 13 Dec 2002 20:15:29 -0000 1.1.1.1 +++ ./lib/stream.c 29 Nov 2004 14:44:20 -0000 @@ -21,22 +21,55 @@ */ #include +#include #include "stream.h" #include "memory.h" #include "network.h" #include "prefix.h" - +#include "log.h" /*A macro to check pointers in order to not go behind the allocated mem block S -- stream reference Z -- size of data to be written */ - #define CHECK_SIZE(S, Z) \ - if (((S)->putp + (Z)) > (S)->size) \ - (Z) = (S)->size - (S)->putp; + do { \ + if (((S)->__endp + (Z)) > (S)->size) \ + (Z) = (S)->size - (S)->__endp; \ + } while (0); + +#define GETP_VALID(S,G) \ + ((G) <= (S)->__endp) +#define ENDP_VALID(S,E) \ + ((E) <= (S)->size) + +/* asserting sanity checks. Following must be true before + * stream functions are called: + * + * Following must always be true of stream elements + * before and after calls to stream functions: + * + * getp <= endp <= size + * + * Note that after a stream function is called following may be true: + * getp == endp -> stream is no longer readable + * endp == size -> stream is no longer writeable + * + * It is valid to put to anywhere within the size of the stream, but only + * using stream_put..._at() functions. + */ +#define STREAM_IS_SANE(S) \ + do { \ + assert ( GETP_VALID(S, (S)->__getp) ); \ + assert ( ENDP_VALID(S, (S)->__endp) ); \ + } while (0) + +#define STR_END_STREAM " past end of stream" +#define STR_END_MID ": Attempt to " +#define STR_END_WARN2 "%s" STR_END_MID "%s %s" STR_END_STREAM +#define STR_END_WARN1 "%s" STR_END_MID "%s" STR_END_STREAM /* Stream is fixed length buffer for network output/input. */ @@ -45,10 +78,26 @@ stream_new (size_t size) { struct stream *s; - + + if (size == 0) + { + zlog_warn ("stream_new(): called with 0 size!"); + size =1 ; /* bounds checks will catch bad users */ + } + s = XCALLOC (MTYPE_STREAM, sizeof (struct stream)); - + + if (s == NULL) + return s; + s->data = XCALLOC (MTYPE_STREAM_DATA, size); + + if (s->data == NULL) + { + XFREE (MTYPE_STREAM, s); + return NULL; + } + s->size = size; return s; } @@ -60,77 +109,200 @@ XFREE (MTYPE_STREAM_DATA, s->data); XFREE (MTYPE_STREAM, s); } - -unsigned long -stream_get_getp (struct stream *s) + +struct stream * +stream_copy (struct stream *new, struct stream *src) { - return s->getp; + STREAM_IS_SANE (src); + + assert (new != NULL); + assert (STREAM_SIZE(new) >= stream_get_endp (src)); + + new->__endp = src->__endp; + new->__getp = src->__getp; + + memcpy (new->data, src->data, stream_get_endp (src)); + + return new; } -unsigned long -stream_get_putp (struct stream *s) +struct stream * +stream_dup (struct stream *s) { - return s->putp; + struct stream *new; + + if ( (new = stream_new (stream_get_endp(s))) == NULL) + return NULL; + + return (stream_copy (new, s)); +} + +size_t +stream_get_getp (struct stream *s) +{ + STREAM_IS_SANE(s); + return s->__getp; } -unsigned long +size_t stream_get_endp (struct stream *s) { - return s->endp; + STREAM_IS_SANE(s); + return s->__endp; } -unsigned long +size_t stream_get_size (struct stream *s) { + STREAM_IS_SANE(s); return s->size; } /* Stream structre' stream pointer related functions. */ void -stream_set_getp (struct stream *s, unsigned long pos) +stream_set_getp (struct stream *s, size_t pos) { - s->getp = pos; -} + STREAM_IS_SANE(s); -void -stream_set_putp (struct stream *s, unsigned long pos) -{ - s->putp = pos; + if (!GETP_VALID (s, pos)) + { + zlog_warn (STR_END_WARN2, "stream_set_getp", "set", "getp"); + pos = s->__endp; + } + + s->__getp = pos; } -/* Forward pointer. */ +/* Forward getp pointer. */ void -stream_forward (struct stream *s, int size) +stream_forward_getp (struct stream *s, size_t size) { - s->getp += size; + STREAM_IS_SANE(s); + + if (!GETP_VALID (s, s->__getp + size)) + { + zlog_warn (STR_END_WARN2, "stream_forward_getp", "seek", "getp"); + return; + } + + s->__getp += size; } /* Copy from stream to destination. */ void stream_get (void *dst, struct stream *s, size_t size) { - memcpy (dst, s->data + s->getp, size); - s->getp += size; + STREAM_IS_SANE(s); + + if (!STREAM_IS_READABLE_TO(s, size)) + { + zlog_warn (STR_END_WARN1, "stream_get", "get"); + return; + } + + memcpy (dst, s->data + s->__getp, size); + s->__getp += size; +} + +/* Copy a word of size bytes from src to dst, swapping byte order + * For internal use by exported, strongly typed, functions only + * Caller is responsible for ensuring destination is sufficiently aligned. + */ +static void * +stream_copy_byte_swapped (void *dst, void *src, size_t size) +{ + u_char *psrc = src; + int i = 0; + + switch (size) + { + case (sizeof (u_int32_t)): + { + u_int32_t *pdst = dst; + *pdst = psrc[i++] << 24; + *pdst |= psrc[i++] << 16; + *pdst |= psrc[i++] << 8; + *pdst |= psrc[i++]; + break; + } + case (sizeof (u_int16_t)): + { + u_int16_t *pdst = dst; + *pdst = psrc[i++] << 8; + *pdst |= psrc[i++]; + break; + } + case (sizeof(char)): + { + u_char *pdst = dst; + u_char *psrc = src; + *pdst = *psrc; + break; + } + default: + zlog_err ("stream_copy_byte_swapped: called with non-integral size" + " %ld! should never happen!", size); + assert (0); + return NULL; + } + return dst; +} + +/* internal helpers to get size bytes, corresponding to an integral + * type from the stream. Must only be called by API visible functions + * which get a definite type (ie char, int16_t, int32_t, int64_t)! + * returns number of bytes gotten. + */ +static int +stream_get_typed_size_from (struct stream *s, void *dst, size_t from, int size) +{ + void *ret = NULL; + + STREAM_IS_SANE(s); + + if (!GETP_VALID (s, from + size)) + { + zlog_warn (STR_END_WARN1, "stream_get_typed_size_at", "get"); + return 0; + } + + ret = stream_copy_byte_swapped (dst, &(s->data[from]), size); + + if (ret == dst) + return size; + else + return 0; } +static int +stream_get_typed_size (struct stream *s, void *dst, int size) +{ + int bytes; + + bytes = stream_get_typed_size_from (s, dst, s->__getp, size); + + s->__getp += bytes; + + return bytes; +} + /* Get next character from the stream. */ u_char stream_getc (struct stream *s) { u_char c; - c = s->data[s->getp]; - s->getp++; + stream_get_typed_size (s, &c, sizeof (u_char)); return c; } /* Get next character from the stream. */ u_char -stream_getc_from (struct stream *s, unsigned long from) +stream_getc_from (struct stream *s, size_t from) { u_char c; - c = s->data[from]; + stream_get_typed_size_from (s, &c, from, sizeof (u_char)); return c; } @@ -139,33 +311,27 @@ stream_getw (struct stream *s) { u_int16_t w; - - w = s->data[s->getp++] << 8; - w |= s->data[s->getp++]; + + stream_get_typed_size (s, &w, sizeof (u_int16_t)); return w; } /* Get next word from the stream. */ u_int16_t -stream_getw_from (struct stream *s, unsigned long from) +stream_getw_from (struct stream *s, size_t from) { u_int16_t w; - w = s->data[from++] << 8; - w |= s->data[from]; + stream_get_typed_size_from (s, &w, from, sizeof (u_int16_t)); return w; } - + /* Get next long word from the stream. */ u_int32_t stream_getl (struct stream *s) { u_int32_t l; - - l = s->data[s->getp++] << 24; - l |= s->data[s->getp++] << 16; - l |= s->data[s->getp++] << 8; - l |= s->data[s->getp++]; + stream_get_typed_size (s, &l, sizeof (u_int32_t)); return l; } @@ -175,209 +341,254 @@ { u_int32_t l; - memcpy (&l, s->data + s->getp, 4); - s->getp += 4; - + STREAM_IS_SANE(s); + + if (!STREAM_IS_READABLE_TO_T (s, u_int32_t)) + { + zlog_warn (STR_END_WARN1, "stream_get_ipv4", "get"); + return 0; + } + + memcpy (&l, s->data + s->__getp, sizeof (u_int32_t)); + s->__getp += sizeof (u_int32_t); + return l; } -/* Copy to source to stream. */ -void -stream_put (struct stream *s, void *src, size_t size) +/* Ideally this would be stream_put, however stream_put uses + * CHECK_SIZE, which has funny semantics. The intention is to remove + * CHECK_SIZE and make this be stream_put() once stream-cleanup work + * is integrated and tested - to be able to differentiate bugs due to former + * from any regressions due to users who rely on the strange CHECK_SIZE + * behaviour + */ +static int +stream_put_memcpy (struct stream *s, void *src, size_t size) { + STREAM_IS_SANE(s); + if (!STREAM_IS_WRITEABLE_TO (s, size)) + { + zlog_warn (STR_END_WARN1, "stream_put_memcpy", "end"); + return 0; + } + + memcpy (s->data + s->__endp, src, size); + + s->__endp += size; + + return size; +} + +/* Copy to source to stream. Deprecated - should die */ +void +stream_put (struct stream *s, void *src, size_t size) +{ CHECK_SIZE(s, size); - if (src) - memcpy (s->data + s->putp, src, size); - else - memset (s->data + s->putp, 0, size); + stream_put_memcpy (s, src, size); + + return; +} + +/* Put integal type size (eg char, int16, int32) bytes to stream + * internal use only, must only ever be called by strongly typed functions + */ +static int +stream_put_typed_size_at (struct stream *s, void *src, size_t putp, int size) +{ + void *ret; + + STREAM_IS_SANE(s); + + if (!ENDP_VALID (s, putp + size)) + { + zlog_warn (STR_END_WARN1, "stream_put_typed_size_at", "endp"); + return 0; + } - s->putp += size; - if (s->putp > s->endp) - s->endp = s->putp; + ret = stream_copy_byte_swapped (&(s->data[putp]), src, size); + + if (ret != &(s->data[putp])) + return 0; + + if ((putp + size) > s->__endp) + s->__endp = putp + size; + + return size; } /* Put character to the stream. */ int stream_putc (struct stream *s, u_char c) { - if (s->putp >= s->size) return 0; - - s->data[s->putp] = c; - s->putp++; - if (s->putp > s->endp) - s->endp = s->putp; - return 1; + return stream_put_typed_size_at (s, &c, s->__endp, sizeof (u_char)); } /* Put word to the stream. */ int stream_putw (struct stream *s, u_int16_t w) { - if ((s->size - s->putp) < 2) return 0; - - s->data[s->putp++] = (u_char)(w >> 8); - s->data[s->putp++] = (u_char) w; - - if (s->putp > s->endp) - s->endp = s->putp; - return 2; + return stream_put_typed_size_at (s, &w, s->__endp, sizeof (u_int16_t)); } /* Put long word to the stream. */ int stream_putl (struct stream *s, u_int32_t l) { - if ((s->size - s->putp) < 4) return 0; - - s->data[s->putp++] = (u_char)(l >> 24); - s->data[s->putp++] = (u_char)(l >> 16); - s->data[s->putp++] = (u_char)(l >> 8); - s->data[s->putp++] = (u_char)l; - - if (s->putp > s->endp) - s->endp = s->putp; - return 4; + return stream_put_typed_size_at (s, &l, s->__endp, sizeof (u_int32_t)); } int -stream_putc_at (struct stream *s, unsigned long putp, u_char c) +stream_putc_at (struct stream *s, size_t putp, u_char c) { - s->data[putp] = c; - return 1; + return stream_put_typed_size_at (s, &c, putp, sizeof (u_char)); } int -stream_putw_at (struct stream *s, unsigned long putp, u_int16_t w) +stream_putw_at (struct stream *s, size_t putp, u_int16_t w) { - s->data[putp] = (u_char)(w >> 8); - s->data[putp + 1] = (u_char) w; - return 2; + return stream_put_typed_size_at (s, &w, putp, sizeof (u_int16_t)); } int -stream_putl_at (struct stream *s, unsigned long putp, u_int32_t l) +stream_putl_at (struct stream *s, size_t putp, u_int32_t l) { - s->data[putp] = (u_char)(l >> 24); - s->data[putp + 1] = (u_char)(l >> 16); - s->data[putp + 2] = (u_char)(l >> 8); - s->data[putp + 3] = (u_char)l; - return 4; + return stream_put_typed_size_at (s, &l, putp, sizeof (u_int32_t)); } /* Put long word to the stream. */ int stream_put_ipv4 (struct stream *s, u_int32_t l) { - if ((s->size - s->putp) < 4) - return 0; - - memcpy (s->data + s->putp, &l, 4); - s->putp += 4; - - if (s->putp > s->endp) - s->endp = s->putp; - return 4; + return stream_put_memcpy (s, &l, sizeof (u_int32_t)); } /* Put long word to the stream. */ int stream_put_in_addr (struct stream *s, struct in_addr *addr) { - if ((s->size - s->putp) < 4) - return 0; - - memcpy (s->data + s->putp, addr, 4); - s->putp += 4; - - if (s->putp > s->endp) - s->endp = s->putp; - return 4; + return stream_put_memcpy (s, addr, sizeof (struct in_addr)); } /* Put prefix by nlri type format. */ int stream_put_prefix (struct stream *s, struct prefix *p) { - u_char psize; + size_t psize; + + STREAM_IS_SANE(s); psize = PSIZE (p->prefixlen); - if ((s->size - s->putp) < psize) return 0; + if (!STREAM_IS_WRITEABLE_TO (s, psize + 1)) + return 0; stream_putc (s, p->prefixlen); - memcpy (s->data + s->putp, &p->u.prefix, psize); - s->putp += psize; + memcpy (s->data + s->__endp, &p->u.prefix, psize); + s->__endp += psize; - if (s->putp > s->endp) - s->endp = s->putp; - return psize; } -/* Read size from fd. */ +/* Read size from fd into stream. */ int stream_read (struct stream *s, int fd, size_t size) { int nbytes; + + STREAM_IS_SANE(s); - nbytes = readn (fd, s->data + s->putp, size); + if (!STREAM_IS_WRITEABLE_TO (s, size)) + return 0; + + nbytes = readn (fd, s->data + s->__endp, size); if (nbytes > 0) - { - s->putp += nbytes; - s->endp += nbytes; - } + s->__endp += nbytes; + return nbytes; } -/* Read size from fd. */ +/* Read size from fd to stream. */ int stream_read_unblock (struct stream *s, int fd, size_t size) { int nbytes; int val; + + STREAM_IS_SANE(s); + if (!STREAM_IS_WRITEABLE_TO (s, size)) + return 0; + val = fcntl (fd, F_GETFL, 0); fcntl (fd, F_SETFL, val|O_NONBLOCK); - nbytes = read (fd, s->data + s->putp, size); + nbytes = read (fd, s->data + s->__endp, size); fcntl (fd, F_SETFL, val); if (nbytes > 0) - { - s->putp += nbytes; - s->endp += nbytes; - } + s->__endp += nbytes; + return nbytes; } -/* Write data to buffer. */ +/* Read up to smaller of size or SIZE_REMAIN() bytes into the stream buffer, + * at its putp. + * First iovec will be used to receive the data. + * Stream need not be empty. + */ int -stream_write (struct stream *s, u_char *ptr, size_t size) +stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags, + size_t size) { + int nbytes; + struct iovec *iov; + + STREAM_IS_SANE(s); + assert (msgh->msg_iovlen > 0); - CHECK_SIZE(s, size); + if (!STREAM_IS_WRITEABLE_TO (s, size)) + return 0; + + iov = &(msgh->msg_iov[0]); - memcpy (s->data + s->putp, ptr, size); - s->putp += size; - if (s->putp > s->endp) - s->endp = s->putp; - return size; + iov->iov_base = (s->data + s->__endp); + iov->iov_len = size; + + nbytes = recvmsg (fd, msgh, flags); + + if (nbytes > 0) + s->__endp += nbytes; + + return nbytes; } -/* Return current read pointer. */ +/* sadly, some users want to access stream data directly, bgpd particularly */ u_char * -stream_pnt (struct stream *s) +stream_get_pnt (struct stream *s, size_t size) { - return s->data + s->getp; + STREAM_IS_SANE (s); + + if (!GETP_VALID (s, s->__getp + size)) + return NULL; + + return (s->data + s->__getp); +} + +/* Write data to buffer. */ +int +stream_write (struct stream *s, u_char *ptr, size_t size) +{ + CHECK_SIZE(s, size); + return stream_put_memcpy (s, ptr, size); } /* Check does this stream empty? */ int stream_empty (struct stream *s) { - if (s->putp == 0 && s->endp == 0 && s->getp == 0) + if (s->__endp == 0) return 1; else return 0; @@ -387,18 +598,18 @@ void stream_reset (struct stream *s) { - s->putp = 0; - s->endp = 0; - s->getp = 0; + s->__endp = 0; + s->__getp = 0; } /* Write stream contens to the file discriptor. */ int stream_flush (struct stream *s, int fd) { + STREAM_IS_SANE(s); int nbytes; - nbytes = write (fd, s->data + s->getp, s->endp - s->getp); + nbytes = write (fd, s->data + s->__getp, s->__endp - s->__getp); return nbytes; } @@ -418,6 +629,8 @@ void stream_fifo_push (struct stream_fifo *fifo, struct stream *s) { + STREAM_IS_SANE(s); + if (fifo->tail) fifo->tail->next = s; else