#include "signal.h"
#include "fd.h"
#include "mood.h"
+#include "sideband.h"
#include "command.h"
/** The osl tables used by afs. \sa blob.c. */
CC: Array of commands for the audio file selector.
AT: server_command
SI: osl regex
-IN: para error crypt command string afh afs server list user_list
+IN: para error crypt sideband command string afh afs server list user_list
SN: list of afs commands
TM: mood lyr img pl
---
#include "fd.h"
#include "ipc.h"
#include "portable_io.h"
+#include "sideband.h"
#include "command.h"
static struct osl_table *audio_file_table;
#include "afh.h"
#include "afs.h"
#include "ipc.h"
+#include "sideband.h"
#include "command.h"
static struct osl_table *attribute_table;
#include "afs.h"
#include "ipc.h"
#include "portable_io.h"
+#include "sideband.h"
#include "command.h"
/**
#include "crypt.h"
#include "net.h"
#include "fd.h"
+#include "sideband.h"
#include "string.h"
#include "client.cmdline.h"
#include "client.h"
return 0;
}
+static int send_sb(struct client_task *ct, void *buf, size_t numbytes,
+ enum sb_designator band, bool dont_free)
+{
+ int ret, fd = ct->scc.fd;
+ struct iovec iov[2];
+
+ if (!ct->sbc) {
+ struct sb_buffer sbb;
+ sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED?
+ NULL : sc_trafo;
+ sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes);
+ ct->sbc = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
+ }
+ ret = sb_get_send_buffers(ct->sbc, iov);
+ ret = xwritev(fd, iov, ret);
+ if (ret < 0) {
+ sb_free(ct->sbc);
+ ct->sbc = NULL;
+ return ret;
+ }
+ if (sb_sent(ct->sbc, ret)) {
+ ct->sbc = NULL;
+ return 1;
+ }
+ return 0;
+}
+
+static int recv_sb(struct client_task *ct, fd_set *rfds,
+ struct sb_buffer *result)
+{
+ int ret;
+ size_t n;
+ sb_transformation trafo;
+ void *trafo_context;
+ struct iovec iov;
+
+ if (!FD_ISSET(ct->scc.fd, rfds))
+ return 0;
+ if (ct->status < CL_SENT_CH_RESPONSE)
+ trafo = trafo_context = NULL;
+ else {
+ trafo = sc_trafo;
+ trafo_context = ct->scc.recv;
+ }
+ if (!ct->sbc)
+ ct->sbc = sb_new_recv(0, trafo, trafo_context);
+again:
+ sb_get_recv_buffer(ct->sbc, &iov);
+ ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
+ if (ret < 0) {
+ sb_free(ct->sbc);
+ ct->sbc = NULL;
+ return ret;
+ }
+ if (n == 0)
+ return 0;
+ if (!sb_received(ct->sbc, n, result))
+ goto again;
+ ct->sbc = NULL;
+ return 1;
+}
+
+
static char **parse_features(char *buf)
{
int i;
#include "para.h"
#include "error.h"
#include "crypt.h"
+#include "sideband.h"
#include "command.h"
#include "server.cmdline.h"
#include "string.h"
return 1;
}
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param buf The buffer to send.
+ * \param numbytes The size of \a buf.
+ * \param band The sideband designator of this packet.
+ * \param dont_free If true, never deallocate \a buf.
+ *
+ * The nonblock flag must be disabled for the file descriptor given by \a scc.
+ *
+ * Stream cipher encryption is automatically activated if neccessary via the
+ * sideband transformation, depending on the value of \a band.
+ *
+ * \return Standard.
+ *
+ * \sa \ref send_sb_va().
+ */
+int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
+ int band, bool dont_free)
+{
+ int ret;
+ struct sb_context *sbc;
+ struct iovec iov[2];
+ struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
+ sb_transformation trafo = band < SBD_PROCEED? NULL : sc_trafo;
+
+ sbc = sb_new_send(&sbb, dont_free, trafo, scc->send);
+ do {
+ ret = sb_get_send_buffers(sbc, iov);
+ ret = xwritev(scc->fd, iov, ret);
+ if (ret < 0)
+ goto fail;
+ } while (sb_sent(sbc, ret) == false);
+ return 1;
+fail:
+ sb_free(sbc);
+ return ret;
+}
+
+/**
+ * Create a variable sized buffer and send it as a sideband packet.
+ *
+ * \param scc Passed to \ref send_sb.
+ * \param band See \ref send_sb.
+ * \param fmt The format string.
+ *
+ * \return The return value of the underlying call to \ref send_sb.
+ */
+__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+ return send_sb(scc, msg, ret, band, false);
+}
+
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param expected_band The expected band designator.
+ * \param max_size Passed to \ref sb_new_recv().
+ * \param result Body of the sideband packet is returned here.
+ *
+ * If \a expected_band is not \p SBD_ANY, the band designator of the received
+ * sideband packet is compared to \a expected_band and a mismatch is considered
+ * an error.
+ *
+ * \return Standard.
+ */
+int recv_sb(struct stream_cipher_context *scc,
+ enum sb_designator expected_band,
+ size_t max_size, struct iovec *result)
+{
+ int ret;
+ struct sb_context *sbc;
+ struct iovec iov;
+ struct sb_buffer sbb;
+ sb_transformation trafo;
+
+ trafo = expected_band != SBD_ANY && expected_band < SBD_PROCEED?
+ NULL : sc_trafo;
+ sbc = sb_new_recv(max_size, trafo, scc->recv);
+ for (;;) {
+ sb_get_recv_buffer(sbc, &iov);
+ ret = recv_bin_buffer(scc->fd, iov.iov_base, iov.iov_len);
+ if (ret == 0)
+ ret = -E_EOF;
+ if (ret < 0)
+ goto fail;
+ ret = sb_received(sbc, ret, &sbb);
+ if (ret < 0)
+ goto fail;
+ if (ret > 0)
+ break;
+ }
+ ret = -E_BAD_BAND;
+ if (expected_band != SBD_ANY && sbb.band != expected_band)
+ goto fail;
+ *result = sbb.iov;
+ return 1;
+fail:
+ sb_free(sbc);
+ return ret;
+}
+
int com_sender(struct command_context *cc)
{
int i, ret;
/** The long help text. */
const char *help;
};
+
+int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
+ int band, bool dont_free);
+__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
+ const char *fmt, ...);
+int recv_sb(struct stream_cipher_context *scc,
+ enum sb_designator expected_band,
+ size_t max_size, struct iovec *result);
exec send_common ggo udp_recv color fec fecdec_filter
prebuffer_filter audiod_command_list bitstream imdct
wma_afh wma_common wmadec_filter buffer_tree crypt_common
- gui gui_theme"
+ gui gui_theme sideband"
executables="recv filter audioc write client afh audiod"
audioc_ldflags=""
audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
-audiod_errlist_objs="audiod signal string daemon stat net crypt_common
+audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband
time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
recv_common fd sched write_common file_write audiod_command fecdec_filter
client_common ggo udp_recv color fec prebuffer_filter audiod_command_list
default_writer="FILE_WRITE"
client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="client net string fd sched stdin stdout time
+client_errlist_objs="client net string fd sched stdin stdout time sideband
client_common buffer_tree crypt_common"
client_ldflags=""
crypt_common ipc dccp_send fd user_list chunk_queue
afs aft mood score attribute blob playlist sched acl
send_common udp_send color fec server_command_list
- afs_command_list wma_afh wma_common"
+ afs_command_list wma_afh wma_common sideband"
all_errlist_objs="$all_errlist_objs server vss command
http_send close_on_fork mm ipc dccp_send user_list
return ret;
}
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+ RC4_KEY *key = &sc->key;
+
+ *dst = (typeof(*dst)) {
+ /*
+ * Add one for the terminating zero byte. Integer overflow is
+ * no problem here as para_malloc() aborts when given a zero
+ * size argument.
+ */
+ .iov_base = para_malloc(src->iov_len + 1),
+ .iov_len = src->iov_len
+ };
+ RC4(key, src->iov_len, src->iov_base, dst->iov_base);
+ ((char *)dst->iov_base)[dst->iov_len] = '\0';
+}
+
void hash_function(const char *data, unsigned long len, unsigned char *hash)
{
SHA_CTX c;
*/
struct stream_cipher *sc_new(const unsigned char *data, int len);
+/**
+ * Encrypt or decrypt a buffer using a stream cipher.
+ *
+ * \param sc Crypto key.
+ * \param src The source buffer and length.
+ * \param dst The destination buffer and length, filled out by the function.
+ *
+ * It is up to the implementation to decide whether the crypt operation is
+ * performed in place. The caller can tell by looking if the buffers given by
+ * \a src and \a dst coincide after the call. If (and only if) the crypt
+ * operation was not performed in place, the function allocated a new buffer
+ * for the result, so dst->iov_base is different from src->iov_base. In this
+ * case, the destination buffer must be freed by the caller when it is no
+ * longer needed.
+ */
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst);
+
+/**
+ * Wrapper for \ref sc_crypt() that can be used as a sideband transformation.
+ *
+ * \param src Passed verbatim to \ref sc_crypt().
+ * \param dst Passed verbatim to \ref sc_crypt().
+ * \param trafo_context Must point to an initialized stream cipher.
+ */
+_static_inline_ void sc_trafo(struct iovec *src, struct iovec *dst,
+ void *trafo_context)
+{
+ sc_crypt(trafo_context, src, dst);
+}
+
/**
* Deallocate a stream cipher structure.
*
*/
int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size);
-
/** Size of the hash value in bytes. */
#define HASH_SIZE 20
#include "para.h"
#include "error.h"
#include "string.h"
-#include "crypt_backend.h"
#include "crypt.h"
+#include "crypt_backend.h"
/** If the key begins with this text, we treat it as an ssh key. */
#define KEY_TYPE_TXT "ssh-rsa"
extern const char **para_errlist[];
+#define SIDEBAND_ERRORS \
+ PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
+ PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
+
+
#define FLACDEC_FILTER_ERRORS \
PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
* As direct use of __printf(p,q) confuses doxygen, here are some extra macros
* for those values p,q that are actually used.
*/
-#define __printf_2_0 __printf(2,0)
-#define __printf_1_2 __printf(1,2)
-#define __printf_2_3 __printf(2,3)
+#define __printf_2_0 __printf(2,0)
+#define __printf_1_2 __printf(1,2)
+#define __printf_2_3 __printf(2,3)
+#define __printf_3_4 __printf(3,4)
# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
# define __must_check __attribute__ ((warn_unused_result))
assert(gret == 0);
return ret;
}
+
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+ gcry_cipher_hd_t handle = sc->handle;
+ gcry_error_t gret;
+
+ /* perform in-place encryption */
+ *dst = *src;
+ gret = gcry_cipher_encrypt(handle, src->iov_base, src->iov_len,
+ NULL, 0);
+ assert(gret == 0);
+}
return ((max + 0.0) * (random() / (RAND_MAX + 1.0)));
}
+/**
+ * Simple sanity check for I/O vectors.
+ *
+ * \param iov Pointer to the I/O vector to check.
+ *
+ * \return True if \a iov points to a non-empty buffer.
+ */
+_static_inline_ bool iov_valid(const struct iovec *iov)
+{
+ return iov && iov->iov_len > 0 && iov->iov_base;
+}
+
/** Round up x to next multiple of y. */
#define ROUND_UP(x, y) ({ \
const typeof(y) _divisor = y; \
CC: array of server commands
AT: server_command
SI: osl regex
-IN: para error crypt command string afh afs server list user_list
+IN: para error crypt sideband command string afh afs server list user_list
SN: list of server commands
---
N: ff
--- /dev/null
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.c Implementation of the sideband API. */
+
+#include <regex.h>
+#include <sys/uio.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "sideband.h"
+
+/** Each sideband packet consists of a header and a data part. */
+#define SIDEBAND_HEADER_SIZE 5
+
+struct sb_context {
+ char header[SIDEBAND_HEADER_SIZE];
+ size_t bytes_dispatched; /* including header */
+ sb_transformation trafo;
+ void *trafo_context;
+ struct sb_buffer sbb;
+ size_t max_size;
+ bool dont_free;
+};
+
+/**
+ * Prepare to receive a sideband packet.
+ *
+ * \param max_size Do not allocate more than this many bytes.
+ * \param t Optional sideband transformation.
+ * \param trafo_context Passed verbatim to \a t.
+ *
+ * \a trafo_context is ignored if \a t is \p NULL.
+ *
+ * \return An opaque sideband handle.
+ */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+ void *trafo_context)
+{
+ struct sb_context *c = para_calloc(sizeof(*c));
+
+ c->max_size = max_size;
+ c->trafo = t;
+ c->trafo_context = trafo_context;
+ return c;
+}
+
+/**
+ * Prepare to write a sideband packet.
+ *
+ * \param sbb Data and meta data to send.
+ * \param dont_free Do not try to deallocate the sideband buffer.
+ * \param t See \ref sb_new_recv().
+ * \param trafo_context See \ref sb_new_recv().
+ *
+ * It's OK to supply a zero-sized buffer in \a sbb. In this case only the band
+ * designator is sent through the sideband channel. Otherwise, if \a dont_free
+ * is false, the buffer of \a sbb is freed after the data has been sent.
+ *
+ * \return See \ref sb_new_recv().
+ */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+ sb_transformation t, void *trafo_context)
+{
+ struct sb_context *c = para_calloc(sizeof(*c));
+ struct iovec src, dst, *srcp, *dstp;
+
+ assert(sbb);
+ c->trafo = t;
+ c->trafo_context = trafo_context;
+ c->dont_free = dont_free;
+ c->sbb = *sbb;
+ write_u32(c->header, sbb->iov.iov_len);
+ write_u8(c->header + 4, sbb->band);
+ if (!t)
+ goto out;
+ src = (typeof(src)){.iov_base = c->header, .iov_len = SIDEBAND_HEADER_SIZE};
+ t(&src, &dst, trafo_context);
+ if (src.iov_base != dst.iov_base) {
+ memcpy(c->header, dst.iov_base, SIDEBAND_HEADER_SIZE);
+ free(dst.iov_base);
+ }
+ if (!iov_valid(&sbb->iov))
+ goto out;
+ srcp = &sbb->iov;
+ dstp = &c->sbb.iov;
+ t(srcp, dstp, trafo_context);
+ if (srcp->iov_base != dstp->iov_base) {
+ if (!c->dont_free)
+ free(srcp->iov_base);
+ c->dont_free = false;
+ }
+out:
+ return c;
+}
+
+/**
+ * Deallocate all memory associated with a sideband handle.
+ *
+ * \param c The sideband handle.
+ *
+ * \a c must point to a handle previously returned by \ref sb_new_recv() or
+ * \ref sb_new_send(). It \a c is \p NULL, the function does nothing.
+ */
+void sb_free(struct sb_context *c)
+{
+ if (!c)
+ return;
+ if (!c->dont_free)
+ free(c->sbb.iov.iov_base);
+ free(c);
+}
+
+/**
+ * Obtain pointer(s) to the sideband send buffer(s).
+ *
+ * \param c The sideband handle.
+ * \param iov Array of two I/O vectors.
+ *
+ * \return The number of buffers that need to be sent, either 1 or 2.
+ *
+ * This function fills out the buffers described by \a iov. The result can be
+ * passed to \ref xwritev() or similar.
+ *
+ * \sa \ref sb_get_recv_buffer().
+ */
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2])
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched;
+
+ if (n < SIDEBAND_HEADER_SIZE) {
+ iov[0].iov_base = c->header + n;
+ iov[0].iov_len = SIDEBAND_HEADER_SIZE - n;
+ if (!iov_valid(&sbb->iov))
+ goto out;
+ iov[1] = sbb->iov;
+ return 2;
+ }
+ n -= SIDEBAND_HEADER_SIZE;
+ assert(n < sbb->iov.iov_len);
+ iov[0].iov_base = sbb->iov.iov_base + n;
+ iov[0].iov_len = sbb->iov.iov_len - n;
+out:
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ return 1;
+}
+
+/**
+ * Update the sideband context after data has been sent.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of sent bytes.
+ *
+ * \return False if more data must be sent to complete the sideband transfer,
+ * true if the transfer is complete. In this case all resources are freed and
+ * the sideband handle must not be used any more.
+ */
+bool sb_sent(struct sb_context *c, size_t nbytes)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+ assert(c->bytes_dispatched + nbytes <= sz);
+ c->bytes_dispatched += nbytes;
+ if (c->bytes_dispatched < sz)
+ return false;
+ sb_free(c);
+ return true;
+}
+
+/**
+ * Obtain a pointer to the next sideband read buffer.
+ *
+ * \param c The sideband handle.
+ * \param iov Result IO vector.
+ *
+ * This fills in \a iov to point to the buffer to which the next chunk of
+ * received data should be written.
+ */
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched;
+
+ if (n < SIDEBAND_HEADER_SIZE) {
+ iov->iov_base = c->header + n;
+ iov->iov_len = SIDEBAND_HEADER_SIZE - n;
+ return;
+ }
+ n -= SIDEBAND_HEADER_SIZE;
+ assert(sbb->iov.iov_base);
+ assert(sbb->iov.iov_len > n);
+ iov->iov_base = sbb->iov.iov_base + n;
+ iov->iov_len = sbb->iov.iov_len - n;
+}
+
+/**
+ * Update the sideband context after data has been received.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of bytes that have been received.
+ * \param result The received sideband packet.
+ *
+ * \return Negative on errors, zero if more data needs to be read to complete
+ * this sideband packet, one if the sideband packet has been received
+ * completely.
+ *
+ * Only if the function returns one, the sideband buffer pointed to by \a
+ * result is set to point to the received data.
+ */
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched,
+ sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+ assert(n + nbytes <= sz);
+ c->bytes_dispatched += nbytes;
+ if (c->bytes_dispatched < SIDEBAND_HEADER_SIZE)
+ return 0;
+ if (n >= SIDEBAND_HEADER_SIZE) { /* header has already been received */
+ if (c->bytes_dispatched < sz) /* need to recv more body data */
+ return 0;
+ /* received everything, decrypt and return sbb */
+ if (c->trafo) {
+ struct iovec dst;
+ c->trafo(&sbb->iov, &dst, c->trafo_context);
+ if (sbb->iov.iov_base != dst.iov_base) {
+ free(sbb->iov.iov_base);
+ sbb->iov.iov_base = dst.iov_base;
+ }
+ }
+ ((char *)(sbb->iov.iov_base))[sbb->iov.iov_len] = '\0';
+ goto success;
+ }
+ /* header has been received, decrypt and decode it */
+ if (c->trafo) { /* decrypt */
+ struct iovec dst, src = (typeof(src)) {
+ .iov_base = c->header,
+ .iov_len = SIDEBAND_HEADER_SIZE
+ };
+ c->trafo(&src, &dst, c->trafo_context);
+ if (src.iov_base != dst.iov_base) {
+ memcpy(c->header, dst.iov_base,
+ SIDEBAND_HEADER_SIZE);
+ free(dst.iov_base);
+ }
+ }
+ /* Decode header, setup sbb */
+ sbb->iov.iov_len = read_u32(c->header);
+ sbb->band = read_u8(c->header + 4);
+ sbb->iov.iov_base = NULL;
+ if (sbb->iov.iov_len == 0) /* zero-sized msg */
+ goto success;
+ if (c->max_size > 0 && sbb->iov.iov_len > c->max_size) {
+ PARA_ERROR_LOG("packet too big (is %zu, max %zu)\n",
+ sbb->iov.iov_len, c->max_size);
+ return -E_SB_PACKET_SIZE;
+ }
+ /*
+ * We like to reserve one extra byte for the terminating NULL
+ * character. However, we must make sure the +1 below does not
+ * overflow iov_len.
+ */
+ if (sbb->iov.iov_len == (size_t)-1)
+ return -E_SB_PACKET_SIZE;
+ sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
+ return 0; /* ready to read body */
+success:
+ *result = c->sbb;
+ free(c);
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.h exported symbols from sideband.c */
+
+/**
+ * The valid sideband designators.
+ *
+ * A sideband packet consists of a header and a body. The header is sent prior
+ * to the data and contains the length of the buffer and the sideband
+ * designator, an integer. This scheme allows receivers to never read more data
+ * than what was specified in the header.
+ *
+ * The sideband designator indicates the type of the data. The authentication
+ * handshake between the command handler (child of para_server) and the client
+ * requires to exchange several small packages. Each such package is sent as a
+ * sideband package with a dedicated designator.
+ *
+ * Other designators are employed for normal command output and for log
+ * messages, where each loglevel corresponds to a sideband designator.
+ *
+ * Note that the values of this enum are part of the ABI, so never change
+ * or remove entries. Appending is OK though.
+ */
+#define SB_DESIGNATORS \
+ /* Special value for receiving only: Accept any band. */ \
+ DESIGNATOR(ANY), \
+ /* This packet contains the authentication challenge. */ \
+ DESIGNATOR(CHALLENGE), \
+ /* I solved your challenge and here is the proof. */ \
+ DESIGNATOR(CHALLENGE_RESPONSE), \
+ /* Congratulations, you are authenticated. */ \
+ DESIGNATOR(PROCEED), \
+ /* This is the command I want you to execute. */ \
+ DESIGNATOR(COMMAND), \
+ /* Ready to receive a blob (addblob commands only). */ \
+ DESIGNATOR(AWAITING_DATA), \
+ /* Normal command output (default). */ \
+ DESIGNATOR(OUTPUT), \
+ /* LL_DEBUG. */ \
+ DESIGNATOR(DEBUG_LOG), \
+ /* LL_INFO. */ \
+ DESIGNATOR(INFO_LOG), \
+ /* LL_NOTICE. */ \
+ DESIGNATOR(NOTICE_LOG), \
+ /* LL_WARNING, */ \
+ DESIGNATOR(WARNING_LOG), \
+ /* LL_ERROR. */ \
+ DESIGNATOR(ERROR_LOG), \
+ /* LL_CRIT. */ \
+ DESIGNATOR(CRIT_LOG), \
+ /* LL_EMERG. */ \
+ DESIGNATOR(EMERG_LOG), \
+ /* Command terminated successfully. */ \
+ DESIGNATOR(EXIT__SUCCESS), \
+ /* Command failed. */ \
+ DESIGNATOR(EXIT__FAILURE), \
+
+/** Just prefix with \p SBD_. */
+#define DESIGNATOR(x) SBD_ ## x
+
+/** All valid sideband designators. */
+enum sb_designator {SB_DESIGNATORS NUM_SB_DESIGNATORS};
+#undef DESIGNATOR
+/** One stringified sideband designator. */
+#define DESIGNATOR(x) #x
+/** List of stringified sidedband designators. */
+#define SB_DESIGNATORS_ARRAY SB_DESIGNATORS
+
+/**
+ * The information contained in a sideband buffer.
+ *
+ * This structure is used for both sending and receiving data through a
+ * sideband channel. A pointer to a sideband buffer is passed to the sending
+ * side of a sideband while the receiving end yields a filled out structure
+ * once all data has been received.
+ */
+struct sb_buffer {
+ /** Data and length. */
+ struct iovec iov;
+ /** The band designator. */
+ uint8_t band;
+};
+
+/**
+ * The opaque sideband context structure.
+ *
+ * A pointer to a structure of this type is returned by the two \a sb_new_xxx
+ * functions as an opaque handle. Other public functions of the sideband API
+ * take such a handle as a parameter.
+ */
+struct sb_context;
+
+/**
+ * The type of a sideband transformation.
+ *
+ * The sideband API allows to filter all data through an arbitrary
+ * transformation, which is useful for crypto purposes. The transformation may
+ * either transform the data in place, or return a pointer to a new buffer
+ * which contains the transformed source buffer. The internal sideband
+ * functions can tell the two types of transformations apart by checking
+ * whether the destination buffer coincides with the source buffer.
+ *
+ * If the transformation allocates a new buffer, it _must_ allocate one extra
+ * byte for \p NULL termination.
+ */
+typedef void (*sb_transformation)(struct iovec *src, struct iovec *dst,
+ void *trafo_context);
+
+
+/** Initialize a sideband buffer. */
+#define SBB_INIT(_band, _buf, _numbytes) \
+ { \
+ .band = band, \
+ .iov = { \
+ .iov_base = _buf, \
+ .iov_len = _numbytes \
+ } \
+ };
+
+/* receiving */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+ void *trafo_context);
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov);
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result);
+
+/* sending */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+ sb_transformation t, void *trafo_context);
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2]);
+bool sb_sent(struct sb_context *c, size_t nbytes);
+
+void sb_free(struct sb_context *c);