Merge branch 't/decoder_fixes'
authorAndre Noll <maan@systemlinux.org>
Tue, 26 Jun 2012 20:07:55 +0000 (22:07 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 26 Jun 2012 20:11:07 +0000 (22:11 +0200)
97dbfe wmadec: Only decode one superframe at a time.
d6e017 flacdec: Only process a single flac audio frame.
1e1968 flacdec_close(): Be liberal in what you accept.
3f96f9 oggdec: Fix EOF handling on repositioning.
c7e2f7 oggdec: Realloc buffer to save memory.
634e75 oggdec: Do not decode more than necessary.

These changes are well tested and there are no known problems.

31 files changed:
Makefile.in
NEWS
afs.c
afs.cmd
afs.h
aft.c
attribute.c
bitstream.h
blob.c
client.c
client.h
client_common.c
command.c
command.h
configure.ac
crypt.c
crypt.h
crypt_common.c
error.h
gcc-compat.h
gcrypt.c
mood.c
para.h
playlist.c
server.cmd
sideband.c [new file with mode: 0644]
sideband.h [new file with mode: 0644]
string.c
string.h
wmadata.h
wmadec_filter.c

index 5760cad..30a5c01 100644 (file)
@@ -45,6 +45,8 @@ ifeq ($(uname_s),Linux)
        LDFLAGS += -Wl,--gc-sections
        CPPFLAGS += -Wstrict-prototypes
        CPPFLAGS += -Wshadow
+       # causes warnings on *BSD for the feature test macros
+       CPPFLAGS += -Wunused-macros
 endif
 CPPFLAGS += -Os
 CPPFLAGS += -Wuninitialized
@@ -56,7 +58,6 @@ CPPFLAGS += -DCODENAME='"$(codename)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -Werror-implicit-function-declaration
 CPPFLAGS += -Wmissing-noreturn
-CPPFLAGS += -Wunused-macros
 CPPFLAGS += -Wbad-function-cast
 CPPFLAGS += -fno-strict-aliasing
 CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
diff --git a/NEWS b/NEWS
index 0ac2240..df54a64 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,8 +2,11 @@
 0.4.11 (to be announced) "mutual diversity"
 -------------------------------------------
 
+       - Sideband connections: If both para_server and para_client
+         support this feature, data is sent as a multiplexed stream.
        - The --no_default_filters option of para_filter has been
          removed.
+       - Several fixes and latency improvements to various decoders.
        - Documentation improvements.
 
 ------------------------------------------
diff --git a/afs.c b/afs.c
index 5939cbe..a1381e7 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -26,6 +26,7 @@
 #include "signal.h"
 #include "fd.h"
 #include "mood.h"
+#include "sideband.h"
 #include "command.h"
 
 /** The osl tables used by afs. \sa blob.c. */
@@ -141,6 +142,8 @@ struct callback_query {
 struct callback_result {
        /** The number of bytes of the result. */
        size_t result_size;
+       /** The band designator (loglevel for the result). */
+       uint8_t band;
 };
 
 static int dispatch_result(int result_shmid, callback_result_handler *handler,
@@ -148,7 +151,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
 {
        struct osl_object result;
        void *result_shm;
-       int ret2, ret = shm_attach(result_shmid, ATTACH_RO, &result_shm);
+       /* must attach r/w as result.data might get encrypted in-place. */
+       int ret2, ret = shm_attach(result_shmid, ATTACH_RW, &result_shm);
        struct callback_result *cr = result_shm;
 
        if (ret < 0) {
@@ -159,7 +163,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
        result.data = result_shm + sizeof(*cr);
        if (result.size) {
                assert(handler);
-               ret = handler(&result, private_result_data);
+               ret = handler(&result, cr->band, private_result_data);
                if (ret < 0)
                        PARA_NOTICE_LOG("result handler error: %s\n",
                                para_strerror(-ret));
@@ -546,8 +550,11 @@ static void com_select_callback(int fd, const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler,
        };
        char *arg = query->data;
        int num_admissible, ret, ret2;
@@ -577,7 +584,7 @@ static void com_select_callback(int fd, const struct osl_object *query)
                        current_mop : "dummy mood", num_admissible);
 out:
        if (ret2 >= 0 && pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -585,23 +592,24 @@ out:
  * Result handler for sending data to the para_client process.
  *
  * \param result The data to be sent.
- * \param private Pointer to the context.
+ * \param band The band designator.
+ * \param private Pointer to the command context.
  *
- * \return The return value of the underlying call to sc_send_bin_buffer().
+ * \return The return value of the underlying call to \ref command.c::send_sb.
  *
- * \sa \ref callback_result_handler, \ref sc_send_bin_buffer().
+ * \sa \ref callback_result_handler, \ref command.c::send_sb.
  */
-int sc_send_result(struct osl_object *result, void *private)
+int afs_cb_result_handler(struct osl_object *result, uint8_t band,
+               void *private)
 {
        struct command_context *cc = private;
-       int ret;
 
        if (!result->size)
                return 1;
-       ret = sc_send_bin_buffer(&cc->scc, result->data, result->size);
-       if (ret < 0 || ret == result->size)
-               return ret;
-       return -E_SHORT_WRITE;
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, result->data, result->size, band,
+                       true);
+       return sc_send_bin_buffer(&cc->scc, result->data, result->size);
 }
 
 int com_select(struct command_context *cc)
@@ -613,7 +621,7 @@ int com_select(struct command_context *cc)
        query.data = cc->argv[1];
        query.size = strlen(cc->argv[1]) + 1;
        return send_callback_request(com_select_callback, &query,
-               &sc_send_result, cc);
+               &afs_cb_result_handler, cc);
 }
 
 static void init_admissible_files(char *arg)
@@ -783,30 +791,30 @@ static void command_pre_select(struct sched *s, struct task *t)
 /**
  * Send data as shared memory to a file descriptor.
  *
+ * \param fd File descriptor to send the shmid to.
+ * \param band The band designator for this data.
  * \param buf The buffer holding the data to be sent.
  * \param size The size of \a buf.
- * \param fd_ptr A pointer to the file descriptor.
- *
- * This function is used as the \a max_size handler in a \ref para_buffer
- * structure. If used this way, it is called by \ref para_printf() whenever
- * the buffer passed to para_printf() is about to exceed its maximal size.
  *
  * This function creates a shared memory area large enough to hold
  * the content given by \a buf and \a size and sends the identifier
- * of this area to the file descriptor given by \a fd_ptr.
+ * of this area to the file descriptor \a fd.
+ *
+ * It is called by the AFS max_size handler as well as directly by the AFS
+ * command callbacks to send command output to the command handlers.
  *
  * \return Zero if \a buf is \p NULL or \a size is zero. Negative on errors,
  * and positive on success.
  */
-int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr)
+int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size)
 {
-       int ret, shmid, fd = *(int *)fd_ptr;
+       int ret, shmid;
        void *shm;
        struct callback_result *cr;
 
        if (!buf || !size)
                return 0;
-       ret = shm_new(size + sizeof(struct callback_result));
+       ret = shm_new(size + sizeof(*cr));
        if (ret < 0)
                return ret;
        shmid = ret;
@@ -815,6 +823,7 @@ int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr)
                goto err;
        cr = shm;
        cr->result_size = size;
+       cr->band = band;
        memcpy(shm + sizeof(*cr), buf, size);
        ret = shm_detach(shm);
        if (ret < 0)
@@ -1023,7 +1032,7 @@ out:
        if (ret < 0)
                para_printf(&pb, "%s\n", para_strerror(-ret));
        if (pb.buf)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -1053,7 +1062,7 @@ int com_init(struct command_context *cc)
                }
        }
        ret = send_callback_request(create_tables_callback, &query,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
        if (ret < 0)
                /* ignore return value */
                sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
@@ -1107,19 +1116,19 @@ int com_check(struct command_context *cc)
                flags = ~0U;
        if (flags & CHECK_AFT) {
                ret = send_callback_request(aft_check_callback, NULL,
-                       sc_send_result, cc);
+                       afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_PLAYLISTS) {
                ret = send_callback_request(playlist_check_callback,
-                       NULL, sc_send_result, cc);
+                       NULL, afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_MOODS) {
                ret = send_callback_request(mood_check_callback, NULL,
-                       sc_send_result, cc);
+                       afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
diff --git a/afs.cmd b/afs.cmd
index c780804..11b1631 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -4,7 +4,7 @@ HC: Prototypes for the commands of the audio file selector.
 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
 ---
diff --git a/afs.h b/afs.h
index 7145c41..2789e94 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -205,9 +205,38 @@ typedef void callback_function(int fd, const struct osl_object *);
  *
  * \sa \ref send_callback_request().
  */
-typedef int callback_result_handler(struct osl_object *result, void *private);
-int sc_send_result(struct osl_object *result, void *private);
-int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr);
+typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
+int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
+int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size);
+
+/** Structure passed to the AFS max_size handler. */
+struct afs_max_size_handler_data {
+       /** Local socket connecting the command handler and the AFS process. */
+       int fd;
+       /** The sideband designator for this data packet. */
+       uint8_t band;
+};
+
+/**
+ * Standard max_size handler for AFS commands.
+ *
+ * \param buf Contains (part of) the AFS command output.
+ * \param size The number of bytes in \a buf.
+ * \param private Pointer to a \ref afs_max_size_handler_data structure.
+ *
+ * Whenever the output of an AFS command exceeds the maximal size of a shared
+ * memory area, the max size handler of the para_buffer which holds the command
+ * output is called with \a private being a pointer to a structure of type
+ * afs_max_size_handler_data.
+ *
+ * \return The return value of the underlying call to \ref
+ *  pass_buffer_as_shm().
+ */
+_static_inline_ int afs_max_size_handler(char *buf, size_t size, void *private)
+{
+       struct afs_max_size_handler_data *amshd = private;
+       return pass_buffer_as_shm(amshd->fd, amshd->band, buf, size);
+}
 
 __noreturn void afs_init(uint32_t cookie, int socket_fd);
 void afs_event(enum afs_events event, struct para_buffer *pb,
diff --git a/aft.c b/aft.c
index 7516e32..2732790 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -21,6 +21,7 @@
 #include "fd.h"
 #include "ipc.h"
 #include "portable_io.h"
+#include "sideband.h"
 #include "command.h"
 
 static struct osl_table *audio_file_table;
@@ -1349,9 +1350,15 @@ static void com_ls_callback(int fd, const struct osl_object *query)
 {
        struct ls_options *opts = query->data;
        char *p, *pattern_start = (char *)query->data + sizeof(*opts);
-       struct para_buffer b = {.max_size = shm_get_shmmax(),
+       struct para_buffer b = {
+               .max_size = shm_get_shmmax(),
                .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
-               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+               .max_size_handler = afs_max_size_handler,
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               }
+       };
        int i = 0, ret;
        time_t current_time;
 
@@ -1393,7 +1400,7 @@ static void com_ls_callback(int fd, const struct osl_object *query)
                }
 out:
        if (b.offset)
-               pass_buffer_as_shm(b.buf, b.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, b.buf, b.offset);
        free(b.buf);
        free(opts->data);
        free(opts->data_ptr);
@@ -1515,7 +1522,7 @@ int com_ls(struct command_context *cc)
        opts.mode = mode;
        opts.num_patterns = cc->argc - i;
        ret = send_option_arg_callback_request(&query, opts.num_patterns,
-               cc->argv + i, com_ls_callback, sc_send_result, cc);
+               cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
        return ret;
 }
 
