2 * Copyright (C) 1997-2006 Andre Noll <maan@systemlinux.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
19 /** \file client.c the client program used to connect to para_server */
25 #include "client.cmdline.h"
28 #include <openssl/rc4.h>
40 CL_RECEIVED_CHALLENGE
,
45 CL_RECEIVING_SERVER_OUTPUT
48 #define CLIENT_BUFSIZE 8192
50 struct private_client_data
{
53 struct client_args_info conf
;
61 char buf
[CLIENT_BUFSIZE
];
65 long unsigned challenge_nr
;
66 /* only used if stdin gets sent to para_server */
74 static struct private_client_data
*pcd
;
75 static struct stdin_task sit
;
76 static struct stdout_task sot
;
79 static void rc4_send(unsigned long len
, const unsigned char *indata
,
80 unsigned char *outdata
)
82 RC4(&pcd
->rc4_send_key
, len
, indata
, outdata
);
85 static void rc4_recv(unsigned long len
, const unsigned char *indata
,
86 unsigned char *outdata
)
88 RC4(&pcd
->rc4_recv_key
, len
, indata
, outdata
);
95 void para_log(int ll
, const char* fmt
,...)
99 /* ignore log message if loglevel is not high enough */
100 if (pcd
&& ll
< pcd
->conf
.loglevel_arg
)
103 vfprintf(stderr
, fmt
, argp
);
107 static void client_close(struct private_client_data
*pcd
)
114 free(pcd
->config_file
);
119 static int client_parse_config(int argc
, char *argv
[],
120 struct private_client_data
**pcd_ptr
)
122 char *home
= para_homedir();
125 struct private_client_data
*p
=
126 para_calloc(sizeof(struct private_client_data
));
129 cmdline_parser(argc
, argv
, &p
->conf
);
130 ret
= - E_CLIENT_SYNTAX
;
131 if (!p
->conf
.inputs_num
)
133 p
->user
= p
->conf
.user_given
?
134 para_strdup(p
->conf
.user_arg
) : para_logname();
136 p
->key_file
= p
->conf
.key_file_given
?
137 para_strdup(p
->conf
.key_file_arg
) :
138 make_message("%s/.paraslash/key.%s", home
, p
->user
);
140 p
->config_file
= p
->conf
.config_file_given
?
141 para_strdup(p
->conf
.config_file_arg
) :
142 make_message("%s/.paraslash/client.conf", home
);
143 ret
= stat(p
->config_file
, &statbuf
);
144 if (ret
&& p
->conf
.config_file_given
) {
149 cmdline_parser_configfile(p
->config_file
, &p
->conf
, 0, 0, 0);
153 "current loglevel: %d\n"
154 "using config_file: %s\n"
155 "using key_file: %s\n"
156 "connecting to %s:%d\n" ,
157 p
->conf
.loglevel_arg
,
160 p
->conf
.hostname_arg
, p
->conf
.server_port_arg
169 static void client_pre_select(struct sched
*s
, struct task
*t
)
171 struct private_client_data
*p
= t
->private_data
;
173 PARA_INFO_LOG("status %d\n", p
->status
);
179 switch (pcd
->status
) {
182 case CL_SENT_CH_RESPONSE
:
183 case CL_SENT_COMMAND
:
184 para_fd_set(pcd
->fd
, &s
->rfds
, &s
->max_fileno
);
188 case CL_RECEIVED_WELCOME
:
189 case CL_RECEIVED_CHALLENGE
:
190 case CL_RECEIVED_PROCEED
:
191 para_fd_set(pcd
->fd
, &s
->wfds
, &s
->max_fileno
);
195 case CL_RECEIVING_SERVER_OUTPUT
:
196 if (pcd
->loaded
< CLIENT_BUFSIZE
- 1) {
197 para_fd_set(pcd
->fd
, &s
->rfds
, &s
->max_fileno
);
201 case CL_SENDING_STDIN
:
203 PARA_INFO_LOG("loaded: %d\n", *p
->in_loaded
);
204 para_fd_set(p
->fd
, &s
->wfds
, &s
->max_fileno
);
208 t
->ret
= -E_INPUT_EOF
;
209 s
->timeout
.tv_sec
= 0;
210 s
->timeout
.tv_usec
= 1;
217 static ssize_t
client_recv_buffer(struct private_client_data
*p
)
219 ssize_t ret
= recv_buffer(p
->fd
, p
->buf
+ p
->loaded
,
220 CLIENT_BUFSIZE
- p
->loaded
);
222 return -E_SERVER_EOF
;
229 static void client_post_select(struct sched
*s
, struct task
*t
)
231 struct private_client_data
*p
= t
->private_data
;
233 PARA_INFO_LOG("status %d\n", p
->status
);
237 if (!p
->check_r
&& !p
->check_w
)
239 if (p
->check_r
&& !FD_ISSET(p
->fd
, &s
->rfds
))
241 if (p
->check_w
&& !FD_ISSET(p
->fd
, &s
->wfds
))
244 case CL_CONNECTED
: /* receive welcome message */
245 t
->ret
= client_recv_buffer(p
);
247 p
->status
= CL_RECEIVED_WELCOME
;
249 case CL_RECEIVED_WELCOME
: /* send auth command */
250 sprintf(p
->buf
, "auth %s%s", p
->conf
.plain_given
?
251 "" : "rc4 ", p
->user
);
252 PARA_INFO_LOG("--> %s\n", p
->buf
);
253 t
->ret
= send_buffer(p
->fd
, p
->buf
);
255 p
->status
= CL_SENT_AUTH
;
257 case CL_SENT_AUTH
: /* receive challenge number */
259 t
->ret
= client_recv_buffer(p
);
263 t
->ret
= -E_INVALID_CHALLENGE
;
264 PARA_ERROR_LOG("received the following: %s\n", p
->buf
);
267 PARA_INFO_LOG("%s", "<-- [challenge]\n");
268 /* decrypt challenge number */
269 t
->ret
= para_decrypt_challenge(p
->key_file
, &p
->challenge_nr
,
270 (unsigned char *) p
->buf
, 64);
272 p
->status
= CL_RECEIVED_CHALLENGE
;
274 case CL_RECEIVED_CHALLENGE
: /* send decrypted challenge */
275 PARA_INFO_LOG("--> %lu\n", p
->challenge_nr
);
276 t
->ret
= send_va_buffer(p
->fd
, "%s%lu", CHALLENGE_RESPONSE_MSG
,
279 p
->status
= CL_SENT_CH_RESPONSE
;
281 case CL_SENT_CH_RESPONSE
: /* read server response */
283 size_t bytes_received
;
284 unsigned char rc4_buf
[2 * RC4_KEY_LEN
] = "";
286 t
->ret
= client_recv_buffer(p
);
289 bytes_received
= t
->ret
;
290 PARA_INFO_LOG("++++ server info ++++\n%s\n++++ end of server "
291 "info ++++\n", p
->buf
);
292 /* check if server has sent "Proceed" message */
293 t
->ret
= -E_CLIENT_AUTH
;
294 if (!strstr(p
->buf
, PROCEED_MSG
))
297 p
->status
= CL_RECEIVED_PROCEED
;
298 if (bytes_received
< PROCEED_MSG_LEN
+ 32)
300 PARA_INFO_LOG("%s", "decrypting session key\n");
301 t
->ret
= para_decrypt_buffer(p
->key_file
, rc4_buf
,
302 (unsigned char *)p
->buf
+ PROCEED_MSG_LEN
+ 1,
303 bytes_received
- PROCEED_MSG_LEN
- 1);
306 RC4_set_key(&p
->rc4_send_key
, RC4_KEY_LEN
, rc4_buf
);
307 RC4_set_key(&p
->rc4_recv_key
, RC4_KEY_LEN
, rc4_buf
+ RC4_KEY_LEN
);
308 enable_crypt(p
->fd
, rc4_recv
, rc4_send
);
310 case CL_RECEIVED_PROCEED
: /* concat args and send command */
313 char *command
= NULL
;
314 for (i
= 0; i
< p
->conf
.inputs_num
; i
++) {
316 command
= make_message("%s\n%s", command
?
317 command
: "", p
->conf
.inputs
[i
]);
320 command
= para_strcat(command
, EOC_MSG
"\n");
321 PARA_INFO_LOG("--> %s\n", command
);
322 t
->ret
= send_buffer(p
->fd
, command
);
325 p
->status
= CL_SENT_COMMAND
;
328 case CL_SENT_COMMAND
:
330 t
->ret
= client_recv_buffer(p
);
333 t
->ret
= -E_HANDSHAKE_COMPLETE
;
334 if (strstr(p
->buf
, AWAITING_DATA_MSG
))
335 p
->status
= CL_SENDING_STDIN
;
337 p
->status
= CL_RECEIVING_SERVER_OUTPUT
;
339 case CL_SENDING_STDIN
: /* FIXME: might block */
340 PARA_INFO_LOG("loaded: %d\n", *p
->in_loaded
);
341 t
->ret
= send_bin_buffer(p
->fd
, p
->inbuf
, *p
->in_loaded
);
347 *p
->in_loaded
= 0; /* FIXME: short writes */
349 case CL_RECEIVING_SERVER_OUTPUT
:
350 t
->ret
= client_recv_buffer(p
);
355 static int client_open(struct private_client_data
*pcd
)
359 struct sockaddr_in their_addr
;
361 /* get the host info */
362 PARA_NOTICE_LOG("getting host info of %s\n",
363 pcd
->conf
.hostname_arg
);
364 ret
= get_host_info(pcd
->conf
.hostname_arg
, &he
);
372 /* init their_addr */
373 init_sockaddr(&their_addr
, pcd
->conf
.server_port_arg
, he
);
375 PARA_NOTICE_LOG("connecting to %s\n", pcd
->conf
.hostname_arg
);
376 ret
= para_connect(pcd
->fd
, &their_addr
);
379 pcd
->status
= CL_CONNECTED
;
380 pcd
->task
.pre_select
= client_pre_select
;
381 pcd
->task
.post_select
= client_post_select
;
382 pcd
->task
.private_data
= pcd
;
383 sprintf(pcd
->task
.status
, "client");
384 register_task(&pcd
->task
);
390 static void client_event_handler(struct task
*t
)
392 struct private_client_data
*p
= t
->private_data
;
394 PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t
->ret
));
395 if (t
->ret
!= -E_HANDSHAKE_COMPLETE
) {
400 if (p
->status
== CL_SENDING_STDIN
) {
401 stdin_set_defaults(&sit
);
402 sit
.buf
= para_malloc(sit
.bufsize
),
403 register_task(&sit
.task
);
405 p
->in_loaded
= &sit
.loaded
;
406 p
->in_eof
= &sit
.eof
;
409 stdout_set_defaults(&sot
);
411 sot
.loaded
= &p
->loaded
;
412 sot
.input_eof
= &p
->eof
;
413 register_task(&sot
.task
);
419 int main(int argc
, char *argv
[])
426 s
.default_timeout
.tv_sec
= 1;
427 s
.default_timeout
.tv_usec
= 0;
428 ret
= client_parse_config(argc
, argv
, &pcd
);
431 pcd
->task
.event_handler
= client_event_handler
;
432 ret
= client_open(pcd
);
439 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret
));
440 return ret
>= 0? EXIT_SUCCESS
: EXIT_FAILURE
;