]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 30 Aug 2015 11:22:19 +0000 (13:22 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 30 Aug 2015 11:22:19 +0000 (13:22 +0200)
* maint:
  lsatt: Fix sort order.
  server: Avoid segfault in com_sender().

1  2 
attribute.c
command.c

diff --combined attribute.c
index e73a0e76db5d9ed4052480bb89d360d44d57dc91,5672309ada3b65dfadad0f1a3ba7f4881e52740c..a28c92e9c133d80accd97cd884a3d267da63b034
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -162,7 -162,7 +162,7 @@@ static void com_lsatt_callback(int fd, 
        };
        struct pattern_match_data pmd = {
                .table = attribute_table,
-               .loop_col_num = ATTCOL_BITNUM,
+               .loop_col_num = ATTCOL_NAME,
                .match_col_num = ATTCOL_NAME,
                .patterns = {.data = (char *)query->data + sizeof(laad.flags),
                        .size = query->size - sizeof(laad.flags)},
                .action = print_attribute
        };
        if (laad.flags & LSATT_FLAG_SORT_BY_ID)
-               pmd.loop_col_num = ATTCOL_NAME;
+               pmd.loop_col_num = ATTCOL_BITNUM;
        if (laad.flags & LSATT_FLAG_REVERSE)
                pmd.pm_flags |= PM_REVERSE_LOOP;
        for_each_matching_row(&pmd);
@@@ -212,10 -212,85 +212,10 @@@ int com_lsatt(struct command_context *c
        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");
 +              ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n");
        return ret;
  }
  
 -static void com_setatt_callback(__a_unused int fd, const struct osl_object *query)
 -{
 -      char *p;
 -      uint64_t add_mask = 0, del_mask = 0;
 -      int ret;
 -      size_t len;
 -      struct osl_object obj;
 -      struct osl_row *row;
 -
 -      for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
 -              char c;
 -
 -              len = strlen(p);
 -              ret = -E_ATTR_SYNTAX;
 -              if (!*p)
 -                      goto out;
 -              c = p[len - 1];
 -              if (c != '+' && c != '-')
 -                      break;
 -              p[len - 1] = '\0';
 -              obj.data = p;
 -              obj.size = len + 1;
 -              ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
 -              if (ret < 0)
 -                      goto out;
 -              ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM,
 -                      &obj));
 -              if (ret < 0)
 -                      goto out;
 -              if (c == '+')
 -                      add_mask |= (1UL << *(unsigned char *)obj.data);
 -              else
 -                      del_mask |= (1UL << *(unsigned char *)obj.data);
 -      }
 -      ret = -E_ATTR_SYNTAX;
 -      if (!add_mask && !del_mask)
 -              goto out;
 -      PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask,
 -              (long long unsigned)del_mask);
 -      for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */
 -              struct afs_info old_afsi, new_afsi;
 -              struct afsi_change_event_data aced = {.old_afsi = &old_afsi};
 -
 -              len = strlen(p);
 -              ret = aft_get_row_of_path(p, &aced.aft_row);
 -              if (ret < 0)
 -                      goto out;
 -              ret = get_afsi_object_of_row(aced.aft_row, &obj);
 -              if (ret < 0)
 -                      goto out;
 -              ret = load_afsi(&old_afsi, &obj);
 -              if (ret < 0)
 -                      goto out;
 -              new_afsi = old_afsi;
 -              new_afsi.attributes |= add_mask;
 -              new_afsi.attributes &= ~del_mask;
 -              save_afsi(&new_afsi, &obj); /* in-place update */
 -              afs_event(AFSI_CHANGE, NULL, &aced);
 -      }
 -out:
 -      if (ret < 0)
 -              PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 -}
 -
 -int com_setatt(struct command_context *cc)
 -{
 -      if (cc->argc < 3)
 -              return -E_ATTR_SYNTAX;
 -      return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
 -              com_setatt_callback, NULL, NULL);
 -}
 -
  struct addatt_event_data {
        const char *name;
        unsigned char bitnum;
@@@ -355,7 -430,7 +355,7 @@@ int com_mvatt(struct command_context *c
  struct remove_attribute_action_data {
        /** Message buffer. */
        struct para_buffer pb;
 -      /** Numver of attributes removed. */
 +      /** Number of attributes removed. */
        int num_removed;
        /** Bitwise "or" of the removed attributes. */
        uint64_t mask_of_removed_atts;
@@@ -438,7 -513,7 +438,7 @@@ int com_rmatt(struct command_context *c
   * used for unset attributes.
   *
   * In practice, not all 64 attributes are defined. In this case, the function
 - * only prints \a N + 1 charaters where \a N is the greatest id of a defined
 + * only prints \a N + 1 characters where \a N is the greatest id of a defined
   * attribute.
   */
  void get_attribute_bitmap(const uint64_t *atts, char *buf)
diff --combined command.c
index eac09f056af21cbfc9c5b4994c7150f200606ae5,f1f00fca8a29add0e35a2e415a927bdeb75c4376..c378563379777fe3c7af5f1bc352871f9b6dbd55
+++ b/command.c
@@@ -1,20 -1,15 +1,20 @@@
  /*
 - * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
  
  /** \file command.c Client authentication and server commands. */
  
 +#include <netinet/in.h>
 +#include <sys/socket.h>
  #include <regex.h>
  #include <signal.h>
  #include <sys/types.h>
  #include <osl.h>
 +#include <arpa/inet.h>
 +#include <sys/un.h>
 +#include <netdb.h>
  
  #include "para.h"
  #include "error.h"
  #include "fd.h"
  #include "ipc.h"
  #include "user_list.h"
 -#include "server_command_list.h"
 -#include "afs_command_list.h"
 +#include "server.command_list.h"
 +#include "afs.command_list.h"
  #include "signal.h"
  #include "version.h"
  
 -struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
 -struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
 +typedef int server_command_handler_t(struct command_context *);
 +static server_command_handler_t SERVER_COMMAND_HANDLERS;
 +server_command_handler_t AFS_COMMAND_HANDLERS;
 +
 +/* Defines one command of para_server. */
 +struct server_command {
 +      /* The name of the command. */
 +      const char *name;
 +      /* Pointer to the function that handles the command. */
 +      server_command_handler_t *handler;
 +      /* The privileges a user must have to execute this command. */
 +      unsigned int perms;
 +      /* One-line description of the command. */
 +      const char *description;
 +      /* Summary of the command line options. */
 +      const char *usage;
 +      /* The long help text. */
 +      const char *help;
 +};
 +
 +static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
 +static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
  
  /** Commands including options must be shorter than this. */
  #define MAX_COMMAND_LEN 32768
@@@ -132,15 -107,22 +132,15 @@@ static char *vss_get_status_flags(unsig
  static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
                char **result)
  {
 -      char mtime[30] = "";
        char *status, *flags; /* vss status info */
        /* nobody updates our version of "now" */
 -      char *ut = get_server_uptime_str(NULL);
        long offset = (nmmd->offset + 500) / 1000;
        struct timeval current_time;
 -      struct tm mtime_tm;
        struct para_buffer b = {.flags = parser_friendly? PBF_SIZE_PREFIX : 0};
  
        /* report real status */
        status = vss_status_tohuman(nmmd->vss_status_flags);
        flags = vss_get_status_flags(nmmd->vss_status_flags);
 -      if (nmmd->size) { /* parent currently has an audio file open */
 -              localtime_r(&nmmd->mtime, &mtime_tm);
 -              strftime(mtime, 29, "%b %d %Y", &mtime_tm);
 -      }
        clock_get_realtime(&current_time);
        /*
         * The calls to WRITE_STATUS_ITEM() below never fail because
         * is not smart enough to prove this and complains nevertheless.
         * Casting the return value to void silences clang.
         */
 -      (void)WRITE_STATUS_ITEM(&b, SI_FILE_SIZE, "%zu\n", nmmd->size / 1024);
 -      (void)WRITE_STATUS_ITEM(&b, SI_MTIME, "%s\n", mtime);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS_FLAGS, "%s\n", flags);
        (void)WRITE_STATUS_ITEM(&b, SI_OFFSET, "%li\n", offset);
                (long unsigned)current_time.tv_usec);
        free(flags);
        free(status);
 -      free(ut);
        *result = b.buf;
        return b.offset;
  }
@@@ -171,7 -156,7 +171,7 @@@ static int check_sender_args(int argc, 
        const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
  
        scd->sender_num = -1;
-       if (argc < 2)
+       if (argc < 3)
                return -E_COMMAND_SYNTAX;
        for (i = 0; senders[i].name; i++)
                if (!strcmp(senders[i].name, argv[1]))
   *
   * The nonblock flag must be disabled for the file descriptor given by \a scc.
   *
 - * Stream cipher encryption is automatically activated if neccessary via the
 + * Stream cipher encryption is automatically activated if necessary via the
   * sideband transformation, depending on the value of \a band.
   *
   * \return Standard.
@@@ -235,8 -220,8 +235,8 @@@ int send_sb(struct stream_cipher_contex
        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;
 +      struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
  
        sbc = sb_new_send(&sbb, dont_free, trafo, scc->send);
        do {
@@@ -283,7 -268,10 +283,7 @@@ __printf_3_4 int send_sb_va(struct stre
   */
  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));
 +      return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
  }
  
  /**
@@@ -350,17 -338,23 +350,17 @@@ static int com_sender(struct command_co
                        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;
 +              return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
        }
        ret = check_sender_args(cc->argc, cc->argv, &scd);
        if (ret < 0) {
                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;
 +              if (strcmp(cc->argv[2], "status") == 0)
 +                      msg = senders[scd.sender_num].status();
 +              else
 +                      msg = senders[scd.sender_num].help();
 +              return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
        }
  
        switch (scd.cmd_num) {
        for (i = 0; i < 10; i++) {
                mutex_lock(mmd_mutex);
                if (mmd->sender_cmd_data.cmd_num >= 0) {
 +                      /* another sender command is active, retry in 100ms */
 +                      struct timespec ts = {.tv_nsec = 100 * 1000 * 1000};
                        mutex_unlock(mmd_mutex);
 -                      usleep(100 * 1000);
 +                      nanosleep(&ts, NULL);
                        continue;
                }
 -              memcpy(&mmd->sender_cmd_data, &scd, sizeof(scd));
 +              mmd->sender_cmd_data = scd;
                mutex_unlock(mmd_mutex);
                break;
        }
  /* server info */
  static int com_si(struct command_context *cc)
  {
 -      int i, ret;
 -      char *msg, *ut, *sender_info = NULL;
 +      int ret;
 +      char *msg, *ut;
  
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
 -      for (i = 0; senders[i].name; i++) {
 -              char *info = senders[i].info();
 -              sender_info = para_strcat(sender_info, info);
 -              free(info);
 -      }
 -      ut = get_server_uptime_str(now);
 +      ut = daemon_get_uptime_str(now);
        ret = xasprintf(&msg,
 -              "version: %s\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
                "current loglevel: %s\n"
 -              "supported audio formats: %s\n"
 -              "%s",
 -              version_git(),
 +              "supported audio formats: %s\n",
                ut, mmd->num_played,
                (int)getppid(),
                (int)mmd->afs_pid,
                mmd->num_commands,
                mmd->num_connects,
                conf.loglevel_arg,
 -              AUDIO_FORMAT_HANDLERS,
 -              sender_info
 +              AUDIO_FORMAT_HANDLERS
        );
        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;
 +      return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
  }
  
  /* version */
@@@ -428,7 -434,9 +428,7 @@@ static int com_version(struct command_c
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
        len = xasprintf(&msg, "%s", version_text("server"));
 -      if (cc->use_sideband)
 -              return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
 -      return sc_send_bin_buffer(&cc->scc, msg, len);
 +      return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
  }
  
  /** These status items are cleared if no audio file is currently open. */
@@@ -533,13 -541,24 +533,13 @@@ static int com_stat(struct command_cont
        for (;;) {
                mmd_dup(nmmd);
                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);
 -              }
 +              ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
                if (ret < 0)
                        goto out;
                if (nmmd->vss_status_flags & VSS_NEXT) {
                        char *esi;
                        ret = empty_status_items(parser_friendly, &esi);
 -                      if (cc->use_sideband)
 -                              ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT,
 -                                      false);
 -                      else {
 -                              ret = sc_send_bin_buffer(&cc->scc, esi, ret);
 -                              free(esi);
 -                      }
 +                      ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false);
                        if (ret < 0)
                                goto out;
                } else