@@ -1674,8 +1681,14 @@ static void com_add_callback(int fd, const struct osl_object *query)
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
-       struct para_buffer msg = {.max_size = shm_get_shmmax(),
-               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+       struct para_buffer msg = {
+               .max_size = shm_get_shmmax(),
+               .max_size_handler = afs_max_size_handler,
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               }
+       };
        uint16_t afhi_offset, chunks_offset;
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
@@ -1796,7 +1809,7 @@ out:
        if (ret < 0)
                para_printf(&msg, "%s\n", para_strerror(-ret));
        if (msg.offset)
-               pass_buffer_as_shm(msg.buf, msg.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, msg.buf, msg.offset);
        free(msg.buf);
 }
 
@@ -1815,7 +1828,8 @@ static void path_brother_callback(int fd, const struct osl_object *query)
        int ret = aft_get_row_of_path(path, &path_brother);
        if (ret < 0)
                return;
-       pass_buffer_as_shm((char *)&path_brother, sizeof(path_brother), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&path_brother,
+               sizeof(path_brother));
 }
 
 static void hash_sister_callback(int fd, const struct osl_object *query)
@@ -1826,10 +1840,12 @@ static void hash_sister_callback(int fd, const struct osl_object *query)
        hash_sister = find_hash_sister(hash);
        if (!hash_sister)
                return;
-       pass_buffer_as_shm((char *)&hash_sister, sizeof(hash_sister), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&hash_sister,
+               sizeof(hash_sister));
 }
 
-static int get_row_pointer_from_result(struct osl_object *result, void *private)
+static int get_row_pointer_from_result(struct osl_object *result,
+               __a_unused uint8_t band, void *private)
 {
        struct osl_row **row = private;
        *row = *(struct osl_row **)(result->data);
@@ -1858,8 +1874,12 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = sc_send_va_buffer(&pad->cc->scc,
-                               "lazy-ignore: %s\n", path);
+                       send_ret = pad->cc->use_sideband?
+                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                                       "lazy-ignore: %s\n", path)
+                       :
+                               sc_send_va_buffer(&pad->cc->scc,
+                                       "lazy-ignore: %s\n", path);
                goto out_free;
        }
        /* We still want to add this file. Compute its hash. */
@@ -1879,8 +1899,12 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = sc_send_va_buffer(&pad->cc->scc,
-                               "%s exists, not forcing update\n", path);
+                       send_ret = pad->cc->use_sideband?
+                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                                       "%s exists, not forcing update\n", path)
+                       :
+                               sc_send_va_buffer(&pad->cc->scc,
+                                       "%s exists, not forcing update\n", path);
                goto out_unmap;
        }
        /*
@@ -1897,13 +1921,19 @@ static int add_one_audio_file(const char *path, void *private_data)
        munmap(map.data, map.size);
        close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
-               send_ret = sc_send_va_buffer(&pad->cc->scc, "adding %s\n", path);
+               send_ret = pad->cc->use_sideband?
+                       send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                               "adding %s\n", path)
+               :
+                       sc_send_va_buffer(&pad->cc->scc,
+                               "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
        save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
        /* Ask afs to consider this entry for adding. */
-       ret = send_callback_request(com_add_callback, &obj, sc_send_result, pad->cc);
+       ret = send_callback_request(com_add_callback, &obj,
+               afs_cb_result_handler, pad->cc);
        goto out_free;
 
 out_unmap:
@@ -1911,8 +1941,14 @@ out_unmap:
        munmap(map.data, map.size);
 out_free:
        if (ret < 0 && send_ret >= 0)
-               send_ret = sc_send_va_buffer(&pad->cc->scc,
-                       "failed to add %s (%s)\n", path, para_strerror(-ret));
+               send_ret = pad->cc->use_sideband?
+                       send_sb_va(&pad->cc->scc, SBD_ERROR_LOG,
+                               "failed to add %s (%s)\n", path,
+                               para_strerror(-ret))
+               :
+                       sc_send_va_buffer(&pad->cc->scc,
+                               "failed to add %s (%s)\n", path,
+                               para_strerror(-ret));
        free(obj.data);
        clear_afhi(afhi_ptr);
        /* Stop adding files only on send errors. */
