]> git.tuebingen.mpg.de Git - paraslash.git/blob - client_common.c
client: Remove client_recv_buffer().
[paraslash.git] / client_common.c
1 /*
2  * Copyright (C) 1997-2013 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 "sideband.h"
21 #include "string.h"
22 #include "client.cmdline.h"
23 #include "client.h"
24 #include "buffer_tree.h"
25 #include "version.h"
26
27 /** The size of the receiving buffer. */
28 #define CLIENT_BUFSIZE 4000
29
30 /**
31  * Close the connection to para_server and deallocate per-command ressources.
32  *
33  * \param ct The client task.
34  *
35  * This frees all ressources of the current command but keeps the configuration
36  * in \p ct->conf.
37  *
38  * \sa \ref client_close().
39  */
40 void client_disconnect(struct client_task *ct)
41 {
42         if (!ct)
43                 return;
44         if (ct->scc.fd >= 0)
45                 close(ct->scc.fd);
46         free_argv(ct->features);
47         ct->features = NULL;
48         sc_free(ct->scc.recv);
49         ct->scc.recv = NULL;
50         sc_free(ct->scc.send);
51         ct->scc.send = NULL;
52         btr_remove_node(&ct->btrn);
53 }
54
55 /**
56  * Close the connection to para_server and free all resources.
57  *
58  * \param ct Pointer to the client data.
59  *
60  * \sa \ref client_open(), \ref client_disconnect().
61  */
62 void client_close(struct client_task *ct)
63 {
64         if (!ct)
65                 return;
66         client_disconnect(ct);
67         free(ct->user);
68         free(ct->config_file);
69         free(ct->key_file);
70         client_cmdline_parser_free(&ct->conf);
71         free(ct->challenge_hash);
72         sb_free(ct->sbc);
73         free(ct);
74 }
75
76 /**
77  * The preselect hook for server commands.
78  *
79  * \param s Pointer to the scheduler.
80  * \param t Pointer to the task struct for this command.
81  *
82  * The task pointer must contain a pointer to the initialized client data
83  * structure as it is returned by client_open().
84  *
85  * This function checks the state of the connection and adds the file descriptor
86  * of the connection to the read or write fd set of \a s accordingly.
87  *
88  * \sa register_task() client_open(), struct sched, struct task.
89  */
90 static void client_pre_select(struct sched *s, struct task *t)
91 {
92         int ret;
93         struct client_task *ct = container_of(t, struct client_task, task);
94         struct btr_node *btrn = ct->btrn;
95
96         if (ct->scc.fd < 0)
97                 return;
98         switch (ct->status) {
99         case CL_CONNECTED:
100         case CL_SENT_AUTH:
101         case CL_SENT_CH_RESPONSE:
102         case CL_SENT_COMMAND:
103                 para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
104                 return;
105
106         case CL_RECEIVED_WELCOME:
107         case CL_RECEIVED_PROCEED:
108         case CL_RECEIVED_CHALLENGE:
109                 para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
110                 return;
111
112         case CL_RECEIVING:
113                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
114                 if (ret != 0) {
115                         if (ret < 0)
116                                 sched_min_delay(s);
117                         else
118                                 para_fd_set(ct->scc.fd, &s->rfds,
119                                         &s->max_fileno);
120                 }
121                 return;
122         case CL_SENDING:
123                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
124                 if (ret != 0) {
125                         if (ret < 0)
126                                 sched_min_delay(s);
127                         else
128                                 para_fd_set(ct->scc.fd, &s->wfds,
129                                         &s->max_fileno);
130                 }
131                 return;
132         }
133 }
134
135 static int send_sb(struct client_task *ct, void *buf, size_t numbytes,
136                 enum sb_designator band, bool dont_free)
137 {
138         int ret, fd = ct->scc.fd;
139         struct iovec iov[2];
140
141         if (!ct->sbc) {
142                 struct sb_buffer sbb;
143                 sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED?
144                         NULL : sc_trafo;
145                 sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes);
146                 ct->sbc = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
147         }
148         ret = sb_get_send_buffers(ct->sbc, iov);
149         ret = xwritev(fd, iov, ret);
150         if (ret < 0) {
151                 sb_free(ct->sbc);
152                 ct->sbc = NULL;
153                 return ret;
154         }
155         if (sb_sent(ct->sbc, ret)) {
156                 ct->sbc = NULL;
157                 return 1;
158         }
159         return 0;
160 }
161
162 static int recv_sb(struct client_task *ct, fd_set *rfds,
163                 struct sb_buffer *result)
164 {
165         int ret;
166         size_t n;
167         sb_transformation trafo;
168         void *trafo_context;
169         struct iovec iov;
170
171         if (!FD_ISSET(ct->scc.fd, rfds))
172                 return 0;
173         if (ct->status < CL_SENT_CH_RESPONSE)
174                 trafo = trafo_context = NULL;
175         else {
176                 trafo = sc_trafo;
177                 trafo_context = ct->scc.recv;
178         }
179         if (!ct->sbc)
180                 ct->sbc = sb_new_recv(0, trafo, trafo_context);
181 again:
182         sb_get_recv_buffer(ct->sbc, &iov);
183         ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
184         if (ret < 0) {
185                 sb_free(ct->sbc);
186                 ct->sbc = NULL;
187                 return ret;
188         }
189         if (n == 0)
190                 return 0;
191         if (!sb_received(ct->sbc, n, result))
192                 goto again;
193         ct->sbc = NULL;
194         return 1;
195 }
196
197
198 static char **parse_features(char *buf)
199 {
200         int i;
201         const char id[] = "\nFeatures: ";
202         char *p, *q, **features;
203
204         p = strstr(buf, id);
205         if (!p)
206                 return NULL;
207         p += strlen(id);
208         q = strchr(p, '\n');
209         if (!q)
210                 return NULL;
211         *q = '\0';
212         create_argv(p, ",", &features);
213         for (i = 0; features[i]; i++)
214                 PARA_INFO_LOG("server feature: %s\n", features[i]);
215         return features;
216 }
217
218 static int dispatch_sbb(struct client_task *ct, struct sb_buffer *sbb)
219 {
220         int ret;
221         const char *designator[] = {SB_DESIGNATORS_ARRAY};
222
223         if (!sbb)
224                 return 0;
225         if (sbb->band < NUM_SB_DESIGNATORS)
226                 PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]);
227
228         switch (sbb->band) {
229         case SBD_OUTPUT:
230                 if (iov_valid(&sbb->iov))
231                         btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len,
232                                 ct->btrn);
233                 ret = 1;
234                 goto out;
235         case SBD_DEBUG_LOG:
236         case SBD_INFO_LOG:
237         case SBD_NOTICE_LOG:
238         case SBD_WARNING_LOG:
239         case SBD_ERROR_LOG:
240         case SBD_CRIT_LOG:
241         case SBD_EMERG_LOG:
242                 if (iov_valid(&sbb->iov)) {
243                         int ll = sbb->band - SBD_DEBUG_LOG;
244                         para_log(ll, "remote: %s", (char *)sbb->iov.iov_base);
245                 }
246                 ret = 1;
247                 goto deallocate;
248         case SBD_EXIT__SUCCESS:
249                 ret = -E_SERVER_CMD_SUCCESS;
250                 goto deallocate;
251         case SBD_EXIT__FAILURE:
252                 ret = -E_SERVER_CMD_FAILURE;
253                 goto deallocate;
254         default:
255                 PARA_ERROR_LOG("invalid band %d\n", sbb->band);
256                 ret = -E_BAD_BAND;
257                 goto deallocate;
258         }
259 deallocate:
260         free(sbb->iov.iov_base);
261 out:
262         sbb->iov.iov_base = NULL;
263         return ret;
264 }
265
266 static bool has_feature(const char *feature, struct client_task *ct)
267 {
268         return find_arg(feature, ct->features) >= 0? true : false;
269 }
270
271 static int send_sb_command(struct client_task *ct)
272 {
273         int i;
274         char *command, *p;
275         size_t len = 0;
276
277         if (ct->sbc)
278                 return send_sb(ct, NULL, 0, 0, false);
279
280         for (i = 0; i < ct->conf.inputs_num; i++)
281                 len += strlen(ct->conf.inputs[i]) + 1;
282         p = command = para_malloc(len);
283         for (i = 0; i < ct->conf.inputs_num; i++) {
284                 strcpy(p, ct->conf.inputs[i]);
285                 p += strlen(ct->conf.inputs[i]) + 1;
286         }
287         PARA_DEBUG_LOG("--> %s\n", command);
288         return send_sb(ct, command, len, SBD_COMMAND, false);
289 }
290
291 /**
292  * The post select hook for client commands.
293  *
294  * \param s Pointer to the scheduler.
295  * \param t Pointer to the task struct for this command.
296  *
297  * Depending on the current state of the connection and the status of the read
298  * and write fd sets of \a s, this function performs the necessary steps to
299  * authenticate the connection, to send the command given by \a t->private_data
300  * and to receive para_server's output, if any.
301  *
302  * \sa struct sched, struct task.
303  */
304 static void client_post_select(struct sched *s, struct task *t)
305 {
306         struct client_task *ct = container_of(t, struct client_task, task);
307         struct btr_node *btrn = ct->btrn;
308         int ret = 0;
309         size_t n;
310         char buf[CLIENT_BUFSIZE];
311
312         ret = task_get_notification(t);
313         if (ret < 0)
314                 goto out;
315         if (ct->scc.fd < 0)
316                 return;
317         switch (ct->status) {
318         case CL_CONNECTED: /* receive welcome message */
319                 ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
320                 if (ret < 0 || n == 0)
321                         goto out;
322                 ct->features = parse_features(buf);
323                 if (!has_feature("sideband", ct)) {
324                         PARA_ERROR_LOG("server has no sideband support\n");
325                         ret = -E_INCOMPAT_FEAT;
326                         goto out;
327                 }
328                 ct->status = CL_RECEIVED_WELCOME;
329                 return;
330         case CL_RECEIVED_WELCOME: /* send auth command */
331                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
332                         return;
333                 sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
334                 PARA_INFO_LOG("--> %s\n", buf);
335                 ret = write_buffer(ct->scc.fd, buf);
336                 if (ret < 0)
337                         goto out;
338                 ct->status = CL_SENT_AUTH;
339                 return;
340         case CL_SENT_AUTH:
341                 /*
342                  * Receive challenge and session keys, decrypt the challenge and
343                  * send back the hash of the decrypted challenge.
344                  */
345                 {
346                 /* decrypted challenge/session key buffer */
347                 unsigned char crypt_buf[1024];
348                 struct sb_buffer sbb;
349
350                 ret = recv_sb(ct, &s->rfds, &sbb);
351                 if (ret <= 0)
352                         goto out;
353                 if (sbb.band != SBD_CHALLENGE) {
354                         ret = -E_BAD_BAND;
355                         free(sbb.iov.iov_base);
356                                 goto out;
357                 }
358                 n = sbb.iov.iov_len;
359                 PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
360                 ret = priv_decrypt(ct->key_file, crypt_buf,
361                         sbb.iov.iov_base, n);
362                 free(sbb.iov.iov_base);
363                 if (ret < 0)
364                         goto out;
365                 ct->challenge_hash = para_malloc(HASH_SIZE);
366                 hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
367                 ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
368                 ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
369                         SESSION_KEY_LEN);
370                 hash_to_asc(ct->challenge_hash, buf);
371                 PARA_INFO_LOG("--> %s\n", buf);
372                 ct->status = CL_RECEIVED_CHALLENGE;
373                 return;
374                 }
375         case CL_RECEIVED_CHALLENGE:
376                 ret = send_sb(ct, ct->challenge_hash, HASH_SIZE,
377                         SBD_CHALLENGE_RESPONSE, false);
378                 if (ret != 0)
379                         ct->challenge_hash = NULL;
380                 if (ret <= 0)
381                         goto out;
382                 ct->status = CL_SENT_CH_RESPONSE;
383                 goto out;
384         case CL_SENT_CH_RESPONSE: /* read server response */
385                 {
386                 struct sb_buffer sbb;
387                 ret = recv_sb(ct, &s->rfds, &sbb);
388                 if (ret <= 0)
389                         goto out;
390                 free(sbb.iov.iov_base);
391                 if (sbb.band != SBD_PROCEED)
392                         ret = -E_BAD_BAND;
393                 else
394                         ct->status = CL_RECEIVED_PROCEED;
395                 goto out;
396                 }
397         case CL_RECEIVED_PROCEED: /* concat args and send command */
398                 {
399                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
400                         return;
401                 ret = send_sb_command(ct);
402                 if (ret <= 0)
403                         goto out;
404                 ct->status = CL_SENT_COMMAND;
405                 return;
406                 }
407         case CL_SENT_COMMAND:
408                 {
409                 struct sb_buffer sbb;
410                 ret = recv_sb(ct, &s->rfds, &sbb);
411                 if (ret <= 0)
412                         goto out;
413                 if (sbb.band == SBD_AWAITING_DATA) {
414                         ct->status = CL_SENDING;
415                         free(sbb.iov.iov_base);
416                         goto out;
417                 }
418                 ct->status = CL_RECEIVING;
419                 ret = dispatch_sbb(ct, &sbb);
420                 goto out;
421                 }
422         case CL_SENDING:
423                 {
424                 char *buf2;
425                 size_t sz;
426                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
427                 if (ret < 0)
428                         goto out;
429                 if (ret == 0)
430                         return;
431                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
432                         return;
433                 sz = btr_next_buffer(btrn, &buf2);
434                 ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
435                 if (ret < 0)
436                         goto out;
437                 btr_consume(btrn, sz);
438                 return;
439                 }
440         case CL_RECEIVING:
441                 {
442                 struct sb_buffer sbb;
443                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
444                 if (ret < 0)
445                         goto out;
446                 if (ret == 0)
447                         return;
448                 /*
449                  * The FD_ISSET() is not strictly necessary, but is allows us
450                  * to skip the malloc below if there is nothing to read anyway.
451                  */
452                 if (!FD_ISSET(ct->scc.fd, &s->rfds))
453                         return;
454                 ret = recv_sb(ct, &s->rfds, &sbb);
455                 if (ret > 0)
456                         ret = dispatch_sbb(ct, &sbb);
457                 goto out;
458                 }
459         }
460 out:
461         t->error = ret;
462         if (ret < 0)
463                 btr_remove_node(&ct->btrn);
464 }
465
466 /**
467  * Connect to para_server and register the client task.
468  *
469  * \param ct The initialized client task structure.
470  * \param s The scheduler instance to register the client task to.
471  * \param parent The parent node of the client btr node.
472  * \param child The child node of the client node.
473  *
474  * The client task structure given by \a ct  must be allocated and initialized
475  * by \ref client_parse_config() before this function is called.
476  *
477  * \return Standard.
478  */
479 int client_connect(struct client_task *ct, struct sched *s,
480                 struct btr_node *parent, struct btr_node *child)
481 {
482         int ret;
483
484         PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
485                 ct->conf.server_port_arg);
486         ct->scc.fd = -1;
487         ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
488                                                ct->conf.server_port_arg);
489         if (ret < 0)
490                 return ret;
491         ct->scc.fd = ret;
492         ret = mark_fd_nonblocking(ct->scc.fd);
493         if (ret < 0)
494                 goto err_out;
495         ct->status = CL_CONNECTED;
496         ct->btrn = btr_new_node(&(struct btr_node_description)
497                 EMBRACE(.name = "client", .parent = parent, .child = child));
498         ct->task.pre_select = client_pre_select;
499         ct->task.post_select = client_post_select;
500         ct->task.error = 0;
501         sprintf(ct->task.status, "client");
502         register_task(s, &ct->task);
503         return 1;
504 err_out:
505         close(ct->scc.fd);
506         ct->scc.fd = -1;
507         return ret;
508 }
509
510 /**
511  * Parse a client configuration.
512  *
513  * \param argc Usual argument count.
514  * \param argv Usual argument vector.
515  * \param ct_ptr Filled in by this function.
516  * \param loglevel If not \p NULL, the number of the loglevel is stored here.
517  *
518  * This checks the command line options given by \a argc and \a argv, sets
519  * default values for the user name and the name of the rsa key file and reads
520  * further options from the config file.
521  *
522  * Upon successful return, \a ct_ptr points to a dynamically allocated and
523  * initialized client task struct.
524  *
525  * \return The number of non-option arguments in \a argc/argv on success,
526  * negative on errors.
527  */
528 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
529                 int *loglevel)
530 {
531         char *home = para_homedir();
532         int ret;
533         struct client_task *ct = para_calloc(sizeof(struct client_task));
534
535         *ct_ptr = ct;
536         ct->scc.fd = -1;
537         ret = -E_CLIENT_SYNTAX;
538         if (client_cmdline_parser(argc, argv, &ct->conf))
539                 goto out;
540         HANDLE_VERSION_FLAG("client", ct->conf);
541
542         ct->config_file = ct->conf.config_file_given?
543                 para_strdup(ct->conf.config_file_arg) :
544                 make_message("%s/.paraslash/client.conf", home);
545         ret = file_exists(ct->config_file);
546         if (!ret && ct->conf.config_file_given) {
547                 ret = -E_NO_CONFIG;
548                 goto out;
549         }
550         if (ret) {
551                 struct client_cmdline_parser_params params = {
552                         .override = 0,
553                         .initialize = 0,
554                         .check_required = 0,
555                         .check_ambiguity = 0,
556                         .print_errors = 0
557                 };
558                 ret = -E_BAD_CONFIG;
559                 if (client_cmdline_parser_config_file(ct->config_file,
560                         &ct->conf, &params))
561                         goto out;
562         }
563         ct->user = ct->conf.user_given?
564                 para_strdup(ct->conf.user_arg) : para_logname();
565
566         if (ct->conf.key_file_given)
567                 ct->key_file = para_strdup(ct->conf.key_file_arg);
568         else {
569                 ct->key_file = make_message("%s/.paraslash/key.%s",
570                         home, ct->user);
571                 if (!file_exists(ct->key_file)) {
572                         free(ct->key_file);
573                         ct->key_file = make_message("%s/.ssh/id_rsa", home);
574                 }
575         }
576
577         if (loglevel)
578                 *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
579         PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
580         PARA_INFO_LOG("config_file: %s\n", ct->config_file);
581         PARA_INFO_LOG("key_file: %s\n", ct->key_file);
582         ret = ct->conf.inputs_num;
583 out:
584         free(home);
585         if (ret < 0) {
586                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
587                 client_close(ct);
588                 *ct_ptr = NULL;
589         }
590         return ret;
591 }
592
593 /**
594  * Parse the client configuration and open a connection to para_server.
595  *
596  * \param argc See \ref client_parse_config.
597  * \param argv See \ref client_parse_config.
598  * \param ct_ptr See \ref client_parse_config.
599  * \param loglevel See \ref client_parse_config.
600  * \param parent See \ref client_connect().
601  * \param child See \ref client_connect().
602  * \param sched See \ref client_connect().
603  *
604  * This function combines client_parse_config() and client_connect(). It is
605  * considered a syntax error if no command was given, i.e. if the number
606  * of non-option arguments is zero.
607  *
608  * \return Standard.
609  */
610 int client_open(int argc, char *argv[], struct client_task **ct_ptr,
611                 int *loglevel, struct btr_node *parent, struct btr_node *child,
612                 struct sched *sched)
613 {
614         int ret = client_parse_config(argc, argv, ct_ptr, loglevel);
615
616         if (ret < 0)
617                 return ret;
618         if (ret == 0) {
619                 ret = -E_CLIENT_SYNTAX;
620                 goto fail;
621         }
622         ret = client_connect(*ct_ptr, sched, parent, child);
623         if (ret < 0)
624                 goto fail;
625         return 1;
626 fail:
627         client_close(*ct_ptr);
628         *ct_ptr = NULL;
629         return ret;
630 }