@@@ -559,6 -578,7 +559,6 @@@ out
  static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
                const char *handler)
  {
 -      int ret;
        char *msg = NULL;
  
        for (; cmd->name; cmd++) {
                msg = para_strcat(msg, tmp);
                free(tmp);
        }
 -      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;
 +      assert(msg);
 +      return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
  }
  
  /* returns string that must be freed by the caller */
@@@ -626,7 -649,11 +626,7 @@@ static int com_help(struct command_cont
        );
        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;
 +      return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
  }
  
  /* hup */
@@@ -735,7 -762,7 +735,7 @@@ static int com_ff(struct command_contex
                promille += 1000 * i / mmd->afd.afhi.seconds_total;
        if (promille < 0)
                promille = 0;
 -      if (promille >  1000) {
 +      if (promille > 1000) {
                mmd->new_vss_status_flags |= VSS_NEXT;
                goto out;
        }
@@@ -766,8 -793,8 +766,8 @@@ static int com_jmp(struct command_conte
        if (i > 100)
                i = 100;
        PARA_INFO_LOG("jumping to %lu%%\n", i);
 -      mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50)/ 100;
 -      PARA_INFO_LOG("sent: %lu,  offset before jmp: %lu\n",
 +      mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100;
 +      PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
                mmd->chunks_sent, mmd->offset);
        mmd->new_vss_status_flags |= VSS_REPOS;
        mmd->new_vss_status_flags &= ~VSS_NEXT;
@@@ -778,26 -805,68 +778,26 @@@ out
        return ret;
  }
  
 +static int com_tasks(struct command_context *cc)
 +{
 +      char *tl = server_get_tasks();
 +      int ret = 1;
 +
 +      if (tl)
 +              ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
 +      return ret;
 +}
 +
  /*
   * check if perms are sufficient to exec a command having perms cmd_perms.
   * Returns 0 if perms are sufficient, -E_PERM otherwise.
   */
 -static int check_perms(unsigned int perms, struct server_command *cmd_ptr)
 +static int check_perms(unsigned int perms, const struct server_command *cmd_ptr)
  {
        PARA_DEBUG_LOG("checking permissions\n");
        return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
  }
  
 -/*
 - * Parse first string from *cmd and lookup in table of valid commands.
 - * On error, NULL is returned.
 - */
 -static struct server_command *parse_cmd(const char *cmdstr)
 -{
 -      char buf[255];
 -      int n = 0;
 -
 -      sscanf(cmdstr, "%200s%n", buf, &n);
 -      if (!n)
 -              return NULL;
 -      buf[n] = '\0';
 -      return get_cmd_ptr(buf, NULL);
 -}
 -
 -static int read_command(struct stream_cipher_context *scc, char **result)
 -{
 -      int ret;
 -      char buf[4096];
 -      char *command = NULL;
 -
 -      for (;;) {
 -              size_t numbytes;
 -              char *p;
 -
 -              ret = sc_recv_buffer(scc, buf, sizeof(buf));
 -              if (ret < 0)
 -                      goto out;
 -              if (!ret)
 -                      break;
 -              numbytes = ret;
 -              ret = -E_COMMAND_SYNTAX;
 -              if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */
 -                      goto out;
 -              command = para_strcat(command, buf);
 -              p = strstr(command, EOC_MSG);
 -              if (p) {
 -                      *p = '\0';
 -                      break;
 -              }
 -      }
 -      ret = command? 1 : -E_COMMAND_SYNTAX;
 -out:
 -      if (ret < 0)
 -              free(command);
 -      else
 -              *result = command;
 -      return ret;
 -
 -}
 -
  static void reset_signals(void)
  {
        para_sigaction(SIGCHLD, SIG_IGN);
        para_sigaction(SIGHUP, SIG_DFL);
  }
  
 +struct connection_features {
 +      bool sideband_requested;
 +      bool aes_ctr128_requested;
 +};
 +
  static int parse_auth_request(char *buf, int len, struct user **u,
 -              bool *use_sideband)
 +              struct connection_features *cf)
  {
        int ret;
        char *p, *username, **features = NULL;
        size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
  
        *u = NULL;
 -      *use_sideband = false;
 +      memset(cf, 0, sizeof(*cf));
        if (len < auth_rq_len + 2)
                return -E_AUTH_REQUEST;
        if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
                create_argv(p, ",", &features);
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
 -                              *use_sideband = true;
 +                              cf->sideband_requested = true;
 +                      else if (strcmp(features[i], "aes_ctr128") == 0)
 +                              cf->aes_ctr128_requested = 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");
 +      PARA_DEBUG_LOG("received auth request for user %s\n", username);
        *u = lookup_user(username);
        ret = 1;
  out:
  
  #define HANDSHAKE_BUFSIZE 4096
  
 -static int parse_sb_command(struct command_context *cc, struct iovec *iov)
 +static int run_command(struct command_context *cc, struct iovec *iov,
 +              const char *peername)
  {
        int ret, i;
        char *p, *end;
 +      struct server_command *cmd;
  
 -      ret = -E_BAD_CMD;
        if (iov->iov_base == NULL || iov->iov_len == 0)
 -              goto out;
 +              return -E_BAD_CMD;
        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);
 +      cmd = get_cmd_ptr(p, NULL);
 +      if (!cmd)
 +              return -E_BAD_CMD;
 +      ret = check_perms(cc->u->perms, cmd);
        if (ret < 0)
 -              goto out;
 +              return ret;
        end = iov->iov_base + iov->iov_len;
 -      for (i = 0, p = iov->iov_base; p < end; i++)
 +      for (i = 0; p < end; i++)
                p += strlen(p) + 1;
        cc->argc = i;
        cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
                p += strlen(p) + 1;
        }
        cc->argv[cc->argc] = NULL;
 -      ret = cc->argc;
 -out:
 -      free(iov->iov_base);
 +      PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
 +              cc->u->name, peername);
 +      ret = cmd->handler(cc);
 +      free_argv(cc->argv);
 +      mutex_lock(mmd_mutex);
 +      mmd->num_commands++;
 +      if (ret >= 0 && (cmd->perms & AFS_WRITE))
 +              mmd->events++;
 +      mutex_unlock(mmd_mutex);
        return ret;
  }
  
   * \param fd The file descriptor to send output to.
   * \param peername Identifies the connecting peer.
   *
 - * Whenever para_server accepts an incoming tcp connection on
 - * the port it listens on, it forks and the resulting child
 - * calls this function.
 + * Whenever para_server accepts an incoming tcp connection on the port it
 + * listens on, it forks and the resulting child calls this function.
   *
 - * An RSA-based challenge/response is used to authenticate
 - * the peer. It that authentication succeeds, a random
 - * session key is generated and sent back to the peer,
 - * encrypted with its RSA public key.  From this point on,
 - * all transfers are crypted with this session key.
 + * An RSA-based challenge/response is used to authenticate the peer. It that
 + * authentication succeeds, a random session key is generated and sent back to
 + * the peer, encrypted with its RSA public key. From this point on, all
 + * transfers are crypted with this session key.
   *
 - * Next it is checked if the peer supplied  a valid server command or a command
 - * for the audio file selector.  If yes, and if the user has sufficient
 + * Next it is checked if the peer supplied a valid server command or a command
 + * for the audio file selector. If yes, and if the user has sufficient
   * permissions to execute that command, the function calls the corresponding
   * command handler which does argument checking and further processing.
   *
 - * In order to cope with a DOS attacks, a timeout is set up
 - * which terminates the function if the connection was not
 - * authenticated when the timeout expires.
 + * In order to cope with a DOS attacks, a timeout is set up which terminates
 + * the function if the connection was not authenticated when the timeout
 + * expires.
   *
   * \sa alarm(2), crypt.c, crypt.h
   */
