Add sideband implementation.
[paraslash.git] / command.c
index cdc42fa1a220662ac7413b52fc40aba0f56e14d8..9cf1967faa4bdb4a3f12d313e2a322881f787d1f 100644 (file)
--- a/command.c
+++ b/command.c
@@ -15,6 +15,7 @@
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
+#include "sideband.h"
 #include "command.h"
 #include "server.cmdline.h"
 #include "string.h"
@@ -191,6 +192,118 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
        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;