Fix a bug in the output of para_audioc.
[paraslash.git] / client_common.c
1 /*
2  * Copyright (C) 1997-2009 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 <sys/types.h>
10 #include <dirent.h>
11 #include <openssl/rc4.h>
12
13 #include "para.h"
14 #include "error.h"
15 #include "list.h"
16 #include "sched.h"
17 #include "client.cmdline.h"
18 #include "crypt.h"
19 #include "rc4.h"
20 #include "net.h"
21 #include "fd.h"
22 #include "string.h"
23 #include "client.cmdline.h"
24 #include "client.h"
25 #include "hash.h"
26
27 /**
28  * Close the connection to para_server and free all resources.
29  *
30  * \param ct Pointer to the client data.
31  *
32  * \sa client_open.
33  */
34 void client_close(struct client_task *ct)
35 {
36         if (!ct)
37                 return;
38         if (ct->rc4c.fd >= 0)
39                 close(ct->rc4c.fd);
40         free(ct->buf);
41         free(ct->user);
42         free(ct->config_file);
43         free(ct->key_file);
44         client_cmdline_parser_free(&ct->conf);
45         free(ct);
46 }
47
48 /**
49  * The preselect hook for server commands.
50  *
51  * \param s Pointer to the scheduler.
52  * \param t Pointer to the task struct for this command.
53  *
54  * The task pointer must contain a pointer to the initialized client data
55  * structure as it is returned by client_open().
56  *
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 \a s accordingly.
59  *
60  * \sa register_task() client_open(), struct sched, struct task.
61  */
62 static void client_pre_select(struct sched *s, struct task *t)
63 {
64         struct client_task *ct = container_of(t, struct client_task, task);
65
66         ct->check_r = 0;
67         ct->check_w = 0;
68         if (ct->rc4c.fd < 0)
69                 return;
70         switch (ct->status) {
71         case CL_CONNECTED:
72         case CL_SENT_AUTH:
73         case CL_SENT_CH_RESPONSE:
74         case CL_SENT_COMMAND:
75                 para_fd_set(ct->rc4c.fd, &s->rfds, &s->max_fileno);
76                 ct->check_r = 1;
77                 return;
78
79         case CL_RECEIVED_WELCOME:
80         case CL_RECEIVED_CHALLENGE:
81         case CL_RECEIVED_PROCEED:
82                 para_fd_set(ct->rc4c.fd, &s->wfds, &s->max_fileno);
83                 ct->check_w = 1;
84                 return;
85
86         case CL_RECEIVING:
87                 if (ct->loaded < CLIENT_BUFSIZE - 1) {
88                         para_fd_set(ct->rc4c.fd, &s->rfds, &s->max_fileno);
89                         ct->check_r = 1;
90                 }
91                 return;
92         case CL_SENDING:
93                 if (!ct->in_loaded) /* stdin task not yet started */
94                         return;
95                 if (*ct->in_loaded) {
96                         PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded);
97                         para_fd_set(ct->rc4c.fd, &s->wfds, &s->max_fileno);
98                         ct->check_w = 1;
99                 } else {
100                         if (*ct->in_error) {
101                                 t->error = *ct->in_error;
102                                 s->timeout.tv_sec = 0;
103                                 s->timeout.tv_usec = 1;
104                         }
105                 }
106                 return;
107         }
108 }
109
110 static ssize_t client_recv_buffer(struct client_task *ct)
111 {
112         ssize_t ret;
113
114         if (ct->status < CL_SENT_CH_RESPONSE)
115                 ret = recv_buffer(ct->rc4c.fd, ct->buf + ct->loaded,
116                         CLIENT_BUFSIZE - ct->loaded);
117         else
118                 ret = rc4_recv_buffer(&ct->rc4c, ct->buf + ct->loaded,
119                         CLIENT_BUFSIZE - ct->loaded);
120         if (!ret)
121                 return -E_SERVER_EOF;
122         if (ret > 0)
123                 ct->loaded += ret;
124         return ret;
125 }
126
127 /**
128  * The post select hook for client commands.
129  *
130  * \param s Pointer to the scheduler.
131  * \param t Pointer to the task struct for this command.
132  *
133  * Depending on the current state of the connection and the status of the read
134  * and write fd sets of \a s, this function performs the necessary steps to
135  * authenticate the connection, to send the command given by \a t->private_data
136  * and to receive para_server's output, if any.
137  *
138  * \sa struct sched, struct task.
139  */
140 static void client_post_select(struct sched *s, struct task *t)
141 {
142         struct client_task *ct = container_of(t, struct client_task, task);
143         unsigned char crypt_buf[1024];
144
145         t->error = 0;
146         if (ct->rc4c.fd < 0)
147                 return;
148         if (!ct->check_r && !ct->check_w)
149                 return;
150         if (ct->check_r && !FD_ISSET(ct->rc4c.fd, &s->rfds))
151                 return;
152         if (ct->check_w && !FD_ISSET(ct->rc4c.fd, &s->wfds))
153                 return;
154         switch (ct->status) {
155         case CL_CONNECTED: /* receive welcome message */
156                 t->error = client_recv_buffer(ct);
157                 if (t->error < 0)
158                         goto err;
159                 ct->status = CL_RECEIVED_WELCOME;
160                 return;
161         case CL_RECEIVED_WELCOME: /* send auth command */
162                 sprintf(ct->buf, AUTH_REQUEST_MSG "%s", ct->user);
163                 PARA_INFO_LOG("--> %s\n", ct->buf);
164                 t->error = send_buffer(ct->rc4c.fd, ct->buf);
165                 if (t->error < 0)
166                         goto err;
167                 ct->status = CL_SENT_AUTH;
168                 return;
169         case CL_SENT_AUTH: /* receive challenge and rc4 keys */
170                 ct->loaded = 0;
171                 t->error = client_recv_buffer(ct);
172                 if (t->error < 0)
173                         goto err;
174                 PARA_INFO_LOG("<-- [challenge] (%d bytes)\n", t->error);
175                 /* decrypt challenge/rc4 buffer  */
176                 t->error = para_decrypt_buffer(ct->key_file, crypt_buf,
177                         (unsigned char *)ct->buf, t->error);
178                 if (t->error < 0)
179                         goto err;
180                 ct->status = CL_RECEIVED_CHALLENGE;
181                 RC4_set_key(&ct->rc4c.send_key, RC4_KEY_LEN,
182                         crypt_buf + CHALLENGE_SIZE);
183                 RC4_set_key(&ct->rc4c.recv_key, RC4_KEY_LEN,
184                         crypt_buf + CHALLENGE_SIZE + RC4_KEY_LEN);
185                 return;
186         case CL_RECEIVED_CHALLENGE:
187                 {
188                 unsigned char challenge_sha1[HASH_SIZE];
189                 /* send sha1 of decrypted challenge */
190                 sha1_hash((char *)crypt_buf, CHALLENGE_SIZE, challenge_sha1);
191                 hash_to_asc(challenge_sha1, ct->buf);
192                 PARA_INFO_LOG("--> %s\n", ct->buf);
193                 t->error = send_bin_buffer(ct->rc4c.fd, (char *)challenge_sha1,
194                         HASH_SIZE);
195                 if (t->error < 0)
196                         goto err;
197                 ct->status = CL_SENT_CH_RESPONSE;
198                 return;
199                 }
200         case CL_SENT_CH_RESPONSE: /* read server response */
201                 {
202                 size_t bytes_received;
203                 ct->loaded = 0;
204                 t->error = client_recv_buffer(ct);
205                 if (t->error < 0)
206                         goto err;
207                 bytes_received = t->error;
208                 /* check if server has sent "Proceed" message */
209                 t->error = -E_CLIENT_AUTH;
210                 if (bytes_received < PROCEED_MSG_LEN)
211                         goto err;
212                 if (!strstr(ct->buf, PROCEED_MSG))
213                         goto err;
214                 ct->status = CL_RECEIVED_PROCEED;
215                 t->error = 0;
216                 return;
217                 }
218         case CL_RECEIVED_PROCEED: /* concat args and send command */
219                 {
220                 int i;
221                 char *command = NULL;
222                 for (i = 0; i < ct->conf.inputs_num; i++) {
223                         char *tmp = command;
224                         command = make_message("%s\n%s", command?
225                                 command : "", ct->conf.inputs[i]);
226                         free(tmp);
227                 }
228                 command = para_strcat(command, EOC_MSG "\n");
229                 PARA_DEBUG_LOG("--> %s\n", command);
230                 t->error = rc4_send_buffer(&ct->rc4c, command);
231                 free(command);
232                 if (t->error < 0)
233                         goto err;
234                 ct->status = CL_SENT_COMMAND;
235                 return;
236                 }
237         case CL_SENT_COMMAND:
238                 ct->loaded = 0;
239                 t->error = client_recv_buffer(ct);
240                 if (t->error < 0)
241                         goto err;
242                 if (strstr(ct->buf, AWAITING_DATA_MSG))
243                         ct->status = CL_SENDING;
244                 else
245                         ct->status = CL_RECEIVING;
246                 return;
247         case CL_SENDING:
248                 PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded);
249                 t->error = rc4_send_bin_buffer(&ct->rc4c, ct->inbuf,
250                         *ct->in_loaded);
251                 if (t->error < 0)
252                         goto err;
253                 *ct->in_loaded = 0;
254                 return;
255         case CL_RECEIVING:
256                 t->error = client_recv_buffer(ct);
257                 if (t->error < 0)
258                         goto err;
259                 return;
260         }
261 err:
262         if (t->error != -E_SERVER_EOF)
263                 PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
264 }
265
266 /* connect to para_server and register the client task */
267 static int client_connect(struct client_task *ct)
268 {
269         int ret;
270
271         ct->rc4c.fd = -1;
272         ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, ct->conf.hostname_arg,
273                 ct->conf.server_port_arg);
274         if (ret < 0)
275                 return ret;
276         ct->rc4c.fd = ret;
277         ct->status = CL_CONNECTED;
278         ret = mark_fd_nonblocking(ct->rc4c.fd);
279         if (ret < 0)
280                 goto err_out;
281         ct->task.pre_select = client_pre_select;
282         ct->task.post_select = client_post_select;
283         sprintf(ct->task.status, "client");
284         register_task(&ct->task);
285         return 1;
286 err_out:
287         close(ct->rc4c.fd);
288         ct->rc4c.fd = -1;
289         return ret;
290 }
291
292 /**
293  * Open connection to para_server.
294  *
295  * \param argc Usual argument count.
296  * \param argv Usual argument vector.
297  * \param ct_ptr Points to dynamically allocated and initialized client task
298  * struct upon successful return.
299  * \param loglevel If not \p NULL, the number of the loglevel is stored here.
300  *
301  * Check the command line options given by \a argc and argv, set default values
302  * for user name and rsa key file, read further option from the config file.
303  * Finally, establish a connection to para_server.
304  *
305  * \return Standard.
306  */
307 int client_open(int argc, char *argv[], struct client_task **ct_ptr,
308                 int *loglevel)
309 {
310         char *home = para_homedir();
311         int ret;
312         struct client_task *ct = para_calloc(sizeof(struct client_task));
313
314         ct->buf = para_malloc(CLIENT_BUFSIZE);
315         *ct_ptr = ct;
316         ct->rc4c.fd = -1;
317         ret = -E_CLIENT_SYNTAX;
318         if (client_cmdline_parser(argc, argv, &ct->conf))
319                 goto out;
320         HANDLE_VERSION_FLAG("client", ct->conf);
321         ret = -E_CLIENT_SYNTAX;
322         if (!ct->conf.inputs_num)
323                 goto out;
324         ct->user = ct->conf.user_given?
325                 para_strdup(ct->conf.user_arg) : para_logname();
326
327         ct->key_file = ct->conf.key_file_given?
328                 para_strdup(ct->conf.key_file_arg) :
329                 make_message("%s/.paraslash/key.%s", home, ct->user);
330
331         ct->config_file = ct->conf.config_file_given?
332                 para_strdup(ct->conf.config_file_arg) :
333                 make_message("%s/.paraslash/client.conf", home);
334         ret = file_exists(ct->config_file);
335         if (!ret && ct->conf.config_file_given) {
336                 ret = -E_NO_CONFIG;
337                 goto out;
338         }
339         if (ret) {
340                 struct client_cmdline_parser_params params = {
341                         .override = 0,
342                         .initialize = 0,
343                         .check_required = 0,
344                         .check_ambiguity = 0,
345                         .print_errors = 0
346                 };
347                 ret = -E_BAD_CONFIG;
348                 if (client_cmdline_parser_config_file(ct->config_file,
349                         &ct->conf, &params))
350                         goto out;
351         }
352         if (loglevel)
353                 *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
354         PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
355         PARA_INFO_LOG("config_file: %s\n", ct->config_file);
356         PARA_INFO_LOG("key_file: %s\n", ct->key_file);
357         PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
358                 ct->conf.server_port_arg);
359         ret = client_connect(ct);
360 out:
361         free(home);
362         if (ret < 0) {
363                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
364                 client_close(ct);
365                 *ct_ptr = NULL;
366         }
367         return ret;
368 }