Document RC4_ALIGN.
[paraslash.git] / client_common.c
1 /*
2  * Copyright (C) 1997-2011 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
25 /** The size of the receiving buffer. */
26 #define CLIENT_BUFSIZE 4000
27
28 /**
29  * Close the connection to para_server and free all resources.
30  *
31  * \param ct Pointer to the client data.
32  *
33  * \sa client_open.
34  */
35 void client_close(struct client_task *ct)
36 {
37         if (!ct)
38                 return;
39         if (ct->scc.fd >= 0)
40                 close(ct->scc.fd);
41         sc_free(ct->scc.recv);
42         sc_free(ct->scc.send);
43         free(ct->user);
44         free(ct->config_file);
45         free(ct->key_file);
46         client_cmdline_parser_free(&ct->conf);
47         free(ct);
48 }
49
50 /**
51  * The preselect hook for server commands.
52  *
53  * \param s Pointer to the scheduler.
54  * \param t Pointer to the task struct for this command.
55  *
56  * The task pointer must contain a pointer to the initialized client data
57  * structure as it is returned by client_open().
58  *
59  * This function checks the state of the connection and adds the file descriptor
60  * of the connection to the read or write fd set of \a s accordingly.
61  *
62  * \sa register_task() client_open(), struct sched, struct task.
63  */
64 static void client_pre_select(struct sched *s, struct task *t)
65 {
66         int ret;
67         struct client_task *ct = container_of(t, struct client_task, task);
68         struct btr_node *btrn = ct->btrn;
69
70         if (ct->scc.fd < 0)
71                 return;
72         switch (ct->status) {
73         case CL_CONNECTED:
74         case CL_SENT_AUTH:
75         case CL_SENT_CH_RESPONSE:
76         case CL_SENT_COMMAND:
77                 para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
78                 return;
79
80         case CL_RECEIVED_WELCOME:
81         case CL_RECEIVED_PROCEED:
82                 para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
83                 return;
84
85         case CL_RECEIVING:
86                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
87                 if (ret != 0) {
88                         if (ret < 0)
89                                 sched_min_delay(s);
90                         else
91                                 para_fd_set(ct->scc.fd, &s->rfds,
92                                         &s->max_fileno);
93                 }
94                 return;
95         case CL_SENDING:
96                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
97                 if (ret != 0) {
98                         if (ret < 0)
99                                 sched_min_delay(s);
100                         else
101                                 para_fd_set(ct->scc.fd, &s->wfds,
102                                         &s->max_fileno);
103                 }
104                 return;
105         }
106 }
107
108 static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
109                 char *buf, size_t sz, size_t *n)
110 {
111         int ret;
112
113         if (ct->status < CL_SENT_CH_RESPONSE)
114                 return read_nonblock(ct->scc.fd, buf, sz, rfds, n);
115
116         *n = 0;
117         ret = sc_recv_buffer(&ct->scc, buf, sz);
118         /*
119          * sc_recv_buffer is used with blocking fds elsewhere, so it
120          * does not use the nonblock-API. Therefore we need to
121          * check for EOF and EAGAIN.
122          */
123         if (ret == 0)
124                 return -E_SERVER_EOF;
125         if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
126                 return 0;
127         if (ret < 0)
128                 return ret;
129         *n = ret;
130         return 0;
131 }
132
133 /**
134  * The post select hook for client commands.
135  *
136  * \param s Pointer to the scheduler.
137  * \param t Pointer to the task struct for this command.
138  *
139  * Depending on the current state of the connection and the status of the read
140  * and write fd sets of \a s, this function performs the necessary steps to
141  * authenticate the connection, to send the command given by \a t->private_data
142  * and to receive para_server's output, if any.
143  *
144  * \sa struct sched, struct task.
145  */
146 static void client_post_select(struct sched *s, struct task *t)
147 {
148         struct client_task *ct = container_of(t, struct client_task, task);
149         struct btr_node *btrn = ct->btrn;
150         int ret = 0;
151         size_t n;
152         char buf[CLIENT_BUFSIZE];
153
154         t->error = 0;
155         if (ct->scc.fd < 0)
156                 return;
157         switch (ct->status) {
158         case CL_CONNECTED: /* receive welcome message */
159                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
160                 if (ret < 0 || n == 0)
161                         goto out;
162                 ct->status = CL_RECEIVED_WELCOME;
163                 return;
164         case CL_RECEIVED_WELCOME: /* send auth command */
165                 sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
166                 PARA_INFO_LOG("--> %s\n", buf);
167                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
168                         return;
169                 ret = send_buffer(ct->scc.fd, buf);
170                 if (ret < 0)
171                         goto out;
172                 ct->status = CL_SENT_AUTH;
173                 return;
174         case CL_SENT_AUTH:
175                 /*
176                  * Receive challenge and session keys, decrypt the challenge and
177                  * send back the hash of the decrypted challenge.
178                  */
179                 {
180                 /* decrypted challenge/session key buffer */
181                 unsigned char crypt_buf[1024];
182                 /* the SHA1 of the decrypted challenge */
183                 unsigned char challenge_hash[HASH_SIZE];
184
185                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
186                 if (ret < 0 || n == 0)
187                         goto out;
188                 PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
189                 ret = priv_decrypt(ct->key_file, crypt_buf,
190                         (unsigned char *)buf, n);
191                 if (ret < 0)
192                         goto out;
193                 hash_function((char *)crypt_buf, CHALLENGE_SIZE, challenge_hash);
194                 ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
195                 ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
196                         SESSION_KEY_LEN);
197                 hash_to_asc(challenge_hash, buf);
198                 PARA_INFO_LOG("--> %s\n", buf);
199                 ret = send_bin_buffer(ct->scc.fd, (char *)challenge_hash,
200                         HASH_SIZE);
201                 if (ret < 0)
202                         goto out;
203                 ct->status = CL_SENT_CH_RESPONSE;
204                 return;
205                 }
206         case CL_SENT_CH_RESPONSE: /* read server response */
207                 {
208                 ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
209                 if (ret < 0 || n == 0)
210                         goto out;
211                 /* check if server has sent "Proceed" message */
212                 ret = -E_CLIENT_AUTH;
213                 if (n < PROCEED_MSG_LEN)
214                         goto out;
215                 if (!strstr(buf, PROCEED_MSG))
216                         goto out;
217                 ct->status = CL_RECEIVED_PROCEED;
218                 return;
219                 }
220         case CL_RECEIVED_PROCEED: /* concat args and send command */
221                 {
222                 int i;
223                 char *command = NULL;
224                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
225                         return;
226                 for (i = 0; i < ct->conf.inputs_num; i++) {
227                         char *tmp = command;
228                         command = make_message("%s\n%s", command?
229                                 command : "", ct->conf.inputs[i]);
230                         free(tmp);
231                 }
232                 command = para_strcat(command, EOC_MSG "\n");
233                 PARA_DEBUG_LOG("--> %s\n", command);
234                 ret = sc_send_buffer(&ct->scc, command);
235                 free(command);
236                 if (ret < 0)
237                         goto out;
238                 ct->status = CL_SENT_COMMAND;
239                 return;
240                 }
241         case CL_SENT_COMMAND:
242                 {
243                 char *buf2;
244                 /* can not use "buf" here because we need a malloced buffer */
245                 buf2 = para_malloc(CLIENT_BUFSIZE);
246                 ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
247                 if (n > 0) {
248                         if (strstr(buf2, AWAITING_DATA_MSG)) {
249                                 free(buf2);
250                                 ct->status = CL_SENDING;
251                                 return;
252                         }
253                         ct->status = CL_RECEIVING;
254                         btr_add_output(buf2, n, btrn);
255                 } else
256                         free(buf2);
257                 goto out;
258                 }
259         case CL_SENDING:
260                 {
261                 char *buf2;
262                 size_t sz;
263                 ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
264                 if (ret < 0)
265                         goto out;
266                 if (ret == 0)
267                         return;
268                 if (!FD_ISSET(ct->scc.fd, &s->wfds))
269                         return;
270                 sz = btr_next_buffer(btrn, &buf2);
271                 ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
272                 if (ret < 0)
273                         goto out;
274                 btr_consume(btrn, sz);
275                 return;
276                 }
277         case CL_RECEIVING:
278                 {
279                 char *buf2;
280                 ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
281                 if (ret < 0)
282                         goto out;
283                 if (ret == 0)
284                         return;
285                 /*
286                  * The FD_ISSET() is not strictly necessary, but is allows us
287                  * to skip the malloc below if there is nothing to read anyway.
288                  */
289                 if (!FD_ISSET(ct->scc.fd, &s->rfds))
290                         return;
291                 buf2 = para_malloc(CLIENT_BUFSIZE);
292                 ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
293                 if (n > 0) {
294                         buf2 = para_realloc(buf2, n);
295                         btr_add_output(buf2, n, btrn);
296                 } else
297                         free(buf2);
298                 goto out;
299                 }
300         }
301 out:
302         t->error = ret;
303         if (ret < 0) {
304                 if (ret != -E_SERVER_EOF && ret != -E_BTR_EOF)
305                         PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
306                 btr_remove_node(btrn);
307         }
308 }
309
310 /* connect to para_server and register the client task */
311 static int client_connect(struct client_task *ct)
312 {
313         int ret;
314
315         ct->scc.fd = -1;
316         ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
317                                                ct->conf.server_port_arg);
318         if (ret < 0)
319                 return ret;
320         ct->scc.fd = ret;
321         ct->status = CL_CONNECTED;
322         ret = mark_fd_nonblocking(ct->scc.fd);
323         if (ret < 0)
324                 goto err_out;
325         ct->task.pre_select = client_pre_select;
326         ct->task.post_select = client_post_select;
327         sprintf(ct->task.status, "client");
328         register_task(&ct->task);
329         return 1;
330 err_out:
331         close(ct->scc.fd);
332         ct->scc.fd = -1;
333         return ret;
334 }
335
336 /**
337  * Open connection to para_server.
338  *
339  * \param argc Usual argument count.
340  * \param argv Usual argument vector.
341  * \param ct_ptr Points to dynamically allocated and initialized client task
342  * struct upon successful return.
343  * \param loglevel If not \p NULL, the number of the loglevel is stored here.
344  * \param parent Add the new buffer tree node as a child of this node.
345  * \param child Add the new buffer tree node as a parent of this node.
346  *
347  * Check the command line options given by \a argc and argv, set default values
348  * for user name and rsa key file, read further option from the config file.
349  * Finally, establish a connection to para_server.
350  *
351  * \return Standard.
352  */
353 int client_open(int argc, char *argv[], struct client_task **ct_ptr,
354                 int *loglevel, struct btr_node *parent, struct btr_node *child)
355 {
356         char *home = para_homedir();
357         int ret;
358         struct client_task *ct = para_calloc(sizeof(struct client_task));
359
360         ct->btrn = btr_new_node(&(struct btr_node_description)
361                 EMBRACE(.name = "client", .parent = parent, .child = child));
362         *ct_ptr = ct;
363         ct->scc.fd = -1;
364         ret = -E_CLIENT_SYNTAX;
365         if (client_cmdline_parser(argc, argv, &ct->conf))
366                 goto out;
367         HANDLE_VERSION_FLAG("client", ct->conf);
368         ret = -E_CLIENT_SYNTAX;
369         if (!ct->conf.inputs_num)
370                 goto out;
371
372         ct->config_file = ct->conf.config_file_given?
373                 para_strdup(ct->conf.config_file_arg) :
374                 make_message("%s/.paraslash/client.conf", home);
375         ret = file_exists(ct->config_file);
376         if (!ret && ct->conf.config_file_given) {
377                 ret = -E_NO_CONFIG;
378                 goto out;
379         }
380         if (ret) {
381                 struct client_cmdline_parser_params params = {
382                         .override = 0,
383                         .initialize = 0,
384                         .check_required = 0,
385                         .check_ambiguity = 0,
386                         .print_errors = 0
387                 };
388                 ret = -E_BAD_CONFIG;
389                 if (client_cmdline_parser_config_file(ct->config_file,
390                         &ct->conf, &params))
391                         goto out;
392         }
393         ct->user = ct->conf.user_given?
394                 para_strdup(ct->conf.user_arg) : para_logname();
395
396         ct->key_file = ct->conf.key_file_given?
397                 para_strdup(ct->conf.key_file_arg) :
398                 make_message("%s/.paraslash/key.%s", home, ct->user);
399
400         if (loglevel)
401                 *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
402         PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
403         PARA_INFO_LOG("config_file: %s\n", ct->config_file);
404         PARA_INFO_LOG("key_file: %s\n", ct->key_file);
405         PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
406                 ct->conf.server_port_arg);
407         ret = client_connect(ct);
408 out:
409         free(home);
410         if (ret < 0) {
411                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
412                 btr_remove_node(ct->btrn);
413                 btr_free_node(ct->btrn);
414                 client_close(ct);
415                 *ct_ptr = NULL;
416         }
417         return ret;
418 }