From: Andre Noll Date: Sat, 3 Dec 2011 13:42:12 +0000 (+0100) Subject: Implement client-server feature negotiation. X-Git-Tag: v0.4.11~14^2~11 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=e151dbb79eac16326585ec0a33cf48029f5f22f4 Implement client-server feature negotiation. The server announces a list of supported features and the client may request any subset of features. The only supported feature is "sideband". It has no effect at the moment but this will change in subsequent patches. TODO: Documentation, remove "foo" feature. --- diff --git a/client.h b/client.h index 8490b72f..a4234062 100644 --- a/client.h +++ b/client.h @@ -32,6 +32,10 @@ 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 configuration (including the command). */ struct client_args_info conf; /** The config file for client options. */ @@ -44,6 +48,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); diff --git a/client_common.c b/client_common.c index 5bd2241b..e34ed641 100644 --- a/client_common.c +++ b/client_common.c @@ -42,6 +42,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); @@ -152,6 +153,31 @@ static int client_recv_buffer(struct client_task *ct, fd_set *rfds, 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. * @@ -181,13 +207,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; diff --git a/command.c b/command.c index f520e3ea..cdc42fa1 100644 --- a/command.c +++ b/command.c @@ -701,6 +701,46 @@ 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; +} + /** * Perform user authentication and execute a command. * @@ -746,19 +786,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)); 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); diff --git a/command.h b/command.h index 05510cc0..851c00ba 100644 --- 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; }; /** diff --git a/error.h b/error.h index cfd2b1bd..a79cb37b 100644 --- a/error.h +++ b/error.h @@ -352,7 +352,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 +432,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"), \ diff --git a/string.c b/string.c index aa3bcbdd..7123ba1a 100644 --- 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. * diff --git a/string.h b/string.h index cdc55d2d..aebb6516 100644 --- 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);