1 /*
2
3 client_ftp.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2001 - 2014 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27 /* File transmission session */
28 struct SilcClientFtpSessionStruct {
29 SilcClient client; /* Client */
30 SilcClientConnection server_conn; /* Connection to server */
31 SilcClientConnection conn; /* Connection to remote host */
32 SilcClientEntry client_entry; /* The client entry */
33 SilcClientListener listener; /* Listener */
34 SilcAsyncOperation op; /* Operation for connecting */
35 SilcClientConnectionParams params; /* Connection params */
36 SilcPublicKey public_key; /* Public key used in key exchange */
37 SilcPrivateKey private_key; /* Private key used in key exchange */
38 SilcUInt32 session_id; /* File transfer ID */
39
40 SilcClientFileMonitor monitor; /* File transfer monitor callback */
41 void *monitor_context;
42 SilcClientFileAskName ask_name; /* File name asking callback */
43 void *ask_name_context;
44 char *filepath; /* The remote filename */
45 char *path; /* User given path to save the file */
46
47 SilcStream stream; /* Wrapped SilcPacketStream */
48 SilcSFTP sftp; /* SFTP server/client */
49 SilcSFTPFilesystem fs; /* SFTP memory file system */
50 SilcSFTPHandle dir_handle; /* SFTP session directory handle */
51 SilcSFTPHandle read_handle; /* SFTP session file handles */
52
53 char *hostname; /* Remote host */
54 SilcUInt16 port; /* Remote port */
55 SilcUInt64 filesize; /* File size */
56 SilcUInt64 read_offset; /* Current read offset */
57 int fd; /* File descriptor */
58 unsigned int initiator : 1; /* File sender sets this to TRUE */
59 unsigned int closed : 1; /* silc_client_file_close called */
60 };
61
62 /************************* SFTP Server Callbacks ****************************/
63
64 /* SFTP monitor callback for SFTP server. This reports the application
65 how the transmission is going along. This function is for the client
66 who made the file available for download. */
67
silc_client_ftp_monitor(SilcSFTP sftp,SilcSFTPMonitors type,const SilcSFTPMonitorData data,void * context)68 static void silc_client_ftp_monitor(SilcSFTP sftp,
69 SilcSFTPMonitors type,
70 const SilcSFTPMonitorData data,
71 void *context)
72 {
73 SilcClientFtpSession session = (SilcClientFtpSession)context;
74
75 if (type == SILC_SFTP_MONITOR_READ) {
76 /* Call the monitor for application */
77 if (session->monitor)
78 (*session->monitor)(session->client, session->conn,
79 SILC_CLIENT_FILE_MONITOR_SEND,
80 SILC_CLIENT_FILE_OK,
81 data->offset, session->filesize,
82 session->client_entry, session->session_id,
83 session->filepath, session->monitor_context);
84 }
85 }
86
87 /************************* SFTP Client Callbacks ****************************/
88
89 /* Returns the read data. This is the downloader's function (client side)
90 to receive the read data and read more until EOF is received from
91 the other side. This will also monitor the transmission and notify
92 the application. */
93
silc_client_ftp_data(SilcSFTP sftp,SilcSFTPStatus status,const unsigned char * data,SilcUInt32 data_len,void * context)94 static void silc_client_ftp_data(SilcSFTP sftp,
95 SilcSFTPStatus status,
96 const unsigned char *data,
97 SilcUInt32 data_len,
98 void *context)
99 {
100 SilcClientFtpSession session = (SilcClientFtpSession)context;
101
102 SILC_LOG_DEBUG(("Start"));
103
104 if (status == SILC_SFTP_STATUS_EOF) {
105 /* EOF received */
106
107 /* Close the handle */
108 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
109 session->read_handle = NULL;
110
111 /* Close the real file descriptor */
112 silc_file_close(session->fd);
113 return;
114 }
115
116 if (status != SILC_SFTP_STATUS_OK) {
117 /* Call monitor callback */
118 if (session->monitor)
119 (*session->monitor)(session->client, session->conn,
120 SILC_CLIENT_FILE_MONITOR_ERROR,
121 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
122 SILC_CLIENT_FILE_NO_SUCH_FILE :
123 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
124 SILC_CLIENT_FILE_PERMISSION_DENIED :
125 SILC_CLIENT_FILE_ERROR), 0, 0,
126 session->client_entry, session->session_id,
127 session->filepath, session->monitor_context);
128
129 /* Close the handle */
130 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
131 session->read_handle = NULL;
132
133 /* Close the real file descriptor */
134 silc_file_close(session->fd);
135 return;
136 }
137
138 /* Read more, until EOF is received */
139 session->read_offset += data_len;
140 silc_sftp_read(sftp, session->read_handle, session->read_offset,
141 SILC_PACKET_MAX_LEN - 1024,
142 silc_client_ftp_data, session);
143
144 /* Write the read data to the real file */
145 silc_file_write(session->fd, data, data_len);
146
147 /* Call monitor callback */
148 if (session->monitor)
149 (*session->monitor)(session->client, session->conn,
150 SILC_CLIENT_FILE_MONITOR_RECEIVE,
151 SILC_CLIENT_FILE_OK,
152 session->read_offset, session->filesize,
153 session->client_entry, session->session_id,
154 session->filepath, session->monitor_context);
155 }
156
157 /* Returns handle for the opened file. This is the downloader's function.
158 This will begin reading the data from the file. */
159
silc_client_ftp_open_handle(SilcSFTP sftp,SilcSFTPStatus status,SilcSFTPHandle handle,void * context)160 static void silc_client_ftp_open_handle(SilcSFTP sftp,
161 SilcSFTPStatus status,
162 SilcSFTPHandle handle,
163 void *context)
164 {
165 SilcClientFtpSession session = (SilcClientFtpSession)context;
166 char path[512];
167
168 SILC_LOG_DEBUG(("Start"));
169
170 if (status != SILC_SFTP_STATUS_OK) {
171 /* Call monitor callback */
172 if (session->monitor)
173 (*session->monitor)(session->client, session->conn,
174 SILC_CLIENT_FILE_MONITOR_ERROR,
175 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
176 SILC_CLIENT_FILE_NO_SUCH_FILE :
177 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
178 SILC_CLIENT_FILE_PERMISSION_DENIED :
179 SILC_CLIENT_FILE_ERROR), 0, 0,
180 session->client_entry, session->session_id,
181 session->filepath, session->monitor_context);
182 return;
183 }
184
185 /* Open the actual local file */
186 memset(path, 0, sizeof(path));
187 silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
188 session->path : "", session->filepath);
189 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
190 if (session->fd < 0) {
191 /* Call monitor callback */
192 session->conn->context_type = SILC_ID_CLIENT;
193 session->conn->client_entry = session->client_entry;
194 session->client->internal->ops->say(session->client, session->conn,
195 SILC_CLIENT_MESSAGE_ERROR,
196 "File `%s' open failed: %s",
197 session->filepath,
198 strerror(errno));
199 session->conn->context_type = SILC_ID_NONE;
200
201 if (session->monitor)
202 (*session->monitor)(session->client, session->conn,
203 SILC_CLIENT_FILE_MONITOR_ERROR,
204 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
205 session->client_entry, session->session_id,
206 session->filepath, session->monitor_context);
207 return;
208 }
209
210 session->read_handle = handle;
211
212 /* Now, start reading the file */
213 silc_sftp_read(sftp, session->read_handle, session->read_offset,
214 SILC_PACKET_MAX_LEN - 1024,
215 silc_client_ftp_data, session);
216
217 /* Call monitor callback */
218 if (session->monitor)
219 (*session->monitor)(session->client, session->conn,
220 SILC_CLIENT_FILE_MONITOR_RECEIVE,
221 SILC_CLIENT_FILE_OK,
222 session->read_offset, session->filesize,
223 session->client_entry, session->session_id,
224 session->filepath, session->monitor_context);
225 }
226
227 /* Ask filename completion callback. Delivers the filepath selected by
228 user. */
229
silc_client_ftp_ask_name(const char * filepath,void * context)230 static void silc_client_ftp_ask_name(const char *filepath,
231 void *context)
232 {
233 SilcClientFtpSession session = (SilcClientFtpSession)context;
234 SilcSFTPAttributesStruct attr;
235 char *remote_file = NULL;
236
237 SILC_LOG_DEBUG(("Start"));
238
239 if (filepath) {
240 remote_file = session->filepath;
241 session->filepath = NULL;
242 silc_free(session->path);
243 session->path = NULL;
244 session->filepath = strdup(filepath);
245 } else {
246 remote_file = strdup(session->filepath);
247 }
248
249 /* Now open the file */
250 memset(&attr, 0, sizeof(attr));
251 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
252 silc_client_ftp_open_handle, session);
253
254 /* Close the directory handle */
255 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
256 session->dir_handle = NULL;
257
258 silc_free(remote_file);
259 }
260
261 /* Returns the file name available for download. This is the downloader's
262 function. */
263
silc_client_ftp_readdir_name(SilcSFTP sftp,SilcSFTPStatus status,const SilcSFTPName name,void * context)264 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
265 SilcSFTPStatus status,
266 const SilcSFTPName name,
267 void *context)
268 {
269 SilcClientFtpSession session = (SilcClientFtpSession)context;
270
271 SILC_LOG_DEBUG(("Start"));
272
273 if (status != SILC_SFTP_STATUS_OK) {
274 /* Call monitor callback */
275 if (session->monitor)
276 (*session->monitor)(session->client, session->conn,
277 SILC_CLIENT_FILE_MONITOR_ERROR,
278 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
279 SILC_CLIENT_FILE_NO_SUCH_FILE :
280 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
281 SILC_CLIENT_FILE_PERMISSION_DENIED :
282 SILC_CLIENT_FILE_ERROR), 0, 0,
283 session->client_entry, session->session_id,
284 session->filepath, session->monitor_context);
285 return;
286 }
287
288 /* Save the important attributes like filename and file size */
289 session->filepath = strdup(name->filename[0]);
290 session->filesize = name->attrs[0]->size;
291
292 /* If the path was not provided, ask from application where to save the
293 downloaded file. */
294 if (!session->path && session->ask_name) {
295 session->ask_name(session->client, session->conn, session->session_id,
296 name->filename[0], silc_client_ftp_ask_name, session,
297 session->ask_name_context);
298 return;
299 }
300
301 /* Start downloading immediately to current directory. */
302 silc_client_ftp_ask_name(NULL, session);
303 }
304
305 /* Returns the file handle after giving opendir command. This is the
306 downloader's function. */
307
silc_client_ftp_opendir_handle(SilcSFTP sftp,SilcSFTPStatus status,SilcSFTPHandle handle,void * context)308 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
309 SilcSFTPStatus status,
310 SilcSFTPHandle handle,
311 void *context)
312 {
313 SilcClientFtpSession session = (SilcClientFtpSession)context;
314
315 SILC_LOG_DEBUG(("Start"));
316
317 if (status != SILC_SFTP_STATUS_OK) {
318 /* Call monitor callback */
319 if (session->monitor)
320 (*session->monitor)(session->client, session->conn,
321 SILC_CLIENT_FILE_MONITOR_ERROR,
322 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
323 SILC_CLIENT_FILE_NO_SUCH_FILE :
324 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
325 SILC_CLIENT_FILE_PERMISSION_DENIED :
326 SILC_CLIENT_FILE_ERROR), 0, 0,
327 session->client_entry, session->session_id,
328 session->filepath, session->monitor_context);
329 return;
330 }
331
332 /* Now, read the directory */
333 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
334 session->dir_handle = handle;
335 }
336
337 /* SFTP version callback for SFTP client. This is the downloader's function
338 after initializing the SFTP connection to the remote client. This will
339 find out the filename available for download. */
340
silc_client_ftp_version(SilcSFTP sftp,SilcSFTPStatus status,SilcSFTPVersion version,void * context)341 static void silc_client_ftp_version(SilcSFTP sftp,
342 SilcSFTPStatus status,
343 SilcSFTPVersion version,
344 void *context)
345 {
346 SilcClientFtpSession session = (SilcClientFtpSession)context;
347
348 SILC_LOG_DEBUG(("Start"));
349
350 if (status != SILC_SFTP_STATUS_OK) {
351 /* Call monitor callback */
352 if (session->monitor)
353 (*session->monitor)(session->client, session->conn,
354 SILC_CLIENT_FILE_MONITOR_ERROR,
355 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
356 SILC_CLIENT_FILE_NO_SUCH_FILE :
357 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
358 SILC_CLIENT_FILE_PERMISSION_DENIED :
359 SILC_CLIENT_FILE_ERROR), 0, 0,
360 session->client_entry, session->session_id,
361 session->filepath, session->monitor_context);
362 return;
363 }
364
365 /* The SFTP session is open, now retrieve the info about available file. */
366 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
367 }
368
369 /* SFTP stream error callback */
370
silc_client_ftp_error(SilcSFTP sftp,SilcSFTPStatus status,void * context)371 static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
372 void *context)
373 {
374
375 }
376
377 /************************ Static utility functions **************************/
378
379 /* Free session resources. Connection must be closed before getting
380 here. */
381
silc_client_ftp_session_free(SilcClientFtpSession session)382 static void silc_client_ftp_session_free(SilcClientFtpSession session)
383 {
384 SILC_LOG_DEBUG(("Free session %d", session->session_id));
385
386 silc_schedule_task_del_by_context(session->client->schedule, session);
387
388 silc_dlist_del(session->client->internal->ftp_sessions, session);
389
390 /* Abort connecting */
391 if (session->op)
392 silc_async_abort(session->op, NULL, NULL);
393
394 /* Destroy SFTP */
395 if (session->sftp) {
396 if (session->initiator)
397 silc_sftp_server_shutdown(session->sftp);
398 else
399 silc_sftp_client_shutdown(session->sftp);
400 }
401 if (session->fs)
402 silc_sftp_fs_memory_free(session->fs);
403
404 /* Destroy listener */
405 if (session->listener)
406 silc_client_listener_free(session->listener);
407
408 /* Destroy wrapped stream */
409 if (session->stream)
410 silc_stream_destroy(session->stream);
411
412 silc_client_unref_client(session->client, session->server_conn,
413 session->client_entry);
414 silc_free(session->hostname);
415 silc_free(session->filepath);
416 silc_free(session->path);
417 silc_free(session);
418 }
419
420 /* File transfer session timeout */
421
SILC_TASK_CALLBACK(silc_client_ftp_timeout)422 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
423 {
424 SilcClientFtpSession session = context;
425
426 SILC_LOG_DEBUG(("Timeout"));
427
428 /* Close connection (destroyes the session context later). If it is
429 already closed, destroy the session now. */
430 if (session->conn) {
431 silc_client_close_connection(session->client, session->conn);
432 session->conn = NULL;
433 } else {
434 /* Call monitor callback */
435 if (session->monitor)
436 (*session->monitor)(session->client, session->conn,
437 SILC_CLIENT_FILE_MONITOR_ERROR,
438 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
439 session->client_entry, session->session_id,
440 session->filepath, session->monitor_context);
441
442 silc_client_ftp_session_free(context);
443 }
444 }
445
446 /* File transfer session closing task callback */
447
SILC_TASK_CALLBACK(silc_client_file_close_final)448 SILC_TASK_CALLBACK(silc_client_file_close_final)
449 {
450 SilcClientFtpSession session = context;
451
452 /* Close connection (destroyes the session context later). If it is
453 already closed, destroy the session now. */
454 if (session->conn) {
455 silc_client_close_connection(session->client, session->conn);
456 session->conn = NULL;
457 } else {
458 silc_client_ftp_session_free(context);
459 }
460 }
461
462 /* Client resolving callback. Continues with the FTP packet processing */
463
silc_client_ftp_client_resolved(SilcClient client,SilcClientConnection conn,SilcStatus status,SilcDList clients,void * context)464 static void silc_client_ftp_client_resolved(SilcClient client,
465 SilcClientConnection conn,
466 SilcStatus status,
467 SilcDList clients,
468 void *context)
469 {
470 SilcFSMThread thread = context;
471 SilcPacket packet = silc_fsm_get_state_context(thread);
472
473 /* If no client found, ignore the packet, a silent error */
474 if (!clients) {
475 silc_packet_free(packet);
476 silc_fsm_finish(thread);
477 return;
478 }
479
480 /* Continue processing the packet */
481 SILC_FSM_CALL_CONTINUE(context);
482 }
483
484 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
485 We add/remove FTP payload in this function, because SFTP library does not
486 add/remove it. */
487
488 static SilcBool
silc_client_ftp_coder(SilcStream stream,SilcStreamStatus status,SilcBuffer buffer,void * context)489 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
490 SilcBuffer buffer, void *context)
491 {
492 /* Pull FTP type in the payload, revealing SFTP payload */
493 if (status == SILC_STREAM_CAN_READ) {
494 if (silc_buffer_len(buffer) >= 1)
495 silc_buffer_pull(buffer, 1);
496 return TRUE;
497 }
498
499 /* Add FTP type before SFTP data */
500 if (status == SILC_STREAM_CAN_WRITE) {
501 silc_buffer_push(buffer, 1);
502 if (silc_buffer_format(buffer,
503 SILC_STR_UI_CHAR(1),
504 SILC_STR_END) < 0)
505 return FALSE;
506 return TRUE;
507 }
508
509 return FALSE;
510 }
511
512 /* FTP Connection callback. The SFTP session is started here. */
513
514 static void
silc_client_ftp_connect_completion(SilcClient client,SilcClientConnection conn,SilcClientConnectionStatus status,SilcStatus error,const char * message,void * context)515 silc_client_ftp_connect_completion(SilcClient client,
516 SilcClientConnection conn,
517 SilcClientConnectionStatus status,
518 SilcStatus error,
519 const char *message,
520 void *context)
521 {
522 SilcClientFtpSession session = context;
523
524 session->conn = conn;
525 session->op = NULL;
526
527 silc_schedule_task_del_by_context(client->schedule, session);
528
529 switch (status) {
530 case SILC_CLIENT_CONN_SUCCESS:
531 SILC_LOG_DEBUG(("Connected, conn %p", conn));
532
533 /* Wrap the connection packet stream */
534 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
535 0, FALSE, 0, NULL, 0, NULL,
536 silc_client_ftp_coder, session);
537 if (!session->stream) {
538 /* Call monitor callback */
539 if (session->monitor)
540 (*session->monitor)(session->client, session->conn,
541 SILC_CLIENT_FILE_MONITOR_ERROR,
542 SILC_CLIENT_FILE_ERROR, 0, 0,
543 session->client_entry, session->session_id,
544 session->filepath, session->monitor_context);
545 silc_client_close_connection(client, conn);
546 session->conn = NULL;
547 return;
548 }
549
550 if (!session->initiator) {
551 /* If we are the SFTP client then start the SFTP session and retrieve
552 the info about the file available for download. */
553 session->sftp = silc_sftp_client_start(session->stream,
554 conn->internal->schedule,
555 silc_client_ftp_version,
556 silc_client_ftp_error, session);
557 } else {
558 /* Start SFTP server */
559 session->sftp = silc_sftp_server_start(session->stream,
560 conn->internal->schedule,
561 silc_client_ftp_error, session,
562 session->fs);
563
564 /* Monitor transmission */
565 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
566 silc_client_ftp_monitor, session);
567 }
568
569 break;
570
571 case SILC_CLIENT_CONN_DISCONNECTED:
572 SILC_LOG_DEBUG(("Disconnected %p", conn));
573
574 /* Call monitor callback */
575 if (session->monitor)
576 (*session->monitor)(session->client, session->conn,
577 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
578 SILC_CLIENT_FILE_ERROR, 0, 0,
579 session->client_entry, session->session_id,
580 session->filepath, session->monitor_context);
581
582 /* Connection already closed */
583 session->conn = NULL;
584
585 /* If closed by user, destroy the session now */
586 if (session->closed)
587 silc_client_ftp_session_free(session);
588 break;
589
590 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
591 SILC_LOG_DEBUG(("Connecting timeout"));
592
593 /* Call monitor callback */
594 if (session->monitor)
595 (*session->monitor)(session->client, session->conn,
596 SILC_CLIENT_FILE_MONITOR_ERROR,
597 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
598 session->client_entry, session->session_id,
599 session->filepath, session->monitor_context);
600
601 /* Connection already closed */
602 session->conn = NULL;
603 break;
604
605 default:
606 SILC_LOG_DEBUG(("Connecting error %d", status));
607
608 /* Call monitor callback */
609 if (session->monitor)
610 (*session->monitor)(session->client, session->conn,
611 SILC_CLIENT_FILE_MONITOR_ERROR,
612 status != SILC_CLIENT_CONN_ERROR ?
613 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
614 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
615 session->client_entry, session->session_id,
616 session->filepath, session->monitor_context);
617
618 /* Connection already closed */
619 session->conn = NULL;
620 break;
621 }
622 }
623
624 /*************************** File Transfer API ******************************/
625
626 /* Free all file transfer sessions. */
627
silc_client_ftp_free_sessions(SilcClient client)628 void silc_client_ftp_free_sessions(SilcClient client)
629 {
630 SilcClientFtpSession session;
631
632 if (!client->internal->ftp_sessions)
633 return;
634
635 silc_dlist_start(client->internal->ftp_sessions);
636 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
637 silc_client_ftp_session_free(session);
638 silc_dlist_del(client->internal->ftp_sessions, session);
639 }
640
641 /* Free file transfer session by client entry. */
642
silc_client_ftp_session_free_client(SilcClient client,SilcClientEntry client_entry)643 void silc_client_ftp_session_free_client(SilcClient client,
644 SilcClientEntry client_entry)
645 {
646 SilcClientFtpSession session;
647
648 if (!client->internal->ftp_sessions)
649 return;
650
651 /* Get the session */
652 silc_dlist_start(client->internal->ftp_sessions);
653 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
654 if (session->client_entry == client_entry)
655 silc_client_ftp_session_free(session);
656 }
657
658 /* Sends a file indicated by the `filepath' to the remote client
659 indicated by the `client_entry'. This will negotiate a secret key
660 with the remote client before actually starting the transmission of
661 the file. The `monitor' callback will be called to monitor the
662 transmission of the file. */
663
664 SilcClientFileError
silc_client_file_send(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcClientConnectionParams * params,SilcPublicKey public_key,SilcPrivateKey private_key,SilcClientFileMonitor monitor,void * monitor_context,const char * filepath,SilcUInt32 * session_id)665 silc_client_file_send(SilcClient client,
666 SilcClientConnection conn,
667 SilcClientEntry client_entry,
668 SilcClientConnectionParams *params,
669 SilcPublicKey public_key,
670 SilcPrivateKey private_key,
671 SilcClientFileMonitor monitor,
672 void *monitor_context,
673 const char *filepath,
674 SilcUInt32 *session_id)
675 {
676 SilcClientFtpSession session;
677 SilcBuffer keyagr;
678 char *filename, *path;
679 int fd;
680
681 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
682
683 if (!client || !client_entry || !filepath || !params ||
684 !public_key || !private_key)
685 return SILC_CLIENT_FILE_ERROR;
686
687 /* Check for existing session for `filepath'. */
688 silc_dlist_start(client->internal->ftp_sessions);
689 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
690 if (session->filepath && !strcmp(session->filepath, filepath) &&
691 session->client_entry == client_entry)
692 return SILC_CLIENT_FILE_ALREADY_STARTED;
693 }
694
695 /* See whether the file exists and can be opened */
696 fd = silc_file_open(filepath, O_RDONLY);
697 if (fd < 0)
698 return SILC_CLIENT_FILE_NO_SUCH_FILE;
699 silc_file_close(fd);
700
701 /* Add new session */
702 session = silc_calloc(1, sizeof(*session));
703 if (!session)
704 return SILC_CLIENT_FILE_ERROR;
705 session->session_id = ++client->internal->next_session_id;
706 session->client = client;
707 session->server_conn = conn;
708 session->initiator = TRUE;
709 session->client_entry = silc_client_ref_client(client, conn, client_entry);
710 session->monitor = monitor;
711 session->monitor_context = monitor_context;
712 session->filepath = strdup(filepath);
713 session->params = *params;
714 session->public_key = public_key;
715 session->private_key = private_key;
716
717 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
718 silc_free(session);
719 return SILC_CLIENT_FILE_NO_MEMORY;
720 }
721
722 /* Allocate memory filesystem and put the file to it */
723 if (strrchr(path, '/'))
724 filename = strrchr(path, '/') + 1;
725 else
726 filename = (char *)path;
727 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
728 SILC_SFTP_FS_PERM_EXEC);
729 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
730 filename, path);
731
732 session->filesize = silc_file_size(filepath);
733
734 /* If local IP is provided, create listener for incoming key exchange */
735 if (params->local_ip || params->bind_ip) {
736 session->listener =
737 silc_client_listener_add(client,
738 conn->internal->schedule,
739 params, public_key, private_key,
740 silc_client_ftp_connect_completion,
741 session);
742 if (!session->listener) {
743 conn->context_type = SILC_ID_CLIENT;
744 conn->client_entry = session->client_entry;
745 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
746 "Cannot create listener for file transfer: "
747 "%s", strerror(errno));
748 conn->context_type = SILC_ID_NONE;
749 silc_free(session);
750 return SILC_CLIENT_FILE_NO_MEMORY;
751 }
752
753 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
754 strdup(params->local_ip));
755 session->port = silc_client_listener_get_local_port(session->listener);
756 }
757
758 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
759
760 /* Send the key agreement inside FTP packet */
761 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
762 session->port);
763 if (!keyagr) {
764 if (session->listener)
765 silc_client_listener_free(session->listener);
766 silc_free(session);
767 return SILC_CLIENT_FILE_NO_MEMORY;
768 }
769 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
770 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
771 SILC_STR_UI_CHAR(1),
772 SILC_STR_DATA(silc_buffer_data(keyagr),
773 silc_buffer_len(keyagr)),
774 SILC_STR_END);
775
776 silc_buffer_free(keyagr);
777 silc_free(path);
778
779 silc_dlist_add(client->internal->ftp_sessions, session);
780 if (session_id)
781 *session_id = session->session_id;
782
783 /* Add session request timeout */
784 if (params && params->timeout_secs)
785 silc_schedule_task_add_timeout(client->schedule,
786 silc_client_ftp_timeout, session,
787 params->timeout_secs, 0);
788
789 return SILC_CLIENT_FILE_OK;
790 }
791
792 /* Receives a file from a client indicated by the `client_entry'. The
793 `session_id' indicates the file transmission session and it has been
794 received in the `ftp' client operation function. This will actually
795 perform the key agreement protocol with the remote client before
796 actually starting the file transmission. The `monitor' callback
797 will be called to monitor the transmission. */
798
799 SilcClientFileError
silc_client_file_receive(SilcClient client,SilcClientConnection conn,SilcClientConnectionParams * params,SilcPublicKey public_key,SilcPrivateKey private_key,SilcClientFileMonitor monitor,void * monitor_context,const char * path,SilcUInt32 session_id,SilcClientFileAskName ask_name,void * ask_name_context)800 silc_client_file_receive(SilcClient client,
801 SilcClientConnection conn,
802 SilcClientConnectionParams *params,
803 SilcPublicKey public_key,
804 SilcPrivateKey private_key,
805 SilcClientFileMonitor monitor,
806 void *monitor_context,
807 const char *path,
808 SilcUInt32 session_id,
809 SilcClientFileAskName ask_name,
810 void *ask_name_context)
811 {
812 SilcClientFtpSession session;
813 SilcBuffer keyagr;
814
815 if (!client || !conn)
816 return SILC_CLIENT_FILE_ERROR;
817
818 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
819
820 /* Get the session */
821 silc_dlist_start(client->internal->ftp_sessions);
822 while ((session = silc_dlist_get(client->internal->ftp_sessions))
823 != SILC_LIST_END) {
824 if (session->session_id == session_id) {
825 break;
826 }
827 }
828
829 if (session == SILC_LIST_END) {
830 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
831 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
832 }
833
834 /* See if we have this session running already */
835 if (session->sftp || session->listener) {
836 SILC_LOG_DEBUG(("Session already started"));
837 return SILC_CLIENT_FILE_ALREADY_STARTED;
838 }
839
840 session->monitor = monitor;
841 session->monitor_context = monitor_context;
842 session->ask_name = ask_name;
843 session->ask_name_context = ask_name_context;
844 session->path = path ? strdup(path) : NULL;
845
846 /* If the hostname and port already exists then the remote client did
847 provide the connection point to us and we won't create listener, but
848 create the connection ourselves. */
849 if (session->hostname && session->port) {
850 SILC_LOG_DEBUG(("Connecting to remote client"));
851 /* Connect to the remote client. Performs key exchange automatically. */
852 session->op =
853 silc_client_connect_to_client(client, params, public_key, private_key,
854 session->hostname, session->port,
855 silc_client_ftp_connect_completion,
856 session);
857 if (!session->op) {
858 silc_free(session);
859 return SILC_CLIENT_FILE_ERROR;
860 }
861 } else {
862 /* Add the listener for the key agreement */
863 SILC_LOG_DEBUG(("Creating listener for file transfer"));
864 if (!params || (!params->local_ip && !params->bind_ip)) {
865 session->conn->context_type = SILC_ID_CLIENT;
866 session->conn->client_entry = session->client_entry;
867 session->client->internal->ops->say(session->client, session->conn,
868 SILC_CLIENT_MESSAGE_ERROR,
869 "Cannot create listener for file "
870 "transfer; IP address and/or port "
871 "not provided");
872 session->conn->context_type = SILC_ID_NONE;
873 silc_free(session);
874 return SILC_CLIENT_FILE_ERROR;
875 }
876 session->listener =
877 silc_client_listener_add(client, conn->internal->schedule, params,
878 public_key, private_key,
879 silc_client_ftp_connect_completion,
880 session);
881 if (!session->listener) {
882 session->conn->context_type = SILC_ID_CLIENT;
883 session->conn->client_entry = session->client_entry;
884 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
885 "Cannot create listener for file transfer: "
886 "%s", strerror(errno));
887 session->conn->context_type = SILC_ID_NONE;
888 silc_free(session);
889 return SILC_CLIENT_FILE_NO_MEMORY;
890 }
891 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
892 strdup(params->local_ip));
893 session->port = silc_client_listener_get_local_port(session->listener);
894
895 /* Send the key agreement inside FTP packet */
896 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
897 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
898 session->port);
899 if (!keyagr) {
900 silc_client_listener_free(session->listener);
901 silc_free(session);
902 return SILC_CLIENT_FILE_NO_MEMORY;
903 }
904 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
905 SILC_ID_CLIENT, &session->client_entry->id,
906 NULL, NULL,
907 SILC_STR_UI_CHAR(1),
908 SILC_STR_DATA(silc_buffer_data(keyagr),
909 silc_buffer_len(keyagr)),
910 SILC_STR_END);
911 silc_buffer_free(keyagr);
912
913 /* Add session request timeout */
914 if (params && params->timeout_secs)
915 silc_schedule_task_add_timeout(client->schedule,
916 silc_client_ftp_timeout, session,
917 params->timeout_secs, 0);
918 }
919
920 return SILC_CLIENT_FILE_OK;
921 }
922
923 /* Closes file transmission session indicated by the `session_id'.
924 If file transmission is being conducted it will be aborted
925 automatically. This function is also used to close the session
926 after successful file transmission. This function can be used
927 also to reject incoming file transmission request. */
928
silc_client_file_close(SilcClient client,SilcClientConnection conn,SilcUInt32 session_id)929 SilcClientFileError silc_client_file_close(SilcClient client,
930 SilcClientConnection conn,
931 SilcUInt32 session_id)
932 {
933 SilcClientFtpSession session;
934
935 if (!client || !conn)
936 return SILC_CLIENT_FILE_ERROR;
937
938 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
939
940 /* Get the session */
941 silc_dlist_start(client->internal->ftp_sessions);
942 while ((session = silc_dlist_get(client->internal->ftp_sessions))
943 != SILC_LIST_END) {
944 if (session->session_id == session_id)
945 break;
946 }
947
948 if (session == SILC_LIST_END) {
949 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
950 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
951 }
952
953 if (session->monitor) {
954 (*session->monitor)(session->client, session->conn,
955 SILC_CLIENT_FILE_MONITOR_CLOSED,
956 SILC_CLIENT_FILE_OK, 0, 0,
957 session->client_entry, session->session_id,
958 session->filepath, session->monitor_context);
959
960 /* No more callbacks to application */
961 session->monitor = NULL;
962 }
963
964 silc_schedule_task_del_by_context(client->schedule, session);
965
966 session->closed = TRUE;
967
968 /* Destroy via timeout */
969 silc_schedule_task_add_timeout(conn->internal->schedule,
970 silc_client_file_close_final, session,
971 0, 1);
972
973 return SILC_CLIENT_FILE_OK;
974 }
975
976 /************************** FTP Request Processing **************************/
977
978 /* Received file transfer packet. Only file transfer requests get here.
979 The actual file transfer is handled by the SFTP library when we give it
980 the packet stream wrapped into SilcStream context. */
981
SILC_FSM_STATE(silc_client_ftp)982 SILC_FSM_STATE(silc_client_ftp)
983 {
984 SilcClientConnection conn = fsm_context;
985 SilcClient client = conn->client;
986 SilcPacket packet = state_context;
987 SilcClientFtpSession session;
988 SilcClientID remote_id;
989 SilcClientEntry remote_client;
990 SilcKeyAgreementPayload payload = NULL;
991 char *hostname;
992 SilcUInt16 port;
993
994 SILC_LOG_DEBUG(("Process file transfer packet"));
995
996 if (silc_buffer_len(&packet->buffer) < 1)
997 goto out;
998
999 /* We support file transfer type number 1 (== SFTP) */
1000 if (packet->buffer.data[0] != 0x01) {
1001 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1002 packet->buffer.data[0]));
1003 goto out;
1004 }
1005
1006 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1007 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1008 SILC_LOG_DEBUG(("Invalid client ID"));
1009 goto out;
1010 }
1011
1012 /* Check whether we know this client already */
1013 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1014 if (!remote_client || !remote_client->internal.valid) {
1015 /** Resolve client info */
1016 silc_client_unref_client(client, conn, remote_client);
1017 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1018 client, conn, &remote_id, NULL,
1019 silc_client_ftp_client_resolved,
1020 fsm));
1021 /* NOT REACHED */
1022 }
1023
1024 /* Get session */
1025 silc_dlist_start(client->internal->ftp_sessions);
1026 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1027 if (session->client_entry == remote_client &&
1028 (!session->initiator || !session->listener))
1029 break;
1030 }
1031
1032 /* Parse the key agreement payload */
1033 payload =
1034 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1035 silc_buffer_len(&packet->buffer) - 1);
1036 if (!payload) {
1037 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1038 goto out;
1039 }
1040
1041 hostname = silc_key_agreement_get_hostname(payload);
1042 port = silc_key_agreement_get_port(payload);
1043 if (!hostname || !port) {
1044 hostname = NULL;
1045 port = 0;
1046 }
1047
1048 /* If session doesn't exist, we create new one. If session exists, but
1049 we are responder it means that the remote sent another request and user
1050 hasn't even accepted the first one yet. We assume this session is new
1051 session as well. */
1052 if (!session || !hostname || !session->initiator) {
1053 /* New file transfer session */
1054 SILC_LOG_DEBUG(("New file transfer session %d",
1055 client->internal->next_session_id + 1));
1056
1057 session = silc_calloc(1, sizeof(*session));
1058 if (!session)
1059 goto out;
1060 session->session_id = ++client->internal->next_session_id;
1061 session->client = client;
1062 session->server_conn = conn;
1063 session->client_entry = silc_client_ref_client(client, conn,
1064 remote_client);
1065 if (hostname && port) {
1066 session->hostname = strdup(hostname);
1067 session->port = port;
1068 }
1069 silc_dlist_add(client->internal->ftp_sessions, session);
1070
1071 /* Notify application of incoming FTP request */
1072 client->internal->ops->ftp(client, conn, remote_client,
1073 session->session_id, hostname, port);
1074 goto out;
1075 }
1076
1077 /* Session exists, continue with key agreement protocol. */
1078 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1079 session->session_id));
1080
1081 session->hostname = strdup(hostname);
1082 session->port = port;
1083
1084 /* Connect to the remote client. Performs key exchange automatically. */
1085 session->op =
1086 silc_client_connect_to_client(client, &session->params,
1087 session->public_key, session->private_key,
1088 session->hostname, session->port,
1089 silc_client_ftp_connect_completion,
1090 session);
1091 if (!session->op) {
1092 /* Call monitor callback */
1093 if (session->monitor)
1094 (*session->monitor)(session->client, session->conn,
1095 SILC_CLIENT_FILE_MONITOR_ERROR,
1096 SILC_CLIENT_FILE_ERROR, 0, 0,
1097 session->client_entry, session->session_id,
1098 session->filepath, session->monitor_context);
1099 }
1100
1101 out:
1102 if (payload)
1103 silc_key_agreement_payload_free(payload);
1104 silc_packet_free(packet);
1105 return SILC_FSM_FINISH;
1106 }
1107