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