@@ -1956,7 +1992,11 @@ int com_add(struct command_context *cc)
                char *path;
                ret = verify_path(cc->argv[i], &path);
                if (ret < 0) {
-                       ret = sc_send_va_buffer(&cc->scc, "%s: %s\n",
+                       ret = cc->use_sideband?
+                               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
+                               cc->argv[i], para_strerror(-ret))
+                       :
+                               sc_send_va_buffer(&cc->scc, "%s: %s\n",
                                cc->argv[i], para_strerror(-ret));
                        if (ret < 0)
                                return ret;
@@ -1964,9 +2004,14 @@ int com_add(struct command_context *cc)
                }
                ret = stat(path, &statbuf);
                if (ret < 0) {
-                       ret = sc_send_va_buffer(&cc->scc,
-                               "failed to stat %s (%s)\n", path,
-                               strerror(errno));
+                       ret = cc->use_sideband?
+                               send_sb_va(&cc->scc, SBD_ERROR_LOG,
+                                       "failed to stat %s (%s)\n", path,
+                                       strerror(errno))
+                               :
+                               sc_send_va_buffer(&cc->scc,
+                                       "failed to stat %s (%s)\n", path,
+                                       strerror(errno));
                        free(path);
                        if (ret < 0)
                                return ret;
@@ -1978,8 +2023,12 @@ int com_add(struct command_context *cc)
                else
                        ret = add_one_audio_file(path, &pad);
                if (ret < 0) {
-                       sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
-                               para_strerror(-ret));
+                       if (cc->use_sideband)
+                               send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path,
+                                       para_strerror(-ret));
+                       else
+                               sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
+                                       para_strerror(-ret));
                        free(path);
                        return ret;
                }
@@ -2082,7 +2131,7 @@ static void com_touch_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret, ret2 = 0;
@@ -2103,7 +2152,7 @@ static void com_touch_callback(int fd, const struct osl_object *query)
        else if (pmd.num_matches == 0)
                ret2 = para_printf(&tad.pb, "no matches\n");
        if (ret2 >= 0 && tad.pb.offset)
-               pass_buffer_as_shm(tad.pb.buf, tad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, tad.pb.buf, tad.pb.offset);
        free(tad.pb.buf);
 }
 
@@ -2175,9 +2224,9 @@ int com_touch(struct command_context *cc)
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_touch_callback, sc_send_result, cc);
+               cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2223,7 +2272,7 @@ static void com_rm_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret;
@@ -2250,7 +2299,7 @@ static void com_rm_callback(int fd, const struct osl_object *query)
                        pmd.num_matches);
        }
        if (ret >= 0 && crd.pb.offset)
-               pass_buffer_as_shm(crd.pb.buf, crd.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, crd.pb.buf, crd.pb.offset);
        free(crd.pb.buf);
 }
 
@@ -2286,9 +2335,9 @@ int com_rm(struct command_context *cc)
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_rm_callback, sc_send_result, cc);
+               cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2365,7 +2414,7 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret;
@@ -2396,7 +2445,7 @@ out:
                        para_printf(&cad.pb, "nothing copied\n");
        }
        if (cad.pb.offset)
-               pass_buffer_as_shm(cad.pb.buf, cad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
        free(cad.pb.buf);
 }
 
@@ -2445,9 +2494,9 @@ int com_cpsi(struct command_context *cc)
        if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
                flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
        ret = send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, com_cpsi_callback, sc_send_result, cc);
+               cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2459,7 +2508,7 @@ static void afs_stat_callback(int fd, const struct osl_object *query)
 
        if (!buf)
                return;
-       pass_buffer_as_shm(buf, strlen(buf), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, buf, strlen(buf));
 }
 
 /**
@@ -2481,7 +2530,8 @@ int send_afs_status(struct command_context *cc, int parser_friendly)
        struct osl_object query = {.data = &parser_friendly,
                .size = sizeof(parser_friendly)};
 
-       return send_callback_request(afs_stat_callback, &query, sc_send_result, cc);
+       return send_callback_request(afs_stat_callback, &query,
+               afs_cb_result_handler, cc);
 }
 
 /* TODO: optionally fix problems by removing offending rows */
@@ -2538,8 +2588,11 @@ void aft_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler
        };
        int ret = para_printf(&pb, "checking audio file table...\n");
 
@@ -2547,7 +2600,7 @@ void aft_check_callback(int fd, __a_unused const struct osl_object *query)
                return;
        audio_file_loop(&pb, check_audio_file);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
index 1970094..638e22e 100644 (file)
@@ -16,6 +16,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "ipc.h"
+#include "sideband.h"
 #include "command.h"
 
 static struct osl_table *attribute_table;
@@ -151,8 +152,11 @@ static void com_lsatt_callback(int fd, const struct osl_object *query)
                .flags = *(unsigned *) query->data,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler
                }
 
        };
@@ -172,7 +176,7 @@ static void com_lsatt_callback(int fd, const struct osl_object *query)
                pmd.pm_flags |= PM_REVERSE_LOOP;
        for_each_matching_row(&pmd);
        if (laad.pb.offset)
-               pass_buffer_as_shm(laad.pb.buf, laad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, laad.pb.buf, laad.pb.offset);
        free(laad.pb.buf);
 }
 
@@ -204,12 +208,14 @@ int com_lsatt(struct command_context *cc)
                }
        }
        ret = send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
-               com_lsatt_callback, sc_send_result, cc);
-       if (!ret) {
-               if (cc->argc > 1)
-                       ret = sc_send_va_buffer(&cc->scc, "no matches\n");
-       } else if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               com_lsatt_callback, afs_cb_result_handler, cc);
+       if (ret < 0)
+               send_strerror(cc, -ret);
+       else if (ret == 0 && cc->argc > 1)
+               ret = cc->use_sideband?
+                       send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n")
+               :
+                       sc_send_va_buffer(&cc->scc, "no matches\n");
        return ret;
 }
 
@@ -297,8 +303,11 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
        int ret = 1, ret2 = 0;
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler
        };
        size_t len;
 
@@ -354,7 +363,7 @@ out:
        if (ret < 0 && ret2 >= 0)
                para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -365,9 +374,9 @@ int com_addatt(struct command_context *cc)
        if (cc->argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_addatt_callback, sc_send_result, cc);
+               com_addatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -380,8 +389,11 @@ static void com_mvatt_callback(int fd, const struct osl_object *query)
        struct osl_row *row;
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler,
        };
        int ret;
 
@@ -397,7 +409,7 @@ out:
        else
                afs_event(ATTRIBUTE_RENAME, &pb, NULL);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -408,9 +420,9 @@ int com_mvatt(struct command_context *cc)
        if (cc->argc != 3)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_mvatt_callback, sc_send_result, cc);
+               com_mvatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -450,8 +462,11 @@ static void com_rmatt_callback(int fd, const struct osl_object *query)
                .num_removed = 0,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        int ret, ret2 = 0;
@@ -469,7 +484,7 @@ static void com_rmatt_callback(int fd, const struct osl_object *query)
        else if (!raad.num_removed)
                ret2 = para_printf(&raad.pb, "no match -- nothing removed\n");
        if (ret2 >= 0 && raad.pb.offset)
-               pass_buffer_as_shm(raad.pb.buf, raad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, raad.pb.buf, raad.pb.offset);
        free(raad.pb.buf);
 }
 
@@ -480,9 +495,9 @@ int com_rmatt(struct command_context *cc)
        if (cc->argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_rmatt_callback, sc_send_result, cc);
