tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

recvmsg_realloc



Hi List

I use recvmsg(2) a fair bit in code.
However, a lot of the time I can't be certain how large a buffer I would
need, so they all ship with a handy function called recvmsg_realloc,
which as you can guess, will realloc the last iovec so that the whole
message fits. Think of it as a convenience function akin to getline(3).

OK, it's not just my code. For BSD, a fair few progs read from the
routing socket with a fixed 2048 char buffer.
While this should be plently, due to the use of read(2) it could
overflow if the system is sending more than the program can process.

Looking over the tree, the following progs should be re-coded to use
recvmsg rather than their current (and potentially buggy how I
understand it) read(2) implementation, and thus benefit from
recvmsg_realloc:
wpa_supplicant
route
ifwatchd
rtadvd
dhcpcd

I'd like to add this to libc instead of bolting it on for this and
possibly more applications. Or just recode the above with a fixed width
buffer and use recvmsg, keeping this out of libc.

Comments welcome, protoype below.

Roy

------------------------------------------------------------------------

#define IOVEC_BUFSIZ		256

ssize_t
recvmsg_realloc(int fd, struct msghdr *msg, int flags)
{
	struct iovec *iov;
	ssize_t slen;
	size_t len;
	void *n;

	assert(msg != NULL);
	assert(msg->msg_iov != NULL && msg->msg_iovlen > 0);
	assert((flags & (MSG_PEEK | MSG_TRUNC)) == 0);

	/* Assume we are reallocing the last iovec. */
	iov = &msg->msg_iov[msg->msg_iovlen - 1];

	for (;;) {
		/* Passing MSG_TRUNC should return the actual size
		 * needed. */
		slen = recvmsg(fd, msg, flags | MSG_PEEK | MSG_TRUNC);
		if (slen == -1)
			return -1;
		if (!(msg->msg_flags & MSG_TRUNC))
			break;

		len = (size_t)slen;

		/* Some kernels return the size of the receive buffer
		 * on truncation, not the actual size needed.
		 * So grow the buffer and try again. */
		if (iov->iov_len == len)
			len++;
		else if (iov->iov_len > len)
			break;
		len = roundup(len, IOVEC_BUFSIZ);
		if ((n = realloc(iov->iov_base, len)) == NULL)
			return -1;
		iov->iov_base = n;
		iov->iov_len = len;	}

	slen = recvmsg(fd, msg, flags);
	if (slen != -1 && msg->msg_flags & MSG_TRUNC) {
		/* This should not be possible ... */
		errno = ENOBUFS;
		return -1;
	}
	return slen;
}


Home | Main Index | Thread Index | Old Index