]> git.tuebingen.mpg.de Git - paraslash.git/blob - client_common.c
e34ed641401c7b91f612e8e5922c0fbd19baa0ac
[paraslash.git] / client_common.c
1 /*
2  * Copyright (C) 1997-2012 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file client_common.c Common functions of para_client and para_audiod. */
8
9 #include <regex.h>
10 #include <sys/types.h>
11
12 #include "para.h"
13 #include "error.h"
14 #include "list.h"
15 #include "sched.h"
16 #include "client.cmdline.h"
17 #include "crypt.h"
18 #include "net.h"
19 #include "fd.h"
20 #include "string.h"
21 #include "client.cmdline.h"
22 #include "client.h"
23 #include "buffer_tree.h"
24 #include "version.h"
25
26 /** The size of the receiving buffer. */
27 #define CLIENT_BUFSIZE 4000
28
29 /**
30  * Close the connection to para_server and deallocate per-command ressources.
31  *
32  * \param ct The client task.
33  *
34  * This frees all ressources of the current command but keeps the configuration
35  * in \p ct->conf.
36  *
37  * \sa \ref client_close().
38  */
39 void client_disconnect(struct client_task *ct)
40 {
41         if (!ct)
42                 return;
43         if (ct->scc.fd >= 0)
44                 close(ct->scc.fd);
45         free_argv(ct->features);
46         sc_free(ct->scc.recv);
47         ct->scc.recv = NULL;
48         sc_free(ct->scc.send);
49         ct->scc.send = NULL;
50         btr_free_node(ct->btrn);
51         ct->btrn = NULL;
52 }
53
54 /**
55  * Close the connection to para_server and free all resources.
56  *
57  * \param ct Pointer to the client data.
58  *
59  * \sa \ref client_open(), \ref client_disconnect().
60  */
61 void client_close(struct client_task *ct)
62 {
63         if (!ct)
64                 return;
65         client_disconnect(ct);
66         free(ct->user);
67         free(ct->config_file);
68         free(ct->key_file);
69         client_cmdline_parser_free(&ct->conf);
70         free(ct);
71 }
72
73 /**
74  * The preselect hook for server commands.
75  *
76  * \param s Pointer to the scheduler.
77  * \param t Pointer to the task struct for this command.
78  *
79  * The task pointer must contain a pointer to the initialized client data
80  * structure as it is returned by client_open().
81  *
82  * This function checks the state of the connection and adds the file descriptor
83  * of the connection to the read or write fd set of \a s accordingly.
84  *
85  * \sa register_task() client_open(), struct sched, struct task.
86  */
87 static void client_pre_select(struct sched *s, struct task *t)
88 {
89         int ret;
90         struct client_task *ct = container_of(t, struct client_task, task);
91         struct btr_node *btrn = ct->btrn;
92
93         if (ct->scc.fd < 0)
94                 return;
95         switch (ct->status) {
96         case CL_CONNECTED:
97         case CL_SENT_AUTH:
98         case CL_SENT_CH_RESPONSE:
99         case CL_SENT_COMMAND:
100                 para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
101                 return;
102
103         case CL_RECEIVED_WELCOME:
104         case CL_RECEIVED_PROCEED:
105                 para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
106                 return;
107
108         case CL_RECEIVING:
109                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
110                 if (ret != 0) {
111                         if (ret < 0)
112                                 sched_min_delay(s);
113                         else
114                                 para_fd_set(ct->scc.fd, &s->rfds,
115                                         &s->max_fileno);
116                 }
117                 return;
118         case CL_SENDING:
119                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
120                 if (ret != 0) {
121                         if (ret < 0)
122                                 sched_min_delay(s);
123                         else
124                                 para_fd_set(ct->scc.fd, &s->wfds,
125                                         &s->max_fileno);
126                 }
127                 return;
128         }
129 }
130
131 static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
132                 char *buf, size_t sz, size_t *n)
133 {
134         int ret;
135
136         if (ct->status < CL_SENT_CH_RESPONSE)
137                 return read_nonblock(ct->scc.fd, buf, sz, rfds, n);
138
139         *n = 0;
140         ret = sc_recv_buffer(&ct->scc, buf, sz);
141         /*
142          * sc_recv_buffer is used with blocking fds elsewhere, so it
143          * does not use the nonblock-API. Therefore we need to
144          * check for EOF and EAGAIN.
145          */
146         if (ret == 0)
147                 return -E_SERVER_EOF;
148         if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
149                 return 0;
150         if (ret < 0)
151                 return ret;
152         *n = ret;
153         return 0;
154 }
155
156 static char **parse_features(char *buf)
157 {
158         int i;
159         const char id[] = "\nFeatures: ";
160         char *p, *q, **features;
161
162         p = strstr(buf, id);
163         if (!p)
164                 return NULL;
165         p += strlen(id);
166         q = strchr(p, '\n');
167         if (!q)
168                 return NULL;
169         *q = '\0';
170         create_argv(p, ",", &features);
171         for (i = 0; features[i]; i++)
172                 PARA_INFO_LOG("server feature: %s\n", features[i]);
173         return features;
174 }
175
176 static bool has_feature(const char *feature, struct client_task *ct)
177 {
178         return find_arg(feature, ct->features) >= 0? true : false;
179 }
180
181 /**
182  * The post select hook for client commands.
183  *
184  * \param s Pointer to the scheduler.
185  * \param t Pointer to the task struct for this command.
186  *
187  * Depending on the current state of the connection and the status of the read
188  * and write fd sets of \a s, this function performs the necessary steps to
189  * authenticate the connection, to send the command given by \a t->private_data
190  * and to receive para_server's output, if any.
191  *
192  * \sa struct sched, struct task.
193  */
194 static void client_post_select(struct sched *s, struct task *t)
195 {
196         struct client_task *ct = container_of(t, struct client_task, task);
197         struct btr_node *btrn = ct->btrn;
198         int ret = 0;
199         size_t n;
200         char buf[CLIENT_BUFSIZE];
201
202         t->error = 0;
203         if (ct->scc.fd < 0)
204                 return;
205         switch (ct->status) {
206         case CL_CONNECTED: /* receive welcome message */
207                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
208                 if (ret < 0 || n == 0)
209                         goto out;
210                 ct->features = parse_features(buf);
211                 ct->status = CL_RECEIVED_WELCOME;
212                 return;
213         case CL_RECEIVED_WELCOME: /* send auth command */
214                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
215                         return;
216                 if (has_feature("sideband", ct)) {
217                         ct->use_sideband = true;
218                         sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
219                 } else
220                         sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
221                 PARA_INFO_LOG("--> %s\n", buf);
222                 ret = write_buffer(ct->scc.fd, buf);
223                 if (ret < 0)
224                         goto out;
225                 ct->status = CL_SENT_AUTH;
226                 return;
227         case CL_SENT_AUTH:
228                 /*
229                  * Receive challenge and session keys, decrypt the challenge and
230                  * send back the hash of the decrypted challenge.
231                  */
232                 {
233                 /* decrypted challenge/session key buffer */
234                 unsigned char crypt_buf[1024];
235                 /* the SHA1 of the decrypted challenge */
236                 unsigned char challenge_hash[HASH_SIZE];
237
238                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
239                 if (ret < 0 || n == 0)
240                         goto out;
241                 PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
242                 ret = priv_decrypt(ct->key_file, crypt_buf,
243                         (unsigned char *)buf, n);
244                 if (ret < 0)
245                         goto out;
246                 hash_function((char *)crypt_buf, CHALLENGE_SIZE, challenge_hash);
247                 ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
248                 ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
249                         SESSION_KEY_LEN);
250                 hash_to_asc(challenge_hash, buf);
251                 PARA_INFO_LOG("--> %s\n", buf);
252                 ret = write_all(ct->scc.fd, (char *)challenge_hash, HASH_SIZE);
253                 if (ret < 0)
254                         goto out;
255                 ct->status = CL_SENT_CH_RESPONSE;
256                 return;
257                 }
258         case CL_SENT_CH_RESPONSE: /* read server response */
259                 {
260                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
261                 if (ret < 0 || n == 0)
262                         goto out;
263                 /* check if server has sent "Proceed" message */
264                 ret = -E_CLIENT_AUTH;
265                 if (n < PROCEED_MSG_LEN)
266                         goto out;
267                 if (!strstr(buf, PROCEED_MSG))
268                         goto out;
269                 ct->status = CL_RECEIVED_PROCEED;
270                 return;
271                 }
272         case CL_RECEIVED_PROCEED: /* concat args and send command */
273                 {
274                 int i;
275                 char *command = NULL;
276                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
277                         return;
278                 for (i = 0; i < ct->conf.inputs_num; i++) {
279                         char *tmp = command;
280                         command = make_message("%s\n%s", command?
281                                 command : "", ct->conf.inputs[i]);
282                         free(tmp);
283                 }
284                 command = para_strcat(command, EOC_MSG "\n");
285                 PARA_DEBUG_LOG("--> %s\n", command);
286                 ret = sc_send_buffer(&ct->scc, command);
287                 free(command);
288                 if (ret < 0)
289                         goto out;
290                 ct->status = CL_SENT_COMMAND;
291                 return;
292                 }
293         case CL_SENT_COMMAND:
294                 {
295                 char *buf2;
296                 /* can not use "buf" here because we need a malloced buffer */
297                 buf2 = para_malloc(CLIENT_BUFSIZE);
298                 ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
299                 if (n > 0) {
300                         if (strstr(buf2, AWAITING_DATA_MSG)) {
301                                 free(buf2);
302                                 ct->status = CL_SENDING;
303                                 return;
304                         }
305                         ct->status = CL_RECEIVING;
306                         btr_add_output(buf2, n, btrn);
307                 } else
308                         free(buf2);
309                 goto out;
310                 }
311         case CL_SENDING:
312                 {
313                 char *buf2;
314                 size_t sz;
315                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
316                 if (ret < 0)
317                         goto out;
318                 if (ret == 0)
319                         return;
320                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
321                         return;
322                 sz = btr_next_buffer(btrn, &buf2);
323                 ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
324                 if (ret < 0)
325                         goto out;
326                 btr_consume(btrn, sz);
327                 return;
328                 }
329         case CL_RECEIVING:
330                 {
331                 char *buf2;
332                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
333                 if (ret < 0)
334                         goto out;
335                 if (ret == 0)
336                         return;
337                 /*
338                  * The FD_ISSET() is not strictly necessary, but is allows us
339                  * to skip the malloc below if there is nothing to read anyway.
340                  */
341                 if (!FD_ISSET(ct->scc.fd, &s->rfds))
342                         return;
343                 buf2 = para_malloc(CLIENT_BUFSIZE);
344                 ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
345                 if (n > 0) {
346                         buf2 = para_realloc(buf2, n);
347                         btr_add_output(buf2, n, btrn);
348                 } else
349                         free(buf2);
350                 goto out;
351                 }
352         }
353 out:
354         t->error = ret;
355         if (ret < 0) {
356                 if (ret != -E_SERVER_EOF && ret != -E_BTR_EOF)
357                         PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
358                 btr_remove_node(btrn);
359         }
360 }
361
362 /**
363  * Connect to para_server and register the client task.
364  *
365  * \param ct The initialized client task structure.
366  * \param s The scheduler instance to register the client task to.
367  * \param parent The parent node of the client btr node.
368  * \param child The child node of the client node.
369  *
370  * The client task structure given by \a ct  must be allocated and initialized
371  * by \ref client_parse_config() before this function is called.
372  *
373  * \return Standard.
374  */
375 int client_connect(struct client_task *ct, struct sched *s,
376                 struct btr_node *parent, struct btr_node *child)
377 {
378         int ret;
379
380         PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
381                 ct->conf.server_port_arg);
382         ct->scc.fd = -1;
383         ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
384                                                ct->conf.server_port_arg);
385         if (ret < 0)
386                 return ret;
387         ct->scc.fd = ret;
388         ret = mark_fd_nonblocking(ct->scc.fd);
389         if (ret < 0)
390                 goto err_out;
391         ct->status = CL_CONNECTED;
392         ct->btrn = btr_new_node(&(struct btr_node_description)
393                 EMBRACE(.name = "client", .parent = parent, .child = child));
394         ct->task.pre_select = client_pre_select;
395         ct->task.post_select = client_post_select;
396         ct->task.error = 0;
397         sprintf(ct->task.status, "client");
398         register_task(s, &ct->task);
399         return 1;
400 err_out:
401         close(ct->scc.fd);
402         ct->scc.fd = -1;
403         return ret;
404 }
405
406 /**
407  * Parse a client configuration.
408  *
409  * \param argc Usual argument count.
410  * \param argv Usual argument vector.
411  * \param ct_ptr Filled in by this function.
412  * \param loglevel If not \p NULL, the number of the loglevel is stored here.
413  *
414  * This checks the command line options given by \a argc and \a argv, sets
415  * default values for the user name and the name of the rsa key file and reads
416  * further options from the config file.
417  *
418  * Upon successful return, \a ct_ptr points to a dynamically allocated and
419  * initialized client task struct.
420  *
421  * \return The number of non-option arguments in \a argc/argv on success,
422  * negative on errors.
423  */
424 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
425                 int *loglevel)
426 {
427         char *home = para_homedir();
428         int ret;
429         struct client_task *ct = para_calloc(sizeof(struct client_task));
430
431         *ct_ptr = ct;
432         ct->scc.fd = -1;
433         ret = -E_CLIENT_SYNTAX;
434         if (client_cmdline_parser(argc, argv, &ct->conf))
435                 goto out;
436         HANDLE_VERSION_FLAG("client", ct->conf);
437
438         ct->config_file = ct->conf.config_file_given?
439                 para_strdup(ct->conf.config_file_arg) :
440                 make_message("%s/.paraslash/client.conf", home);
441         ret = file_exists(ct->config_file);
442         if (!ret && ct->conf.config_file_given) {
443                 ret = -E_NO_CONFIG;
444                 goto out;
445         }
446         if (ret) {
447                 struct client_cmdline_parser_params params = {
448                         .override = 0,
449                         .initialize = 0,
450                         .check_required = 0,
451                         .check_ambiguity = 0,
452                         .print_errors = 0
453                 };
454                 ret = -E_BAD_CONFIG;
455                 if (client_cmdline_parser_config_file(ct->config_file,
456                         &ct->conf, &params))
457                         goto out;
458         }
459         ct->user = ct->conf.user_given?
460                 para_strdup(ct->conf.user_arg) : para_logname();
461
462         if (ct->conf.key_file_given)
463                 ct->key_file = para_strdup(ct->conf.key_file_arg);
464         else {
465                 ct->key_file = make_message("%s/.paraslash/key.%s",
466                         home, ct->user);
467                 if (!file_exists(ct->key_file)) {
468                         free(ct->key_file);
469                         ct->key_file = make_message("%s/.ssh/id_rsa", home);
470                 }
471         }
472
473         if (loglevel)
474                 *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
475         PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
476         PARA_INFO_LOG("config_file: %s\n", ct->config_file);
477         PARA_INFO_LOG("key_file: %s\n", ct->key_file);
478         ret = ct->conf.inputs_num;
479 out:
480         free(home);
481         if (ret < 0) {
482                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
483                 client_close(ct);
484                 *ct_ptr = NULL;
485         }
486         return ret;
487 }
488
489 /**
490  * Parse the client configuration and open a connection to para_server.
491  *
492  * \param argc See \ref client_parse_config.
493  * \param argv See \ref client_parse_config.
494  * \param ct_ptr See \ref client_parse_config.
495  * \param loglevel See \ref client_parse_config.
496  * \param parent See \ref client_connect().
497  * \param child See \ref client_connect().
498  * \param sched See \ref client_connect().
499  *
500  * This function combines client_parse_config() and client_connect(). It is
501  * considered a syntax error if no command was given, i.e. if the number
502  * of non-option arguments is zero.
503  *
504  * \return Standard.
505  */
506 int client_open(int argc, char *argv[], struct client_task **ct_ptr,
507                 int *loglevel, struct btr_node *parent, struct btr_node *child,
508                 struct sched *sched)
509 {
510         int ret = client_parse_config(argc, argv, ct_ptr, loglevel);
511
512         if (ret < 0)
513                 return ret;
514         if (ret == 0) {
515                 ret = -E_CLIENT_SYNTAX;
516                 goto fail;
517         }
518         ret = client_connect(*ct_ptr, sched, parent, child);
519         if (ret < 0)
520                 goto fail;
521         return 1;
522 fail:
523         client_close(*ct_ptr);
524         *ct_ptr = NULL;
525         return ret;
526 }