1 /* Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file client_common.c Common functions of para_client and para_audiod. */
5 #include <netinet/in.h>
6 #include <sys/socket.h>
14 #include "client.lsg.h"
25 #include "buffer_tree.h"
28 /** The size of the receiving buffer. */
29 #define CLIENT_BUFSIZE 4000
32 * Close the connection to para_server and free all resources.
34 * \param ct Pointer to the client data.
36 * \sa \ref client_open().
38 void client_close(struct client_task
*ct
)
44 lls_free_parse_result(ct
->lpr
, CLIENT_CMD_PTR
);
45 free(ct
->challenge_hash
);
52 * The preselect hook for server commands.
54 * The task pointer must contain a pointer to the initialized client data
55 * structure as it is returned by client_open().
57 * This function checks the state of the connection and adds the file descriptor
58 * of the connection to the read or write fd set of s accordingly.
60 static void client_pre_select(struct sched
*s
, void *context
)
63 struct client_task
*ct
= context
;
70 case CL_SENT_CH_RESPONSE
:
71 para_fd_set(ct
->scc
.fd
, &s
->rfds
, &s
->max_fileno
);
74 case CL_RECEIVED_WELCOME
:
75 case CL_RECEIVED_PROCEED
:
76 case CL_RECEIVED_CHALLENGE
:
77 para_fd_set(ct
->scc
.fd
, &s
->wfds
, &s
->max_fileno
);
82 ret
= btr_node_status(ct
->btrn
[1], 0, BTR_NT_LEAF
);
86 para_fd_set(ct
->scc
.fd
, &s
->wfds
, &s
->max_fileno
);
91 ret
= btr_node_status(ct
->btrn
[0], 0, BTR_NT_ROOT
);
95 para_fd_set(ct
->scc
.fd
, &s
->rfds
, &s
->max_fileno
);
101 static int send_sb(struct client_task
*ct
, int channel
, void *buf
, size_t numbytes
,
102 enum sb_designator band
, bool dont_free
)
104 int ret
, fd
= ct
->scc
.fd
;
107 if (!ct
->sbc
[channel
]) {
108 struct sb_buffer sbb
;
109 sb_transformation trafo
= ct
->status
< CL_RECEIVED_PROCEED
?
111 sbb
= (typeof(sbb
))SBB_INIT(band
, buf
, numbytes
);
112 ct
->sbc
[channel
] = sb_new_send(&sbb
, dont_free
, trafo
, ct
->scc
.send
);
114 ret
= sb_get_send_buffers(ct
->sbc
[channel
], iov
);
115 ret
= xwritev(fd
, iov
, ret
);
117 sb_free(ct
->sbc
[channel
]);
118 ct
->sbc
[channel
] = NULL
;
121 if (sb_sent(ct
->sbc
[channel
], ret
)) {
122 ct
->sbc
[channel
] = NULL
;
128 static int recv_sb(struct client_task
*ct
, fd_set
*rfds
,
129 struct sb_buffer
*result
)
133 sb_transformation trafo
;
137 if (!FD_ISSET(ct
->scc
.fd
, rfds
))
139 if (ct
->status
< CL_SENT_CH_RESPONSE
)
140 trafo
= trafo_context
= NULL
;
143 trafo_context
= ct
->scc
.recv
;
146 ct
->sbc
[0] = sb_new_recv(0, trafo
, trafo_context
);
148 sb_get_recv_buffer(ct
->sbc
[0], &iov
);
149 ret
= read_nonblock(ct
->scc
.fd
, iov
.iov_base
, iov
.iov_len
, rfds
, &n
);
157 ret
= sb_received(ct
->sbc
[0], n
, result
);
167 static char **parse_features(char *buf
)
170 const char id
[] = "\nFeatures: ";
171 char *p
, *q
, **features
;
181 create_argv(p
, ",", &features
);
182 for (i
= 0; features
[i
]; i
++)
183 PARA_INFO_LOG("server feature: %s\n", features
[i
]);
187 static int dispatch_sbb(struct client_task
*ct
, struct sb_buffer
*sbb
)
190 const char *designator
[] = {SB_DESIGNATORS_ARRAY
};
194 if (sbb
->band
< NUM_SB_DESIGNATORS
)
195 PARA_DEBUG_LOG("band: %s\n", designator
[sbb
->band
]);
198 case SBD_AWAITING_DATA
:
199 ct
->status
= CL_SENDING
;
203 if (iov_valid(&sbb
->iov
))
204 btr_add_output(sbb
->iov
.iov_base
, sbb
->iov
.iov_len
,
211 case SBD_WARNING_LOG
:
215 if (iov_valid(&sbb
->iov
)) {
216 int ll
= sbb
->band
- SBD_DEBUG_LOG
;
217 para_log(ll
, "remote: %s", (char *)sbb
->iov
.iov_base
);
221 case SBD_EXIT__SUCCESS
:
222 ret
= -E_SERVER_CMD_SUCCESS
;
224 case SBD_EXIT__FAILURE
:
225 ret
= -E_SERVER_CMD_FAILURE
;
228 PARA_ERROR_LOG("invalid band %d\n", sbb
->band
);
233 free(sbb
->iov
.iov_base
);
235 sbb
->iov
.iov_base
= NULL
;
239 static int send_sb_command(struct client_task
*ct
)
244 unsigned num_inputs
= lls_num_inputs(ct
->lpr
);
247 return send_sb(ct
, 0, NULL
, 0, 0, false);
249 for (i
= 0; i
< num_inputs
; i
++)
250 len
+= strlen(lls_input(i
, ct
->lpr
)) + 1;
251 p
= command
= para_malloc(len
);
252 for (i
= 0; i
< num_inputs
; i
++) {
253 const char *str
= lls_input(i
, ct
->lpr
);
255 p
+= strlen(str
) + 1;
257 PARA_DEBUG_LOG("--> %s\n", command
);
258 return send_sb(ct
, 0, command
, len
, SBD_COMMAND
, false);
262 * The post select hook for client commands.
264 * Depending on the current state of the connection and the status of the read
265 * and write fd sets of s, this function performs the necessary steps to
266 * authenticate the connection, to send the command given by t->private_data
267 * and to receive para_server's output, if any.
269 static int client_post_select(struct sched
*s
, void *context
)
271 struct client_task
*ct
= context
;
274 char buf
[CLIENT_BUFSIZE
];
276 ret
= task_get_notification(ct
->task
);
281 switch (ct
->status
) {
282 case CL_CONNECTED
: /* receive welcome message */
283 ret
= read_nonblock(ct
->scc
.fd
, buf
, sizeof(buf
), &s
->rfds
, &n
);
284 if (ret
< 0 || n
== 0)
286 ct
->features
= parse_features(buf
);
287 ct
->status
= CL_RECEIVED_WELCOME
;
289 case CL_RECEIVED_WELCOME
: /* send auth command */
290 if (!FD_ISSET(ct
->scc
.fd
, &s
->wfds
))
292 sprintf(buf
, AUTH_REQUEST_MSG
"%s sideband,aes_ctr128",
294 PARA_INFO_LOG("--> %s\n", buf
);
295 ret
= write_buffer(ct
->scc
.fd
, buf
);
298 ct
->status
= CL_SENT_AUTH
;
302 * Receive challenge and session keys, decrypt the challenge and
303 * send back the hash of the decrypted challenge.
306 /* decrypted challenge/session key buffer */
307 unsigned char crypt_buf
[1024];
308 struct sb_buffer sbb
;
310 ret
= recv_sb(ct
, &s
->rfds
, &sbb
);
313 if (sbb
.band
!= SBD_CHALLENGE
) {
315 free(sbb
.iov
.iov_base
);
319 PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n
);
320 ret
= apc_priv_decrypt(ct
->key_file
, crypt_buf
,
321 sbb
.iov
.iov_base
, n
);
322 free(sbb
.iov
.iov_base
);
325 ct
->challenge_hash
= para_malloc(HASH_SIZE
);
326 hash_function((char *)crypt_buf
, APC_CHALLENGE_SIZE
, ct
->challenge_hash
);
327 ct
->scc
.send
= sc_new(crypt_buf
+ APC_CHALLENGE_SIZE
, SESSION_KEY_LEN
);
328 ct
->scc
.recv
= sc_new(crypt_buf
+ APC_CHALLENGE_SIZE
+ SESSION_KEY_LEN
,
330 hash_to_asc(ct
->challenge_hash
, buf
);
331 PARA_INFO_LOG("--> %s\n", buf
);
332 ct
->status
= CL_RECEIVED_CHALLENGE
;
335 case CL_RECEIVED_CHALLENGE
:
336 ret
= send_sb(ct
, 0, ct
->challenge_hash
, HASH_SIZE
,
337 SBD_CHALLENGE_RESPONSE
, false);
339 ct
->challenge_hash
= NULL
;
342 ct
->status
= CL_SENT_CH_RESPONSE
;
344 case CL_SENT_CH_RESPONSE
: /* read server response */
346 struct sb_buffer sbb
;
347 ret
= recv_sb(ct
, &s
->rfds
, &sbb
);
350 free(sbb
.iov
.iov_base
);
351 if (sbb
.band
!= SBD_PROCEED
)
354 ct
->status
= CL_RECEIVED_PROCEED
;
357 case CL_RECEIVED_PROCEED
: /* concat args and send command */
359 if (!FD_ISSET(ct
->scc
.fd
, &s
->wfds
))
361 ret
= send_sb_command(ct
);
364 ct
->status
= CL_EXECUTING
;
371 ret
= btr_node_status(ct
->btrn
[1], 0, BTR_NT_LEAF
);
372 if (ret
== -E_BTR_EOF
) {
373 /* empty blob data packet indicates EOF */
374 PARA_INFO_LOG("blob sent\n");
375 ret
= send_sb(ct
, 1, NULL
, 0, SBD_BLOB_DATA
, true);
381 if (ret
> 0 && FD_ISSET(ct
->scc
.fd
, &s
->wfds
)) {
382 sz
= btr_next_buffer(ct
->btrn
[1], &buf2
);
384 ret
= send_sb(ct
, 1, buf2
, sz
, SBD_BLOB_DATA
, true);
388 btr_consume(ct
->btrn
[1], sz
);
394 ret
= btr_node_status(ct
->btrn
[0], 0, BTR_NT_ROOT
);
397 if (ret
> 0 && FD_ISSET(ct
->scc
.fd
, &s
->rfds
)) {
398 struct sb_buffer sbb
;
399 ret
= recv_sb(ct
, &s
->rfds
, &sbb
);
403 ret
= dispatch_sbb(ct
, &sbb
);
413 PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret
));
414 btr_remove_node(&ct
->btrn
[1]);
419 PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret
));
420 btr_remove_node(&ct
->btrn
[0]);
421 if (ct
->btrn
[1] && ct
->status
== CL_SENDING
)
426 btr_remove_node(&ct
->btrn
[0]);
427 btr_remove_node(&ct
->btrn
[1]);
428 if (ret
!= -E_SERVER_CMD_SUCCESS
&& ret
!= -E_SERVER_CMD_FAILURE
)
429 PARA_ERROR_LOG("%s\n", para_strerror(-ret
));
430 if (ct
->scc
.fd
>= 0) {
434 free_argv(ct
->features
);
436 sc_free(ct
->scc
.recv
);
438 sc_free(ct
->scc
.send
);
444 * Connect to para_server and register the client task.
446 * \param ct The initialized client task structure.
447 * \param s The scheduler instance to register the client task to.
448 * \param parent The parent node of the client btr node.
449 * \param child The child node of the client node.
451 * The client task structure given by \a ct must be allocated and initialized
452 * by \ref client_parse_config() before this function is called.
456 int client_connect(struct client_task
*ct
, struct sched
*s
,
457 struct btr_node
*parent
, struct btr_node
*child
)
460 const char *host
= CLIENT_OPT_STRING_VAL(HOSTNAME
, ct
->lpr
);
461 uint32_t port
= CLIENT_OPT_UINT32_VAL(SERVER_PORT
, ct
->lpr
);
463 PARA_NOTICE_LOG("connecting %s:%u\n", host
, port
);
465 ret
= para_connect_simple(IPPROTO_TCP
, host
, port
);
469 ret
= mark_fd_nonblocking(ct
->scc
.fd
);
472 ct
->status
= CL_CONNECTED
;
473 ct
->btrn
[0] = btr_new_node(&(struct btr_node_description
)
474 EMBRACE(.name
= "client recv", .parent
= NULL
, .child
= child
));
475 ct
->btrn
[1] = btr_new_node(&(struct btr_node_description
)
476 EMBRACE(.name
= "client send", .parent
= parent
, .child
= NULL
));
478 ct
->task
= task_register(&(struct task_info
) {
480 .pre_select
= client_pre_select
,
481 .post_select
= client_post_select
,
491 static void handle_help_flag(struct lls_parse_result
*lpr
)
495 if (CLIENT_OPT_GIVEN(DETAILED_HELP
, lpr
))
496 help
= lls_long_help(CLIENT_CMD_PTR
);
497 else if (CLIENT_OPT_GIVEN(HELP
, lpr
))
498 help
= lls_short_help(CLIENT_CMD_PTR
);
501 printf("%s\n", help
);
507 * Parse a client configuration.
509 * \param argc Usual argument count.
510 * \param argv Usual argument vector.
511 * \param ct_ptr Filled in by this function.
512 * \param loglevel If not \p NULL, the number of the loglevel is stored here.
514 * This checks the command line options given by \a argc and \a argv, sets
515 * default values for the user name and the name of the rsa key file and reads
516 * further options from the config file.
518 * Upon successful return, \a ct_ptr points to a dynamically allocated and
519 * initialized client task struct.
521 * \return The number of non-option arguments in \a argc/argv on success,
522 * negative on errors.
524 int client_parse_config(int argc
, char *argv
[], struct client_task
**ct_ptr
,
527 const struct lls_command
*cmd
= CLIENT_CMD_PTR
;
530 struct lls_parse_result
*lpr
;
532 struct client_task
*ct
;
533 char *cf
= NULL
, *kf
= NULL
, *user
, *errctx
, *home
= para_homedir();
535 ret
= lls(lls_parse(argc
, argv
, cmd
, &lpr
, &errctx
));
538 version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION
, lpr
));
539 handle_help_flag(lpr
);
541 if (CLIENT_OPT_GIVEN(CONFIG_FILE
, lpr
))
542 cf
= para_strdup(CLIENT_OPT_STRING_VAL(CONFIG_FILE
, lpr
));
544 cf
= make_message("%s/.paraslash/client.conf", home
);
545 ret
= mmap_full_file(cf
, O_RDONLY
, &map
, &sz
, NULL
);
547 if (ret
!= -E_EMPTY
&& ret
!= -ERRNO_TO_PARA_ERROR(ENOENT
))
549 if (ret
== -ERRNO_TO_PARA_ERROR(ENOENT
) &&
550 CLIENT_OPT_GIVEN(CONFIG_FILE
, lpr
))
555 struct lls_parse_result
*cf_lpr
, *merged_lpr
;
556 ret
= lls(lls_convert_config(map
, sz
, NULL
, &cf_argv
, &errctx
));
557 para_munmap(map
, sz
);
561 ret
= lls(lls_parse(cf_argc
, cf_argv
, cmd
, &cf_lpr
, &errctx
));
562 lls_free_argv(cf_argv
);
565 ret
= lls(lls_merge(lpr
, cf_lpr
, cmd
, &merged_lpr
,
567 lls_free_parse_result(cf_lpr
, cmd
);
570 lls_free_parse_result(lpr
, cmd
);
574 ll
= CLIENT_OPT_UINT32_VAL(LOGLEVEL
, lpr
);
577 user
= CLIENT_OPT_GIVEN(USER
, lpr
)?
578 para_strdup(CLIENT_OPT_STRING_VAL(USER
, lpr
)) : para_logname();
580 if (CLIENT_OPT_GIVEN(KEY_FILE
, lpr
))
581 kf
= para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE
, lpr
));
583 kf
= make_message("%s/.paraslash/key.%s", home
, user
);
584 if (!file_exists(kf
)) {
586 kf
= make_message("%s/.ssh/id_rsa", home
);
589 PARA_INFO_LOG("user: %s\n", user
);
590 PARA_INFO_LOG("config file: %s\n", cf
);
591 PARA_INFO_LOG("key file: %s\n", kf
);
592 PARA_INFO_LOG("loglevel: %d\n", ll
);
593 ct
= para_calloc(sizeof(*ct
));
599 ret
= lls_num_inputs(lpr
);
605 PARA_ERROR_LOG("%s\n", errctx
);
607 lls_free_parse_result(lpr
, cmd
);
615 * Parse the client configuration and open a connection to para_server.
617 * \param argc See \ref client_parse_config.
618 * \param argv See \ref client_parse_config.
619 * \param ct_ptr See \ref client_parse_config.
620 * \param loglevel See \ref client_parse_config.
621 * \param parent See \ref client_connect().
622 * \param child See \ref client_connect().
623 * \param sched See \ref client_connect().
625 * This function combines client_parse_config() and client_connect(). It is
626 * considered a syntax error if no command was given, i.e. if the number
627 * of non-option arguments is zero.
631 int client_open(int argc
, char *argv
[], struct client_task
**ct_ptr
,
632 int *loglevel
, struct btr_node
*parent
, struct btr_node
*child
,
635 int ret
= client_parse_config(argc
, argv
, ct_ptr
, loglevel
);
640 ret
= -E_CLIENT_SYNTAX
;
643 ret
= client_connect(*ct_ptr
, sched
, parent
, child
);
648 client_close(*ct_ptr
);