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 configuration (including the command). */
struct client_args_info conf;
/** The config file for client options. */
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);
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);
return 0;
}
+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 bool has_feature(const char *feature, struct client_task *ct)
+{
+ return find_arg(feature, ct->features) >= 0? true : false;
+}
+
/**
* The post select hook for client commands.
*
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;
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;
+}
+
/**
* Perform user authentication and execute a command.
*
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));
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);
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;
};
/**
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 \
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"), \
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.
*
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);