/*
* This file is part of Cockpit.
*
* Copyright (C) 2014 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see .
*/
#include "config.h"
#include "cockpitframe.h"
#include
#include
#include
#include
#include
#include
#define MAX_FRAME_SIZE_BYTES 7
/**
* cockpit_frame_parse:
* @input: An buffer of bytes
* @length: The length of @input buffer
* @consumed: Number of bytes consumed from @input
*
* Parse message framing length string from the top
* of the @input buffer. These are used by Cockpit transport framing
* over a stream based protocol.
*
* Returns: The length, zero if more data is needed, or -1 if an error.
*/
ssize_t
cockpit_frame_parse (unsigned char *input,
size_t length,
size_t *consumed)
{
size_t size = 0;
size_t i;
assert (input != NULL || length == 0);
size = 0;
for (i = 0; i < length; i++)
{
/* Check invalid characters, prevent integer overflow, limit max length */
if (i > MAX_FRAME_SIZE_BYTES || (char)(input[i]) < '0' || (char)(input[i]) > '9')
break;
size *= 10;
size += (char)(input[i]) - '0';
}
/* Want more data */
if (i == length)
return 0;
/* A failure */
if (size == 0 || input[i] != '\n')
return -1;
if (consumed)
*consumed = i + 1;
return size;
}
ssize_t
cockpit_fd_write_all (int fd,
unsigned char *data,
size_t length)
{
ssize_t written = 0;
ssize_t res;
assert (data != NULL || length == 0);
while (length > 0)
{
res = write (fd, data, length);
if (res < 0)
{
if (errno != EAGAIN && errno != EINTR)
return -1;
}
else
{
data += res;
length -= res;
written += res;
}
}
return written;
}
ssize_t
cockpit_frame_write (int fd,
unsigned char *input,
size_t length)
{
char *prefix = NULL;
ssize_t ret = -1;
int errn = 0;
assert (length > 0);
assert (input != NULL);
if (asprintf (&prefix, "%u\n", (unsigned int)length) < 0)
{
errn = ENOMEM;
goto out;
}
ret = cockpit_fd_write_all (fd, (unsigned char *)prefix, strlen (prefix));
if (ret > 0)
ret = cockpit_fd_write_all (fd, input, length);
if (ret < 0)
errn = errno;
out:
free (prefix);
if (ret < 0)
errno = errn;
return ret;
}
static void *
xrealloc (void *old,
size_t length)
{
void *data = realloc (old, length);
if (!data && length > 0)
free (old);
return data;
}
ssize_t
cockpit_frame_read (int fd,
unsigned char **output)
{
ssize_t size = 0;
size_t skip;
ssize_t res;
int errn = 0;
ssize_t ret = -1;
unsigned char *buf = NULL;
size_t buflen = 0;
size_t allocated = 0;
while (size == 0 || buflen < size)
{
/* Reallocate */
if (buflen + 1 > allocated)
{
allocated = size;
if (allocated < buflen + 128)
allocated = buflen + 128;
buf = xrealloc (buf, allocated);
if (!buf)
{
errn = ENOMEM;
goto out;
}
}
res = read (fd, buf + buflen, 1);
if (res < 0 && errno == ECONNRESET && buflen == 0)
res = 0;
if (res < 0)
{
/* A read failure */
if (errno != EINTR && errno != EAGAIN)
{
errn = errno;
goto out;
}
}
else if (res == 0)
{
/* No message parsed, but also no data received */
if (size == 0 && buflen == 0)
ret = 0;
else
errn = EBADMSG;
goto out;
}
else if (res > 0)
{
buflen += 1;
}
/* Parse the length if necessary */
if (size == 0)
{
assert (buf != NULL);
size = cockpit_frame_parse (buf, buflen, &skip);
if (size > 0)
{
assert (buflen >= skip);
if (buflen > skip)
memmove (buf, buf + skip, buflen - skip);
buflen -= skip;
}
}
if (size < 0)
{
errn = EBADMSG;
goto out;
}
}
if (output)
{
*output = buf;
buf = NULL;
}
ret = size;
out:
free (buf);
if (ret < 0)
errno = errn;
return ret;
}