+               com_rmatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
index 5d6be3b..a339338 100644 (file)
@@ -11,9 +11,9 @@
 
 /** Structure for bistream I/O. */
 struct getbit_context {
-       /* Start of the internal buffer. */
+       /** Start of the internal buffer. */
        const uint8_t *buffer;
-       /* End of the internal buffer. */
+       /** End of the internal buffer. */
        const uint8_t *buffer_end;
        /** Bit counter. */
        int index;
diff --git a/blob.c b/blob.c
index 409e348..f51f4de 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -18,6 +18,7 @@
 #include "afs.h"
 #include "ipc.h"
 #include "portable_io.h"
+#include "sideband.h"
 #include "command.h"
 
 /**
@@ -133,8 +134,11 @@ static void com_lsblob_callback(struct osl_table *table,
                .flags = *(uint32_t *)query->data,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        struct pattern_match_data pmd = {
@@ -160,7 +164,7 @@ static void com_lsblob_callback(struct osl_table *table,
        else if (pmd.num_matches == 0 && pmd.patterns.size > 0)
                para_printf(&lbad.pb, "no matches\n");
        if (lbad.pb.offset)
-               pass_buffer_as_shm(lbad.pb.buf, lbad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, lbad.pb.buf, lbad.pb.offset);
        free(lbad.pb.buf);
 }
 
@@ -195,20 +199,20 @@ static int com_lsblob(callback_function *f, struct command_context *cc)
 //     if (argc > i)
 //             return -E_BLOB_SYNTAX;
        return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, f, sc_send_result, cc);
+               cc->argv + i, f, afs_cb_result_handler, cc);
 }
 
 static int cat_blob(struct osl_table *table, struct osl_row *row,
                __a_unused const char *name, void *data)
 {
-       int ret = 0, ret2;
+       int ret = 0, ret2, fd = *(int *)data;
        struct osl_object obj;
 
        ret = osl(osl_open_disk_object(table, row, BLOBCOL_DEF, &obj));
        if (ret < 0)
                return (ret == osl(-E_OSL_EMPTY))? 0 : ret;
        assert(obj.size > 0);
-       ret = pass_buffer_as_shm(obj.data, obj.size, data);
+       ret = pass_buffer_as_shm(fd, SBD_OUTPUT, obj.data, obj.size);
        ret2 = osl(osl_close_disk_object(&obj));
        return (ret < 0)? ret : ret2;
 }
@@ -228,7 +232,7 @@ static void com_catblob_callback(struct osl_table *table, int fd,
        for_each_matching_row(&pmd);
        if (pmd.num_matches == 0) {
                char err_msg[] = "no matches\n";
-               pass_buffer_as_shm(err_msg, sizeof(err_msg), &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, err_msg, sizeof(err_msg));
        }
 }
 
@@ -237,7 +241,7 @@ static int com_catblob(callback_function *f, struct command_context *cc)
        if (cc->argc < 2)
                return -E_BLOB_SYNTAX;
        return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
 }
 
 /** Used for removing rows from a blob table. */
@@ -265,8 +269,11 @@ static void com_rmblob_callback(struct osl_table *table, int fd,
        struct rmblob_data rmbd = {
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        struct pattern_match_data pmd = {
@@ -292,7 +299,7 @@ static void com_rmblob_callback(struct osl_table *table, int fd,
        }
 out:
        if (ret2 >= 0 && rmbd.pb.offset)
-               pass_buffer_as_shm(rmbd.pb.buf, rmbd.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, rmbd.pb.buf, rmbd.pb.offset);
        free(rmbd.pb.buf);
 }
 
@@ -301,7 +308,7 @@ static int com_rmblob(callback_function *f, struct command_context *cc)
        if (cc->argc < 2)
                return -E_MOOD_SYNTAX;
        return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
 }
 
 static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
@@ -432,7 +439,10 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        struct osl_object query, stdin_obj;
        int ret;
 
-       ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
+       if (cc->use_sideband)
+               ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
+       else
+               ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
        if (ret < 0)
                return ret;
        ret = fd2buf(&cc->scc, max_len, &stdin_obj);
index 5f51170..c194e19 100644 (file)
--- a/client.c
+++ b/client.c
@@ -603,14 +603,23 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = ct->btrn));
        register_task(&sched, &svt);
        ret = schedule(&sched);
+       if (ret >= 0 && ct->task.error < 0) {
+               switch(ct->task.error) {
+               /* these are not errors */
+               case -E_SERVER_CMD_SUCCESS:
+               case -E_EOF:
+               case -E_SERVER_EOF:
+               case -E_BTR_EOF:
+                       ret = 0;
+                       break;
+               default: ret = ct->task.error;
+               }
+       }
 out:
+       if (ret < 0)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        client_close(ct);
        btr_free_node(sit.btrn);
        btr_free_node(sot.btrn);
-       if (ret < 0) {
-               /* can not use PARA_LOG here because ct is NULL */
-               fprintf(stderr, "%s\n", para_strerror(-ret));
-               return EXIT_FAILURE;
-       }
-       return EXIT_SUCCESS;
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 8490b72..b841b7e 100644 (file)
--- a/client.h
+++ b/client.h
@@ -14,6 +14,8 @@ enum {
        CL_RECEIVED_WELCOME,
        /** Client sends the authentification request. */
        CL_SENT_AUTH,
+       /** Server sends encrypted challenge. */
+       CL_RECEIVED_CHALLENGE,
        /** Client solves the challenge and sends the result. */
        CL_SENT_CH_RESPONSE,
        /** Server accepts this authentication. */
@@ -32,6 +34,12 @@ struct client_task {
        int status;
        /** The file descriptor and the session keys. */
        struct stream_cipher_context scc;
+       /** True if this connections uses the sideband API. */
+       bool use_sideband;
+       /** The sideband context, ignored if \a use_sideband is false. */
+       struct sb_context *sbc;
+       /** The hash value of the decrypted challenge. */
+       unsigned char *challenge_hash;
        /** The configuration (including the command). */
        struct client_args_info conf;
        /** The config file for client options. */
@@ -44,6 +52,8 @@ struct client_task {
        struct task task;
        /** The buffer tree node of the client task. */
        struct btr_node *btrn;
+       /** List of features supported by the server. */
+       char **features;
 };
 
 void client_disconnect(struct client_task *ct);
index 5bd2241..649a1b4 100644 (file)
@@ -17,6 +17,7 @@
 #include "crypt.h"
 #include "net.h"
 #include "fd.h"
+#include "sideband.h"
 #include "string.h"
 #include "client.cmdline.h"
 #include "client.h"
@@ -42,6 +43,7 @@ void client_disconnect(struct client_task *ct)
                return;
        if (ct->scc.fd >= 0)
                close(ct->scc.fd);
+       free_argv(ct->features);
        sc_free(ct->scc.recv);
        ct->scc.recv = NULL;
        sc_free(ct->scc.send);
@@ -66,6 +68,8 @@ void client_close(struct client_task *ct)
        free(ct->config_file);
        free(ct->key_file);
        client_cmdline_parser_free(&ct->conf);
+       free(ct->challenge_hash);
+       sb_free(ct->sbc);
        free(ct);
 }
 
@@ -101,6 +105,7 @@ static void client_pre_select(struct sched *s, struct task *t)
 
        case CL_RECEIVED_WELCOME:
        case CL_RECEIVED_PROCEED:
+       case CL_RECEIVED_CHALLENGE:
                para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                return;
 
@@ -152,6 +157,162 @@ static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
        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;
+       const char id[] = "\nFeatures: ";
+       char *p, *q, **features;
+
+       p = strstr(buf, id);
+       if (!p)
+               return NULL;
+       p += strlen(id);
+       q = strchr(p, '\n');
+       if (!q)
+               return NULL;
+       *q = '\0';
+       create_argv(p, ",", &features);
+       for (i = 0; features[i]; i++)
+               PARA_INFO_LOG("server feature: %s\n", features[i]);
+       return features;
+}
+
+static int dispatch_sbb(struct client_task *ct, struct sb_buffer *sbb)
+{
+       int ret;
+       const char *designator[] = {SB_DESIGNATORS_ARRAY};
+
+       if (!sbb)
+               return 0;
+       if (sbb->band < NUM_SB_DESIGNATORS)
+               PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]);
+
+       switch (sbb->band) {
+       case SBD_OUTPUT:
+               if (iov_valid(&sbb->iov))
+                       btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len,
+                               ct->btrn);
+               ret = 1;
+               goto out;
+       case SBD_DEBUG_LOG:
+       case SBD_INFO_LOG:
+       case SBD_NOTICE_LOG:
+       case SBD_WARNING_LOG:
+       case SBD_ERROR_LOG:
+       case SBD_CRIT_LOG:
+       case SBD_EMERG_LOG:
+               if (iov_valid(&sbb->iov)) {
+                       int ll = sbb->band - SBD_DEBUG_LOG;
+                       para_log(ll, "remote: %s", (char *)sbb->iov.iov_base);
+               }
+               ret = 1;
+               goto deallocate;
+       case SBD_EXIT__SUCCESS:
+               ret = -E_SERVER_CMD_SUCCESS;
+               goto deallocate;
+       case SBD_EXIT__FAILURE:
+               ret = -E_SERVER_CMD_FAILURE;
+               goto deallocate;
+       default:
+               PARA_ERROR_LOG("invalid band %d\n", sbb->band);
+               ret = -E_BAD_BAND;
+               goto deallocate;
+       }
+deallocate:
+       free(sbb->iov.iov_base);
+out:
+       sbb->iov.iov_base = NULL;
+       return ret;
+}
+
+static bool has_feature(const char *feature, struct client_task *ct)
+{
+       return find_arg(feature, ct->features) >= 0? true : false;
+}
+
+static int send_sb_command(struct client_task *ct)
+{
+       int i;
+       char *command, *p;
+       size_t len = 0;
+
+       if (ct->sbc)
+               return send_sb(ct, NULL, 0, 0, false);
+
+       for (i = 0; i < ct->conf.inputs_num; i++)
+               len += strlen(ct->conf.inputs[i]) + 1;
+       p = command = para_malloc(len);
+       for (i = 0; i < ct->conf.inputs_num; i++) {
+               strcpy(p, ct->conf.inputs[i]);
+               p += strlen(ct->conf.inputs[i]) + 1;
+       }
+       PARA_DEBUG_LOG("--> %s\n", command);
+       return send_sb(ct, command, len, SBD_COMMAND, false);
+}
+
 /**
  * The post select hook for client commands.
  *
@@ -181,13 +342,18 @@ static void client_post_select(struct sched *s, struct task *t)
                ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
                if (ret < 0 || n == 0)
                        goto out;
+               ct->features = parse_features(buf);
                ct->status = CL_RECEIVED_WELCOME;
                return;
        case CL_RECEIVED_WELCOME: /* send auth command */