@@@ -923,11 -981,9 +923,11 @@@ __noreturn void handle_connect(int fd, 
        int ret;
        unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
 -      char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
 +      char *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;
 +      struct iovec iov;
 +      struct connection_features cf;
  
        cc->scc.fd = fd;
        reset_signals();
                goto net_err;
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
 -              PACKAGE_VERSION  ".\n"
 -              "Features: sideband\n"
 +              PACKAGE_VERSION ".\n"
 +              "Features: sideband,aes_ctr128\n"
        );
        if (ret < 0)
                goto net_err;
        ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
 -      ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
 +      ret = parse_auth_request(buf, ret, &cc->u, &cf);
        if (ret < 0)
                goto net_err;
 -      p = buf + strlen(AUTH_REQUEST_MSG);
 -      PARA_DEBUG_LOG("received auth request for user %s\n", p);
 -      cc->u = lookup_user(p);
 +      if (!cf.sideband_requested) { /* sideband is mandatory */
 +              PARA_ERROR_LOG("client did not request sideband\n");
 +              ret = -E_BAD_FEATURE;
 +              goto net_err;
 +      }
        if (cc->u) {
                get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
                ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
                numbytes = 256;
                get_random_bytes_or_die((unsigned char *)buf, numbytes);
        }
 -      PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
 +      PARA_DEBUG_LOG("sending %u byte challenge + session key (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
 -      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;
 -      }
 +      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;
        PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
        /* 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);
 -      if (cc->use_sideband)
 -              ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
 -      else
 -              ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
 +      cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN,
 +              cf.aes_ctr128_requested);
 +      cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
 +              SESSION_KEY_LEN, cf.aes_ctr128_requested);
 +      ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
 -      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);
 -      free_argv(cc->argv);
 -      mutex_lock(mmd_mutex);
 -      mmd->num_commands++;
 -      mutex_unlock(mmd_mutex);
 +      ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
 +      if (ret < 0)
 +              goto net_err;
 +      ret = run_command(cc, &iov, peername);
 +      free(iov.iov_base);
 +      if (ret < 0)
 +              goto err_out;
        if (ret >= 0)
                goto out;
  err_out:
 -      if (send_strerror(cc, -ret) >= 0 && cc->use_sideband)
 +      if (send_strerror(cc, -ret) >= 0)
                send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true);
  net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
@@@ -1025,9 -1121,11 +1025,9 @@@ out
        free(buf);
        free(command);
        mutex_lock(mmd_mutex);
 -      if (cc->cmd && (cc->cmd->perms & AFS_WRITE) && ret >= 0)
 -              mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
 -      if (ret >= 0 && cc->use_sideband) {
 +      if (ret >= 0) {
                ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
                if (ret < 0)
                        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));