X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=client_common.c;h=eea14fa8d3a461ce9862b38af3c35a8e807e3568;hp=eb9f9e1fcda2a49ba960582ba67be9c882897a20;hb=4744d937c4160898d1fe151257606430750e580c;hpb=bda95f9508b456dcea89d300f6d4104e30ab9f3e diff --git a/client_common.c b/client_common.c index eb9f9e1f..eea14fa8 100644 --- a/client_common.c +++ b/client_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2011 Andre Noll + * Copyright (C) 1997-2012 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -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" @@ -27,25 +28,48 @@ #define CLIENT_BUFSIZE 4000 /** - * Close the connection to para_server and free all resources. + * Close the connection to para_server and deallocate per-command ressources. * - * \param ct Pointer to the client data. + * \param ct The client task. * - * \sa client_open. + * This frees all ressources of the current command but keeps the configuration + * in \p ct->conf. + * + * \sa \ref client_close(). */ -void client_close(struct client_task *ct) +void client_disconnect(struct client_task *ct) { if (!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); + ct->scc.send = NULL; + btr_free_node(ct->btrn); + ct->btrn = NULL; +} + +/** + * Close the connection to para_server and free all resources. + * + * \param ct Pointer to the client data. + * + * \sa \ref client_open(), \ref client_disconnect(). + */ +void client_close(struct client_task *ct) +{ + if (!ct) + return; + client_disconnect(ct); free(ct->user); free(ct->config_file); free(ct->key_file); - btr_free_node(ct->btrn); client_cmdline_parser_free(&ct->conf); + free(ct->challenge_hash); + sb_free(ct->sbc); free(ct); } @@ -81,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; @@ -132,6 +157,94 @@ 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 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. * @@ -161,14 +274,19 @@ 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; - ret = send_buffer(ct->scc.fd, buf); + 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; ct->status = CL_SENT_AUTH; @@ -182,29 +300,59 @@ 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 = send_bin_buffer(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 */ { ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n); @@ -303,31 +451,49 @@ 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 (ret != -E_SERVER_EOF && ret != -E_BTR_EOF && ret != -E_EOF) PARA_ERROR_LOG("%s\n", para_strerror(-t->error)); btr_remove_node(btrn); } } -/* connect to para_server and register the client task */ -static int client_connect(struct client_task *ct) +/** + * Connect to para_server and register the client task. + * + * \param ct The initialized client task structure. + * \param s The scheduler instance to register the client task to. + * \param parent The parent node of the client btr node. + * \param child The child node of the client node. + * + * The client task structure given by \a ct must be allocated and initialized + * by \ref client_parse_config() before this function is called. + * + * \return Standard. + */ +int client_connect(struct client_task *ct, struct sched *s, + struct btr_node *parent, struct btr_node *child) { int ret; + PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg, + ct->conf.server_port_arg); ct->scc.fd = -1; ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg, ct->conf.server_port_arg); if (ret < 0) return ret; ct->scc.fd = ret; - ct->status = CL_CONNECTED; ret = mark_fd_nonblocking(ct->scc.fd); if (ret < 0) goto err_out; + ct->status = CL_CONNECTED; + ct->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "client", .parent = parent, .child = child)); ct->task.pre_select = client_pre_select; ct->task.post_select = client_post_select; + ct->task.error = 0; sprintf(ct->task.status, "client"); - register_task(&ct->task); + register_task(s, &ct->task); return 1; err_out: close(ct->scc.fd); @@ -336,40 +502,36 @@ err_out: } /** - * Open connection to para_server. + * Parse a client configuration. * * \param argc Usual argument count. * \param argv Usual argument vector. - * \param ct_ptr Points to dynamically allocated and initialized client task - * struct upon successful return. + * \param ct_ptr Filled in by this function. * \param loglevel If not \p NULL, the number of the loglevel is stored here. - * \param parent Add the new buffer tree node as a child of this node. - * \param child Add the new buffer tree node as a parent of this node. * - * Check the command line options given by \a argc and argv, set default values - * for user name and rsa key file, read further option from the config file. - * Finally, establish a connection to para_server. + * This checks the command line options given by \a argc and \a argv, sets + * default values for the user name and the name of the rsa key file and reads + * further options from the config file. * - * \return Standard. + * Upon successful return, \a ct_ptr points to a dynamically allocated and + * initialized client task struct. + * + * \return The number of non-option arguments in \a argc/argv on success, + * negative on errors. */ -int client_open(int argc, char *argv[], struct client_task **ct_ptr, - int *loglevel, struct btr_node *parent, struct btr_node *child) +int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr, + int *loglevel) { char *home = para_homedir(); int ret; struct client_task *ct = para_calloc(sizeof(struct client_task)); - ct->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = "client", .parent = parent, .child = child)); *ct_ptr = ct; ct->scc.fd = -1; ret = -E_CLIENT_SYNTAX; if (client_cmdline_parser(argc, argv, &ct->conf)) goto out; HANDLE_VERSION_FLAG("client", ct->conf); - ret = -E_CLIENT_SYNTAX; - if (!ct->conf.inputs_num) - goto out; ct->config_file = ct->conf.config_file_given? para_strdup(ct->conf.config_file_arg) : @@ -411,16 +573,52 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr, PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg); PARA_INFO_LOG("config_file: %s\n", ct->config_file); PARA_INFO_LOG("key_file: %s\n", ct->key_file); - PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg, - ct->conf.server_port_arg); - ret = client_connect(ct); + ret = ct->conf.inputs_num; out: free(home); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - btr_remove_node(ct->btrn); client_close(ct); *ct_ptr = NULL; } return ret; } + +/** + * Parse the client configuration and open a connection to para_server. + * + * \param argc See \ref client_parse_config. + * \param argv See \ref client_parse_config. + * \param ct_ptr See \ref client_parse_config. + * \param loglevel See \ref client_parse_config. + * \param parent See \ref client_connect(). + * \param child See \ref client_connect(). + * \param sched See \ref client_connect(). + * + * This function combines client_parse_config() and client_connect(). It is + * considered a syntax error if no command was given, i.e. if the number + * of non-option arguments is zero. + * + * \return Standard. + */ +int client_open(int argc, char *argv[], struct client_task **ct_ptr, + int *loglevel, struct btr_node *parent, struct btr_node *child, + struct sched *sched) +{ + int ret = client_parse_config(argc, argv, ct_ptr, loglevel); + + if (ret < 0) + return ret; + if (ret == 0) { + ret = -E_CLIENT_SYNTAX; + goto fail; + } + ret = client_connect(*ct_ptr, sched, parent, child); + if (ret < 0) + goto fail; + return 1; +fail: + client_close(*ct_ptr); + *ct_ptr = NULL; + return ret; +}