-               sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
-               PARA_INFO_LOG("--> %s\n", buf);
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return;
+               if (has_feature("sideband", ct)) {
+                       ct->use_sideband = true;
+                       sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
+               } else
+                       sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
+               PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                        goto out;
@@ -202,30 +368,73 @@ static void client_post_select(struct sched *s, struct task *t)
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
                /* the SHA1 of the decrypted challenge */
-               unsigned char challenge_hash[HASH_SIZE];
 
-               ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
-               if (ret < 0 || n == 0)
-                       goto out;
-               PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-               ret = priv_decrypt(ct->key_file, crypt_buf,
-                       (unsigned char *)buf, n);
-               if (ret < 0)
-                       goto out;
-               hash_function((char *)crypt_buf, CHALLENGE_SIZE, challenge_hash);
+               if (ct->use_sideband) {
+                       struct sb_buffer sbb;
+                       ret = recv_sb(ct, &s->rfds, &sbb);
+                       if (ret <= 0)
+                               goto out;
+                       if (sbb.band != SBD_CHALLENGE) {
+                               ret = -E_BAD_BAND;
+                               free(sbb.iov.iov_base);
+                                       goto out;
+                       }
+                       n = sbb.iov.iov_len;
+                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
+                       ret = priv_decrypt(ct->key_file, crypt_buf,
+                               sbb.iov.iov_base, n);
+                       free(sbb.iov.iov_base);
+                       if (ret < 0)
+                               goto out;
+               } else {
+                       ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
+                       if (ret < 0 || n == 0)
+                               goto out;
+                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
+                       ret = priv_decrypt(ct->key_file, crypt_buf,
+                               (unsigned char *)buf, n);
+                       if (ret < 0)
+                               goto out;
+               }
+               ct->challenge_hash = para_malloc(HASH_SIZE);
+               hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
                ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
                        SESSION_KEY_LEN);
-               hash_to_asc(challenge_hash, buf);
+               hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
-               ret = write_all(ct->scc.fd, (char *)challenge_hash, HASH_SIZE);
-               if (ret < 0)
-                       goto out;
-               ct->status = CL_SENT_CH_RESPONSE;
+               ct->status = CL_RECEIVED_CHALLENGE;
                return;
                }
+       case CL_RECEIVED_CHALLENGE:
+               if (ct->use_sideband) {
+                       ret = send_sb(ct, ct->challenge_hash, HASH_SIZE,
+                               SBD_CHALLENGE_RESPONSE, false);
+                       if (ret != 0)
+                               ct->challenge_hash = NULL;
+                       if (ret <= 0)
+                               goto out;
+               } else {
+                       ret = write_all(ct->scc.fd, (char *)ct->challenge_hash, HASH_SIZE);
+                       if (ret < 0)
+                               goto out;
+               }
+               ct->status = CL_SENT_CH_RESPONSE;
+               goto out;
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
+               if (ct->use_sideband) {
+                       struct sb_buffer sbb;
+                       ret = recv_sb(ct, &s->rfds, &sbb);
+                       if (ret <= 0)
+                               goto out;
+                       free(sbb.iov.iov_base);
+                       if (sbb.band != SBD_PROCEED)
+                               ret = -E_BAD_BAND;
+                       else
+                               ct->status = CL_RECEIVED_PROCEED;
+                       goto out;
+               }
                ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
                if (ret < 0 || n == 0)
                        goto out;
