+ struct client_task *ct = context;
+ int ret = 0;
+ size_t n;
+ char buf[CLIENT_BUFSIZE];
+
+ ret = task_get_notification(ct->task);
+ if (ret < 0)
+ goto out;
+ if (ct->scc.fd < 0)
+ return 0;
+ switch (ct->status) {
+ case CL_CONNECTED: /* receive welcome message */
+ ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &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))
+ return 0;
+ sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
+ has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
+ 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
+ * send back the hash of the decrypted challenge.
+ */
+ {
+ /* decrypted challenge/session key buffer */
+ unsigned char crypt_buf[1024];
+ struct sb_buffer sbb;
+ bool use_aes;
+
+ 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;
+ ct->challenge_hash = para_malloc(HASH_SIZE);
+ hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
+ use_aes = has_feature("aes_ctr128", ct);
+ ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
+ ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+ SESSION_KEY_LEN, use_aes);
+ hash_to_asc(ct->challenge_hash, buf);
+ 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 (ret != 0)
+ ct->challenge_hash = NULL;
+ if (ret <= 0)
+ goto out;
+ ct->status = CL_SENT_CH_RESPONSE;
+ goto out;
+ case CL_SENT_CH_RESPONSE: /* read server response */
+ {
+ struct sb_buffer sbb;
+ ret = recv_sb(ct, &s->rfds, &sbb);
+ if (ret <= 0)
+ goto out;
+ free(sbb.iov.iov_base);
+ if (sbb.band != SBD_PROCEED)
+ ret = -E_BAD_BAND;
+ else
+ ct->status = CL_RECEIVED_PROCEED;
+ goto out;
+ }
+ case CL_RECEIVED_PROCEED: /* concat args and send command */
+ {
+ if (!FD_ISSET(ct->scc.fd, &s->wfds))
+ return 0;
+ ret = send_sb_command(ct);
+ if (ret <= 0)
+ goto out;
+ ct->status = CL_EXECUTING;
+ return 0;
+ }
+ case CL_SENDING:
+ if (ct->btrn[1]) {
+ char *buf2;
+ size_t sz;
+ ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
+ if (ret == -E_BTR_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;
+ }
+ if (ret < 0)
+ goto close1;
+ if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+ sz = btr_next_buffer(ct->btrn[1], &buf2);
+ assert(sz);
+ ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
+ if (ret < 0)
+ goto close1;
+ if (ret > 0)
+ btr_consume(ct->btrn[1], sz);
+ }
+ }
+ /* fall through */
+ 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)) {
+ struct sb_buffer sbb;
+ ret = recv_sb(ct, &s->rfds, &sbb);
+ if (ret < 0)
+ goto close0;
+ if (ret > 0) {
+ ret = dispatch_sbb(ct, &sbb);
+ if (ret < 0)
+ goto close0;
+ }
+ }
+ }
+ ret = 0;
+ goto out;
+ }
+close1:
+ PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret));
+ btr_remove_node(&ct->btrn[1]);
+ if (ct->btrn[0])
+ return 0;
+ goto out;
+close0:
+ PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret));
+ btr_remove_node(&ct->btrn[0]);
+ if (ct->btrn[1] && ct->status == CL_SENDING)
+ return 0;
+out:
+ if (ret >= 0)
+ 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));
+ if (ct->scc.fd >= 0) {
+ close(ct->scc.fd);
+ ct->scc.fd = -1;