X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=client_common.c;h=a7eee85d3cc0d5cf94073ca39c6223a7c4a639d4;hp=a06aaa000b1df567914a00c65ec7cd86b00e6e46;hb=HEAD;hpb=764e71ceb1e188086c08e38dfd52d45b67dc15ba diff --git a/client_common.c b/client_common.c index a06aaa00..fe8234f9 100644 --- a/client_common.c +++ b/client_common.c @@ -1,8 +1,4 @@ -/* - * Copyright (C) 1997 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 1997 Andre Noll , see file COPYING. */ /** \file client_common.c Common functions of para_client and para_audiod. */ @@ -19,6 +15,7 @@ #include "para.h" #include "error.h" #include "list.h" +#include "lsu.h" #include "sched.h" #include "crypt.h" #include "net.h" @@ -44,7 +41,6 @@ void client_close(struct client_task *ct) if (!ct) return; free(ct->user); - free(ct->config_file); free(ct->key_file); lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR); free(ct->challenge_hash); @@ -54,15 +50,14 @@ void client_close(struct client_task *ct) } /* - * The preselect hook for server commands. + * This function asks the scheduler to monitor a file descriptor which + * corresponds to an active connection. The descriptor is monitored for either + * reading or writing, depending on the state of the connection. * - * The task pointer must contain a pointer to the initialized client data - * structure as it is returned by client_open(). - * - * This function checks the state of the connection and adds the file descriptor - * of the connection to the read or write fd set of s accordingly. + * The context pointer is assumed to refer to a client task structure that was + * initialized earlier by client_open(). */ -static void client_pre_select(struct sched *s, void *context) +static void client_pre_monitor(struct sched *s, void *context) { int ret; struct client_task *ct = context; @@ -73,13 +68,13 @@ static void client_pre_select(struct sched *s, void *context) case CL_CONNECTED: case CL_SENT_AUTH: case CL_SENT_CH_RESPONSE: - para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno); + sched_monitor_readfd(ct->scc.fd, s); return; case CL_RECEIVED_WELCOME: case CL_RECEIVED_PROCEED: case CL_RECEIVED_CHALLENGE: - para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno); + sched_monitor_writefd(ct->scc.fd, s); return; case CL_SENDING: @@ -88,16 +83,16 @@ static void client_pre_select(struct sched *s, void *context) if (ret < 0) sched_min_delay(s); else if (ret > 0) - para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno); + sched_monitor_writefd(ct->scc.fd, s); } - /* fall though */ + __attribute__ ((fallthrough)); case CL_EXECUTING: if (ct->btrn[0]) { ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT); if (ret < 0) sched_min_delay(s); else if (ret > 0) - para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno); + sched_monitor_readfd(ct->scc.fd, s); } return; } @@ -130,8 +125,7 @@ static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbyt return 0; } -static int recv_sb(struct client_task *ct, fd_set *rfds, - struct sb_buffer *result) +static int recv_sb(struct client_task *ct, struct sb_buffer *result) { int ret; size_t n; @@ -139,8 +133,6 @@ static int recv_sb(struct client_task *ct, fd_set *rfds, 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 { @@ -151,7 +143,7 @@ static int recv_sb(struct client_task *ct, fd_set *rfds, ct->sbc[0] = sb_new_recv(0, trafo, trafo_context); again: sb_get_recv_buffer(ct->sbc[0], &iov); - ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n); + ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, &n); if (ret < 0) { sb_free(ct->sbc[0]); ct->sbc[0] = NULL; @@ -253,7 +245,7 @@ static int send_sb_command(struct client_task *ct) for (i = 0; i < num_inputs; i++) len += strlen(lls_input(i, ct->lpr)) + 1; - p = command = para_malloc(len); + p = command = alloc(len); for (i = 0; i < num_inputs; i++) { const char *str = lls_input(i, ct->lpr); strcpy(p, str); @@ -263,15 +255,29 @@ static int send_sb_command(struct client_task *ct) return send_sb(ct, 0, command, len, SBD_COMMAND, false); } +/* Find out if the given string is contained in the features vector. */ +static bool has_feature(const char *feature, struct client_task *ct) +{ + if (!ct->features) + return false; + for (int i = 0; ct->features[i]; i++) + if (strcmp(feature, ct->features[i]) == 0) + return true; + return false; +} + /* - * The post select hook for client commands. + * This function reads or writes to the socket file descriptor which + * corresponds to an established connection between the client and the server. + * It depends on the current state of the connection and on the readiness of + * the socket file descriptor which type of I/O is going to be performed. + * Besides the initial handshake and authentication, the function sends the + * server command and receives the output from the server, if any. * - * Depending on the current state of the connection and the status of the read - * and write fd sets of s, this function performs the necessary steps to - * authenticate the connection, to send the command given by t->private_data - * and to receive para_server's output, if any. + * The context pointer refers to a client task structure that was initialized + * earlier by client_open(). */ -static int client_post_select(struct sched *s, void *context) +static int client_post_monitor(struct sched *s, void *context) { struct client_task *ct = context; int ret = 0; @@ -285,23 +291,32 @@ static int client_post_select(struct sched *s, void *context) return 0; switch (ct->status) { case CL_CONNECTED: /* receive welcome message */ - ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n); + ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &n); if (ret < 0 || n == 0) goto out; ct->features = parse_features(buf); ct->status = CL_RECEIVED_WELCOME; return 0; case CL_RECEIVED_WELCOME: /* send auth command */ - if (!FD_ISSET(ct->scc.fd, &s->wfds)) + { + /* + * Use sha256 iff the server announced the feature. After 0.7.0 + * we may request and use the feature unconditionally. After + * 0.8.0 we no longer need to request the feature. + */ + bool has_sha256; + if (!sched_write_ok(ct->scc.fd, s)) return 0; - sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128", - ct->user); + has_sha256 = has_feature("sha256", ct); + sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256? + " sha256" : ""); PARA_INFO_LOG("--> %s\n", buf); ret = write_buffer(ct->scc.fd, buf); if (ret < 0) goto out; ct->status = CL_SENT_AUTH; return 0; + } case CL_SENT_AUTH: /* * Receive challenge and session keys, decrypt the challenge and @@ -312,7 +327,7 @@ static int client_post_select(struct sched *s, void *context) unsigned char crypt_buf[1024]; struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); + ret = recv_sb(ct, &sbb); if (ret <= 0) goto out; if (sbb.band != SBD_CHALLENGE) { @@ -322,24 +337,36 @@ static int client_post_select(struct sched *s, void *context) } n = sbb.iov.iov_len; PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n); - ret = priv_decrypt(ct->key_file, crypt_buf, + ret = apc_priv_decrypt(ct->key_file, crypt_buf, sbb.iov.iov_base, n); free(sbb.iov.iov_base); 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(ct->challenge_hash, buf); + ct->challenge_hash = alloc(HASH2_SIZE); + if (has_feature("sha256", ct)) { + hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, + ct->challenge_hash); + hash2_to_asc(ct->challenge_hash, buf); + } else { + hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, + ct->challenge_hash); + hash_to_asc(ct->challenge_hash, buf); + } + ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, + SESSION_KEY_LEN); + ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + + SESSION_KEY_LEN, SESSION_KEY_LEN); PARA_INFO_LOG("--> %s\n", buf); ct->status = CL_RECEIVED_CHALLENGE; return 0; } case CL_RECEIVED_CHALLENGE: - ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, - SBD_CHALLENGE_RESPONSE, false); + if (has_feature("sha256", ct)) + ret = send_sb(ct, 0, ct->challenge_hash, HASH2_SIZE, + SBD_CHALLENGE_RESPONSE, false); + else + ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, + SBD_CHALLENGE_RESPONSE, false); if (ret != 0) ct->challenge_hash = NULL; if (ret <= 0) @@ -349,7 +376,7 @@ static int client_post_select(struct sched *s, void *context) case CL_SENT_CH_RESPONSE: /* read server response */ { struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); + ret = recv_sb(ct, &sbb); if (ret <= 0) goto out; free(sbb.iov.iov_base); @@ -361,7 +388,7 @@ static int client_post_select(struct sched *s, void *context) } case CL_RECEIVED_PROCEED: /* concat args and send command */ { - if (!FD_ISSET(ct->scc.fd, &s->wfds)) + if (!sched_write_ok(ct->scc.fd, s)) return 0; ret = send_sb_command(ct); if (ret <= 0) @@ -374,16 +401,16 @@ static int client_post_select(struct sched *s, void *context) char *buf2; size_t sz; ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF); - if (ret == -E_BTR_EOF) { + if (ret == -E_EOF) { /* empty blob data packet indicates EOF */ PARA_INFO_LOG("blob sent\n"); ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true); if (ret >= 0) - ret = -E_BTR_EOF; + ret = -E_EOF; } if (ret < 0) goto close1; - if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) { + if (ret > 0 && sched_write_ok(ct->scc.fd, s)) { sz = btr_next_buffer(ct->btrn[1], &buf2); assert(sz); ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true); @@ -393,15 +420,15 @@ static int client_post_select(struct sched *s, void *context) btr_consume(ct->btrn[1], sz); } } - /* fall through */ + __attribute__ ((fallthrough)); case CL_EXECUTING: if (ct->btrn[0]) { ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT); if (ret < 0) goto close0; - if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) { + if (ret > 0 && sched_read_ok(ct->scc.fd, s)) { struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); + ret = recv_sb(ct, &sbb); if (ret < 0) goto close0; if (ret > 0) { @@ -430,8 +457,7 @@ out: return 0; btr_remove_node(&ct->btrn[0]); btr_remove_node(&ct->btrn[1]); - if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE) - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + PARA_NOTICE_LOG("closing connection (%s)\n", para_strerror(-ret)); if (ct->scc.fd >= 0) { close(ct->scc.fd); ct->scc.fd = -1; @@ -467,7 +493,7 @@ int client_connect(struct client_task *ct, struct sched *s, PARA_NOTICE_LOG("connecting %s:%u\n", host, port); ct->scc.fd = -1; - ret = para_connect_simple(IPPROTO_TCP, host, port); + ret = para_connect(IPPROTO_TCP, host, port); if (ret < 0) return ret; ct->scc.fd = ret; @@ -482,8 +508,8 @@ int client_connect(struct client_task *ct, struct sched *s, ct->task = task_register(&(struct task_info) { .name = "client", - .pre_select = client_pre_select, - .post_select = client_post_select, + .pre_monitor = client_pre_monitor, + .post_monitor = client_post_monitor, .context = ct, }, s); return 1; @@ -530,78 +556,47 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr, int *loglevel) { const struct lls_command *cmd = CLIENT_CMD_PTR; - void *map; - size_t sz; struct lls_parse_result *lpr; int ret, ll; struct client_task *ct; - char *cf = NULL, *kf = NULL, *user, *errctx, *home = para_homedir(); + char *kf = NULL, *user, *errctx, *home = para_homedir(); ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx)); if (ret < 0) goto out; - ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr); version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr)); handle_help_flag(lpr); - if (CLIENT_OPT_GIVEN(CONFIG_FILE, lpr)) - cf = para_strdup(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr)); - else - cf = make_message("%s/.paraslash/client.conf", home); - ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); - if (ret < 0) { - if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) - goto out; - if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && - CLIENT_OPT_GIVEN(CONFIG_FILE, lpr)) - goto out; - } else { - int cf_argc; - char **cf_argv; - struct lls_parse_result *cf_lpr, *merged_lpr; - ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx)); - para_munmap(map, sz); - if (ret < 0) - goto out; - cf_argc = ret; - ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx)); - lls_free_argv(cf_argv); - if (ret < 0) - goto out; - ret = lls(lls_merge(lpr, cf_lpr, cmd, &merged_lpr, - &errctx)); - lls_free_parse_result(cf_lpr, cmd); - if (ret < 0) - goto out; - lls_free_parse_result(lpr, cmd); - lpr = merged_lpr; - } + ret = lsu_merge_config_file_options(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr), + "client.conf", &lpr, cmd, client_suite, 0U /* default flags */); + if (ret < 0) + goto out; /* success */ + ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr); + if (loglevel) + *loglevel = ll; user = CLIENT_OPT_GIVEN(USER, lpr)? para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname(); if (CLIENT_OPT_GIVEN(KEY_FILE, lpr)) kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr)); else { + struct stat statbuf; kf = make_message("%s/.paraslash/key.%s", home, user); - if (!file_exists(kf)) { + if (stat(kf, &statbuf) != 0) { /* assume file does not exist */ free(kf); kf = make_message("%s/.ssh/id_rsa", home); } } PARA_INFO_LOG("user: %s\n", user); - PARA_INFO_LOG("config file: %s\n", cf); PARA_INFO_LOG("key file: %s\n", kf); PARA_INFO_LOG("loglevel: %d\n", ll); - ct = para_calloc(sizeof(*ct)); + ct = zalloc(sizeof(*ct)); ct->scc.fd = -1; ct->lpr = lpr; ct->key_file = kf; - ct->config_file = cf; ct->user = user; *ct_ptr = ct; - if (loglevel) - *loglevel = ll; ret = lls_num_inputs(lpr); out: free(home); @@ -609,9 +604,7 @@ out: if (errctx) PARA_ERROR_LOG("%s\n", errctx); free(errctx); - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); lls_free_parse_result(lpr, cmd); - free(cf); free(kf); *ct_ptr = NULL; }