@@ -244,6 +453,13 @@ static void client_post_select(struct sched *s, struct task *t)
                char *command = NULL;
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return;
+               if (ct->use_sideband) {
+                       ret = send_sb_command(ct);
+                       if (ret <= 0)
+                               goto out;
+                       ct->status = CL_SENT_COMMAND;
+                       return;
+               }
                for (i = 0; i < ct->conf.inputs_num; i++) {
                        char *tmp = command;
                        command = make_message("%s\n%s", command?
@@ -262,6 +478,20 @@ static void client_post_select(struct sched *s, struct task *t)
        case CL_SENT_COMMAND:
                {
                char *buf2;
+               if (ct->use_sideband) {
+                       struct sb_buffer sbb;
+                       ret = recv_sb(ct, &s->rfds, &sbb);
+                       if (ret <= 0)
+                               goto out;
+                       if (sbb.band == SBD_AWAITING_DATA) {
+                               ct->status = CL_SENDING;
+                               free(sbb.iov.iov_base);
+                               goto out;
+                       }
+                       ct->status = CL_RECEIVING;
+                       ret = dispatch_sbb(ct, &sbb);
+                       goto out;
+               }
                /* can not use "buf" here because we need a malloced buffer */
                buf2 = para_malloc(CLIENT_BUFSIZE);
                ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
@@ -309,6 +539,13 @@ static void client_post_select(struct sched *s, struct task *t)
                 */
                if (!FD_ISSET(ct->scc.fd, &s->rfds))
                        return;
+               if (ct->use_sideband) {
+                       struct sb_buffer sbb;
+                       ret = recv_sb(ct, &s->rfds, &sbb);
+                       if (ret > 0)
+                               ret = dispatch_sbb(ct, &sbb);
+                       goto out;
+               }
                buf2 = para_malloc(CLIENT_BUFSIZE);
                ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
                if (n > 0) {
@@ -322,7 +559,8 @@ static void client_post_select(struct sched *s, struct task *t)
 out:
        t->error = ret;
        if (ret < 0) {
-               if (ret != -E_SERVER_EOF && ret != -E_BTR_EOF)
+               if (!ct->use_sideband && ret != -E_SERVER_EOF &&
+                               ret != -E_BTR_EOF && ret != -E_EOF)
                        PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
                btr_remove_node(btrn);
        }
index f520e3e..2cac57f 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"
@@ -101,7 +102,8 @@ static char *vss_get_status_flags(unsigned int flags)
        return msg;
 }
 
-static char *get_status(struct misc_meta_data *nmmd, int parser_friendly)
+static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
+               char **result)
 {
        char mtime[30] = "";
        char *status, *flags; /* vss status info */
@@ -141,7 +143,8 @@ static char *get_status(struct misc_meta_data *nmmd, int parser_friendly)
        free(flags);
        free(status);
        free(ut);
-       return b.buf;
+       *result = b.buf;
+       return b.offset;
 }
 
 static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
@@ -191,19 +194,142 @@ 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);
+}
+
+int send_strerror(struct command_context *cc, int err)
+{
+       return cc->use_sideband?
+               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err))
+       :
+               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err));
+}
+
+/**
+ * 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;
+       int i, ret = 0;
        char *msg = NULL;
        struct sender_command_data scd;
 
        if (cc->argc < 2) {
                for (i = 0; senders[i].name; i++) {
-                       char *tmp = make_message("%s%s\n",
-                               msg? msg : "", senders[i].name);
+                       char *tmp;
+                       ret = xasprintf(&tmp, "%s%s\n", msg? msg : "",
+                               senders[i].name);
                        free(msg);
                        msg = tmp;
                }
+               if (cc->use_sideband)
+                       return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
                ret = sc_send_buffer(&cc->scc, msg);
                free(msg);
                return ret;
@@ -213,6 +339,9 @@ int com_sender(struct command_context *cc)
                if (scd.sender_num < 0)
                        return ret;
                msg = senders[scd.sender_num].help();
+               if (cc->use_sideband)
+                       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT,
+                               false);
                ret = sc_send_buffer(&cc->scc, msg);
                free(msg);
                return ret;
@@ -245,8 +374,7 @@ int com_sender(struct command_context *cc)
 int com_si(struct command_context *cc)
 {
        int i, ret;
-       char *ut;
-       char *sender_info = NULL;
+       char *msg, *ut, *sender_info = NULL;
 
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -257,7 +385,7 @@ int com_si(struct command_context *cc)
                free(info);
        }
        ut = get_server_uptime_str(now);
-       ret = sc_send_va_buffer(&cc->scc, "version: " GIT_VERSION "\n"
+       ret = xasprintf(&msg, "version: " GIT_VERSION "\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
@@ -278,18 +406,27 @@ int com_si(struct command_context *cc)
        mutex_unlock(mmd_mutex);
        free(ut);
        free(sender_info);
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
+       ret = sc_send_bin_buffer(&cc->scc, msg, ret);
+       free(msg);
        return ret;
 }
 
 /* version */
 int com_version(struct command_context *cc)
 {
+       char *msg;
+       size_t len;
+
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
-       return sc_send_buffer(&cc->scc, VERSION_TEXT("server")
-               "built: " BUILD_DATE "\n"
-               UNAME_RS ", " CC_VERSION "\n"
-       );
+       msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS
+               ", " CC_VERSION "\n";
+       len = strlen(msg);
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true);
+       return sc_send_bin_buffer(&cc->scc, msg, len);
 }
 
 #define EMPTY_STATUS_ITEMS \
@@ -325,10 +462,16 @@ int com_version(struct command_context *cc)
  *
  * This is used by vss when currently no audio file is open.
  */
-static char *empty_status_items(int parser_friendly)
+static unsigned empty_status_items(int parser_friendly, char **result)
 {
+       static char *esi;
+       static unsigned len;
+
+       if (esi)
+               goto out;
+
        if (parser_friendly)
-               return make_message(
+               len = xasprintf(&esi,
                        #define ITEM(x) "0004 %02x:\n"
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
@@ -336,14 +479,18 @@ static char *empty_status_items(int parser_friendly)
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
                );
-       return make_message(
-               #define ITEM(x) "%s:\n"
-               EMPTY_STATUS_ITEMS
-               #undef ITEM
-               #define ITEM(x) ,status_item_list[SI_ ## x]
-               EMPTY_STATUS_ITEMS
-               #undef ITEM
-       );
+       else
+               len = xasprintf(&esi,
+                       #define ITEM(x) "%s:\n"
+                       EMPTY_STATUS_ITEMS
+                       #undef ITEM
+                       #define ITEM(x) ,status_item_list[SI_ ## x]
+                       EMPTY_STATUS_ITEMS
+                       #undef ITEM
+               );
+out:
+       *result = esi;
+       return len;
 }
 #undef EMPTY_STATUS_ITEMS
 
@@ -352,7 +499,7 @@ int com_stat(struct command_context *cc)
 {
        int i, ret;
        struct misc_meta_data tmp, *nmmd = &tmp;
-       char *s;
+       char *s, *esi = NULL;
        int32_t num = 0;
        int parser_friendly = 0;
 
@@ -382,16 +529,22 @@ int com_stat(struct command_context *cc)
                return -E_COMMAND_SYNTAX;
        for (;;) {
                mmd_dup(nmmd);
-               s = get_status(nmmd, parser_friendly);
-               ret = sc_send_buffer(&cc->scc, s);
-               free(s);
+               ret = get_status(nmmd, parser_friendly, &s);
+               if (cc->use_sideband)
+                       ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
+               else {
+                       ret = sc_send_bin_buffer(&cc->scc, s, ret);
+                       free(s);
+               }
                if (ret < 0)
                        goto out;
                if (nmmd->vss_status_flags & VSS_NEXT) {
-                       static char *esi;
-                       if (!esi)
-                               esi = empty_status_items(parser_friendly);
-                       ret = sc_send_buffer(&cc->scc, esi);
+                       ret = empty_status_items(parser_friendly, &esi);
+                       if (cc->use_sideband)
+                               ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT,
+                                       true);
+                       else
+                               ret = sc_send_bin_buffer(&cc->scc, esi, ret);
                        if (ret < 0)
                                goto out;
                } else
@@ -400,29 +553,34 @@ int com_stat(struct command_context *cc)
                if (num > 0 && !--num)
                        goto out;
                sleep(50);
+               ret = -E_SERVER_CRASH;
                if (getppid() == 1)
-                       return -E_SERVER_CRASH;
+                       goto out;
        }
 out:
+       free(esi);
        return ret;
 }
 
-static int send_list_of_commands(struct stream_cipher_context *scc, struct server_command *cmd,
+static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
                const char *handler)
 {
-       int ret, i;
+       int ret;
+       char *msg = NULL;
 
-       for (i = 1; cmd->name; cmd++, i++) {
-               char *perms = cmd_perms_itohuman(cmd->perms);
-               ret = sc_send_va_buffer(scc, "%s\t%s\t%s\t%s\n", cmd->name,
-                       handler,
-                       perms,
-                       cmd->description);
+       for (; cmd->name; cmd++) {
+               char *tmp, *perms = cmd_perms_itohuman(cmd->perms);
+               tmp = make_message("%s\t%s\t%s\t%s\n", cmd->name, handler,
+                       perms, cmd->description);
                free(perms);
-               if (ret < 0)
-                       return ret;
+               msg = para_strcat(msg, tmp);
+               free(tmp);
        }
-       return 1;
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
+       ret = sc_send_buffer(&cc->scc, msg);
+       free(msg);
+       return ret;
 }
 
 /* returns string that must be freed by the caller */
@@ -450,22 +608,21 @@ static struct server_command *get_cmd_ptr(const char *name, char **handler)
 int com_help(struct command_context *cc)
 {
        struct server_command *cmd;
-       char *perms, *handler;
+       char *perms, *handler, *buf;
        int ret;
 
        if (cc->argc < 2) {
                /* no argument given, print list of commands */
-               if ((ret = send_list_of_commands(&cc->scc, server_cmds, "server")) < 0)
+               if ((ret = send_list_of_commands(cc, server_cmds, "server")) < 0)
                        return ret;
-               return send_list_of_commands(&cc->scc, afs_cmds, "afs");
+               return send_list_of_commands(cc, afs_cmds, "afs");
        }
        /* argument given for help */
        cmd = get_cmd_ptr(cc->argv[1], &handler);
        if (!cmd)
                return -E_BAD_CMD;
        perms = cmd_perms_itohuman(cmd->perms);
-       ret = sc_send_va_buffer(&cc->scc,
-               "%s - %s\n\n"
+       ret = xasprintf(&buf, "%s - %s\n\n"
                "handler: %s\n"
                "permissions: %s\n"
                "usage: %s\n\n"
@@ -479,6 +636,10 @@ int com_help(struct command_context *cc)
        );
        free(perms);
        free(handler);
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
+       ret = sc_send_buffer(&cc->scc, buf);
+       free(buf);
        return ret;
 }
 
@@ -701,6 +862,80 @@ static void reset_signals(void)
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
+static int parse_auth_request(char *buf, int len, struct user **u,
+               bool *use_sideband)
+{
+       int ret;
+       char *p, *username, **features = NULL;
+       size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
+
+       *u = NULL;
+       *use_sideband = false;
+       if (len < auth_rq_len + 2)
+               return -E_AUTH_REQUEST;
+       if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
+               return -E_AUTH_REQUEST;
+       username = buf + auth_rq_len;
+       p = strchr(username, ' ');
+       if (p) {
+               int i;
+               if (p == username)
+                       return -E_AUTH_REQUEST;
+               *p = '\0';
+               p++;
+               create_argv(p, ",", &features);
+               for (i = 0; features[i]; i++) {
+                       if (strcmp(features[i], "sideband") == 0)
+                               *use_sideband = true;
+                       else {
+                               ret = -E_BAD_FEATURE;
+                               goto out;
+                       }
+               }
+       }
+       PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n",
+               username, *use_sideband? "true" : "false");
+       *u = lookup_user(username);
+       ret = 1;
+out:
+       free_argv(features);
+       return ret;
+}
+
+#define HANDSHAKE_BUFSIZE 4096
+
+static int parse_sb_command(struct command_context *cc, struct iovec *iov)
+{
+       int ret, i;
+       char *p, *end;
+
+       ret = -E_BAD_CMD;
+       if (iov->iov_base == NULL || iov->iov_len == 0)
+               goto out;
+       p = iov->iov_base;
+       p[iov->iov_len - 1] = '\0'; /* just to be sure */
+       cc->cmd = get_cmd_ptr(p, NULL);
+       if (!cc->cmd)
+               goto out;
+       ret = check_perms(cc->u->perms, cc->cmd);
+       if (ret < 0)
+               goto out;
+       end = iov->iov_base + iov->iov_len;
+       for (i = 0, p = iov->iov_base; p < end; i++)
+               p += strlen(p) + 1;
+       cc->argc = i;
+       cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
+       for (i = 0, p = iov->iov_base; p < end; i++) {
+               cc->argv[i] = para_strdup(p);
+               p += strlen(p) + 1;
+       }
+       cc->argv[cc->argc] = NULL;
+       ret = cc->argc;
+out:
+       free(iov->iov_base);
+       return ret;
+}
+
 /**
  * Perform user authentication and execute a command.
  *
@@ -731,10 +966,9 @@ static void reset_signals(void)
 __noreturn void handle_connect(int fd, const char *peername)
 {
        int ret;
-       char buf[4096];
        unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
-       char *p, *command = NULL;
+       char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
 
@@ -746,19 +980,17 @@ __noreturn void handle_connect(int fd, const char *peername)
                goto net_err;
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
-               PACKAGE_VERSION  ".\n" );
+               PACKAGE_VERSION  ".\n"
+               "Features: sideband,foo\n"
+       );
        if (ret < 0)
                goto net_err;
        /* recv auth request line */
-       ret = recv_buffer(fd, buf, sizeof(buf));
+       ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
-       if (ret < 10) {
-               ret = -E_AUTH_REQUEST;
-               goto net_err;
-       }
-       ret = -E_AUTH_REQUEST;
-       if (strncmp(buf, AUTH_REQUEST_MSG, strlen(AUTH_REQUEST_MSG)))
+       ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
+       if (ret < 0)
                goto net_err;
        p = buf + strlen(AUTH_REQUEST_MSG);
        PARA_DEBUG_LOG("received auth request for user %s\n", p);
@@ -781,15 +1013,29 @@ __noreturn void handle_connect(int fd, const char *peername)
        }
        PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
-       ret = write_all(fd, buf, numbytes);
-       if (ret < 0)
-               goto net_err;
-       /* recv challenge response */
-       ret = recv_bin_buffer(fd, buf, HASH_SIZE);
-       if (ret < 0)
-               goto net_err;
-       numbytes = ret;
-       PARA_DEBUG_LOG("received %d bytes challenge response\n", ret);
+       if (cc->use_sideband) {
+               struct iovec iov;
+               ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
+               buf = NULL;
+               if (ret < 0)
+                       goto net_err;
+               ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
+                       HANDSHAKE_BUFSIZE, &iov);
+               if (ret < 0)
+                       goto net_err;
+               buf = iov.iov_base;
+               numbytes = iov.iov_len;
+       } else {
+               ret = write_all(fd, buf, numbytes);
+               if (ret < 0)
+                       goto net_err;
+               /* recv challenge response */
+               ret = recv_bin_buffer(fd, buf, HASH_SIZE);
+               if (ret < 0)
+                       goto net_err;
+               numbytes = ret;
+       }
+       PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
                goto net_err;
@@ -809,27 +1055,41 @@ __noreturn void handle_connect(int fd, const char *peername)
        /* init stream cipher keys with the second part of the random buffer */
        cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
        cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN);
-       ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
-       if (ret < 0)
-               goto net_err;
-       ret = read_command(&cc->scc, &command);
-       if (ret == -E_COMMAND_SYNTAX)
-               goto err_out;
+       if (cc->use_sideband)
+               ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
+       else
+               ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
        if (ret < 0)
                goto net_err;
-       ret = -E_BAD_CMD;
-       cc->cmd = parse_cmd(command);
-       if (!cc->cmd)
-               goto err_out;
-       /* valid command, check permissions */
-       ret = check_perms(cc->u->perms, cc->cmd);
-       if (ret < 0)
-               goto err_out;
-       /* valid command and sufficient perms */
-       ret = create_argv(command, "\n", &cc->argv);
-       if (ret < 0)
-               goto err_out;
-       cc->argc = ret;
+       if (cc->use_sideband) {
+               struct iovec iov;
+               ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
+               if (ret < 0)
+                       goto net_err;
+               ret = parse_sb_command(cc, &iov);
+               if (ret < 0)
+                       goto err_out;
+               cc->argc = ret;
+       } else {
+               ret = read_command(&cc->scc, &command);
+               if (ret == -E_COMMAND_SYNTAX)
+                       goto err_out;
+               if (ret < 0)
+                       goto net_err;
+               ret = -E_BAD_CMD;
+               cc->cmd = parse_cmd(command);
+               if (!cc->cmd)
+                       goto err_out;
+               /* valid command, check permissions */
+               ret = check_perms(cc->u->perms, cc->cmd);
+               if (ret < 0)
+                       goto err_out;
+               /* valid command and sufficient perms */
+               ret = create_argv(command, "\n", &cc->argv);
+               if (ret < 0)
+                       goto err_out;
+               cc->argc = ret;
+       }
        PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name,
                cc->u->name, peername);
        ret = cc->cmd->handler(cc);
@@ -840,10 +1100,12 @@ __noreturn void handle_connect(int fd, const char *peername)
        if (ret >= 0)
                goto out;
 err_out:
-       sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+       if (send_strerror(cc, -ret) >= 0 && cc->use_sideband)
+               send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true);
 net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 out:
+       free(buf);
        free(command);
        sc_free(cc->scc.recv);
        sc_free(cc->scc.send);
@@ -852,5 +1114,12 @@ out:
                mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+       if (!cc->use_sideband)
+               exit(EXIT_SUCCESS);
+       ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
+       if (ret < 0)
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
index 05510cc..e4159e6 100644 (file)
--- a/command.h
+++ b/command.h
@@ -14,6 +14,8 @@ struct command_context {
        struct server_command *cmd;
        /** File descriptor and crypto keys. */
        struct stream_cipher_context scc;
+       /** Whether to use the sideband API for this command. */
+       bool use_sideband;
 };
 
 /**
@@ -33,3 +35,12 @@ struct server_command {
        /** 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 send_strerror(struct command_context *cc, int err);
+int recv_sb(struct stream_cipher_context *scc,
+               enum sb_designator expected_band,
+               size_t max_size, struct iovec *result);
index a57d8d7..c616b0a 100644 (file)
@@ -100,7 +100,7 @@ all_errlist_objs="mp3_afh afh_common net string signal time daemon
        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"
 
@@ -122,7 +122,7 @@ audioc_errlist_objs="audioc string net fd"
 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
@@ -142,7 +142,7 @@ writers=" file"
 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=""
 
@@ -270,7 +270,7 @@ else
                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
diff --git a/crypt.c b/crypt.c
index f33f769..b754c09 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -317,6 +317,23 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
        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;
diff --git a/crypt.h b/crypt.h
index c466669..ac11088 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -128,6 +128,36 @@ struct stream_cipher_context {
  */
 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.
  *
@@ -200,7 +230,6 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
  */
 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
 
index 5ad4d43..705c724 100644 (file)
@@ -11,8 +11,8 @@
 #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"
diff --git a/error.h b/error.h
index cfd2b1b..3100b31 100644 (file)
--- a/error.h
+++ b/error.h
@@ -39,6 +39,11 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 
 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"), \
@@ -258,6 +263,8 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
        PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
        PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
+       PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
+       PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
 
 
 #define SCHED_ERRORS \
@@ -352,7 +359,8 @@ extern const char **para_errlist[];
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \
-       PARA_ERROR(REGEX, "regular expression error") \
+       PARA_ERROR(REGEX, "regular expression error"), \
+       PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
 
 
 #define EXEC_ERRORS \
@@ -431,6 +439,7 @@ extern const char **para_errlist[];
        PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
        PARA_ERROR(BAD_USER, "auth request for invalid user"), \
+       PARA_ERROR(BAD_FEATURE, "request for unknown or invalid feature"), \
        PARA_ERROR(BAD_AUTH, "authentication failure"), \
 
 
index 5d20728..dd6afe1 100644 (file)
  * 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))
index d11e94c..926eb15 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -972,3 +972,15 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
        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);
+}
diff --git a/mood.c b/mood.c
index f580400..bafe710 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -17,6 +17,7 @@
 #include "list.h"
 #include "ipc.h"
 #include "mm.h"
+#include "sideband.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -425,8 +426,11 @@ void mood_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler
        };
 
        int ret = para_printf(&pb, "checking moods...\n");
@@ -435,7 +439,7 @@ void mood_check_callback(int fd, __a_unused const struct osl_object *query)
        osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
                check_mood);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
diff --git a/para.h b/para.h
index 0a14b99..5e0501a 100644 (file)
--- a/para.h
+++ b/para.h
@@ -131,6 +131,18 @@ _static_inline_ long int para_random(unsigned max)
        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; \
index 13c2940..48ee4ca 100644 (file)
@@ -13,6 +13,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "ipc.h"
+#include "sideband.h"
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
@@ -132,8 +133,11 @@ void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler,
        };
        int ret = para_printf(&pb, "checking playlists...\n");
 
@@ -142,7 +146,7 @@ void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
        osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
                check_playlist);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
index c9b34d1..d0e8f27 100644 (file)
@@ -4,7 +4,7 @@ HC: prototypes for the server command handlers
 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
diff --git a/sideband.c b/sideband.c
new file mode 100644 (file)
index 0000000..bf99088
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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;
+}
diff --git a/sideband.h b/sideband.h
new file mode 100644 (file)
index 0000000..4f4ed0a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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);
index aa3bcbd..7123ba1 100644 (file)
--- a/string.c
+++ b/string.c
@@ -855,6 +855,27 @@ err:
        return ret;
 }
 
+/**
+ * Find out if the given string is contained in the arg vector.
+ *
+ * \param arg The string to look for.
+ * \param argv The array to search.
+ *
+ * \return The first index whose value equals \a arg, or \p -E_ARG_NOT_FOUND if
+ * arg was not found in \a argv.
+ */
+int find_arg(const char *arg, char **argv)
+{
+       int i;
+
+       if (!argv)
+               return -E_ARG_NOT_FOUND;
+       for (i = 0; argv[i]; i++)
+               if (strcmp(arg, argv[i]) == 0)
+                       return i;
+       return -E_ARG_NOT_FOUND;
+}
+
 /**
  * Compile a regular expression.
  *
index cdc55d2..aebb651 100644 (file)
--- a/string.h
+++ b/string.h
@@ -83,6 +83,7 @@ int para_atoi32(const char *str, int32_t *value);
 int get_loglevel_by_name(const char *txt);
 int read_size_header(const char *buf);
 int create_argv(const char *buf, const char *delim, char ***result);
+int find_arg(const char *arg, char **argv);
 void free_argv(char **argv);
 int para_regcomp(regex_t *preg, const char *regex, int cflags);
 void freep(void *arg);
index b5dad0f..185ec1f 100644 (file)
--- a/wmadata.h
+++ b/wmadata.h
@@ -29,7 +29,7 @@ struct coef_vlc_table {
        const uint32_t *huffcodes;
        /** VLC bit size. */
        const uint8_t *huffbits;
-       /* Table to build run/level tables. */
+       /** Table to build run/level tables. */
        const uint16_t *levels;
 };
 
index 5106724..e58754f 100644 (file)
@@ -71,7 +71,9 @@ struct private_wmadec_data {
        int use_exp_vlc;
        /** Whether perceptual noise is added. */
        int use_noise_coding;
+       /** Depends on number of the bits per second and the frame length. */
        int byte_offset_bits;
+       /** Only used if use_exp_vlc is true. */
        struct vlc exp_vlc;
        int exponent_sizes[BLOCK_NB_SIZES];
        uint16_t exponent_bands[BLOCK_NB_SIZES][25];
@@ -502,9 +504,8 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
 
        if (pwd->use_exp_vlc) {
                PARA_INFO_LOG("using exp_vlc\n");
-               init_vlc(&pwd->exp_vlc, EXPVLCBITS,
-               sizeof(wma_scale_huffbits), wma_scale_huffbits,
-               wma_scale_huffcodes, 4);
+               init_vlc(&pwd->exp_vlc, EXPVLCBITS, sizeof(wma_scale_huffbits),
+                       wma_scale_huffbits, wma_scale_huffcodes, 4);
        } else {
                PARA_INFO_LOG("using curve\n");
                wma_lsp_to_curve_init(pwd, pwd->frame_len);