1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #if defined(WIN32) || (defined(__CYGWIN__) && defined(WINSOCK_SUPPORT))
32 #include <winsock2.h>
33 #ifdef _MSC_VER
34 #include <io.h>
35 #endif
36 #else
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #endif
44 
45 #include <stdio.h>
46 #include <string.h>
47 #include <errno.h>
48 
49 #endif /* WZD_USE_PCH */
50 
51 #define Sleep(x)        usleep((x)*1000)
52 
53 #include <time.h>
54 
55 
56 #include "wzd_hardlimits.h"
57 #include "wzd_structs.h"
58 #include "wzd_log.h"
59 #include "wzd_tls.h"
60 #include "wzd_misc.h"
61 #include "wzd_ClientThread.h"
62 #include "wzd_messages.h"
63 #include "wzd_configfile.h"
64 #include "wzd_crc32.h"
65 #include "wzd_events.h"
66 #include "wzd_file.h"
67 #include "wzd_libmain.h"
68 #include "wzd_mod.h"
69 #include "wzd_data.h"
70 #include "wzd_socket.h"
71 #include "wzd_threads.h"
72 #include "wzd_user.h"
73 
74 #include "wzd_debug.h"
75 
update_last_file(wzd_context_t * context)76 void update_last_file(wzd_context_t * context)
77 {
78   struct timeval tv;
79 
80   gettimeofday(&tv, NULL);
81   strncpy(context->last_file.name,context->current_action.arg,WZD_MAX_PATH);
82   context->last_file.size = context->current_action.bytesnow; /* size */
83   if (server_time > context->current_action.tm_start)
84     context->last_file.time = (server_time - context->current_action.tm_start); /* size */
85   else
86     context->last_file.time = 0;
87   context->last_file.tv.tv_sec = tv.tv_sec - context->current_action.tv_start.tv_sec;
88   context->last_file.tv.tv_usec = tv.tv_usec - context->current_action.tv_start.tv_usec;
89   context->last_file.token = context->current_action.token;
90 }
91 
92 /** \brief Close data connection (if opened) */
data_close(wzd_context_t * context)93 void data_close(wzd_context_t * context)
94 {
95   int ret;
96 
97 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
98   if (context->tls_data_mode == TLS_PRIV)
99     ret = tls_close_data(context);
100 #endif
101 #ifdef DEBUG
102 out_err(LEVEL_FLOOD,"closing data connection fd: %d (control fd: %d)\n",context->datafd, context->controlfd);
103 #endif
104   ret = socket_close(context->datafd);
105   FD_UNREGISTER(context->datafd,"Client data socket");
106   context->datafd = -1;
107   context->pasvsock = -1;
108   context->state = STATE_UNKNOWN;
109 }
110 
111 /** \brief End current transfer if any, close data connection and send event
112  */
data_end_transfer(int is_upload,int end_ok,wzd_context_t * context)113 void data_end_transfer(int is_upload, int end_ok, wzd_context_t * context)
114 {
115   file_unlock(context->current_action.current_file);
116   file_close(context->current_action.current_file, context);
117   FD_UNREGISTER(context->current_action.current_file,"Client file (RETR or STOR)");
118 
119   out_xferlog(context,end_ok /* complete */);
120   update_last_file(context);
121 
122   context->current_action.current_file = -1;
123   context->current_action.bytesnow = 0;
124   context->state = STATE_COMMAND;
125   data_close(context);
126 
127   context->current_action.token = TOK_UNKNOWN;
128 
129   {
130     u32_t event_id = (is_upload) ? EVENT_POSTUPLOAD : EVENT_POSTDOWNLOAD;
131     unsigned int reply_code = (end_ok) ? 226 : 426;
132     wzd_user_t * user = GetUserByID(context->userid);
133     wzd_string_t * event_args = str_allocate();
134 
135     /** \todo Find a way to indicate if transfer was ok in event */
136     str_sprintf(event_args,"%s %s",user->username,context->current_action.arg);
137     event_send(mainConfig->event_mgr, event_id, reply_code, event_args, context);
138     str_deallocate(event_args);
139   }
140 }
141 
data_set_fd(wzd_context_t * context,fd_set * fdr,fd_set * fdw,fd_set * fde)142 int data_set_fd(wzd_context_t * context, fd_set *fdr, fd_set *fdw, fd_set *fde)
143 {
144   unsigned int action;
145 
146   if (!context) return -1;
147 
148   action = context->current_action.token;
149 
150   switch (action) {
151   case TOK_RETR:
152     if (context->state != STATE_XFER) {
153       out_log(LEVEL_HIGH,"Assertion failed: state != XFER but current action is RETR. Please report me to authors\n");
154       return -1;
155     }
156     if (context->datafd==(fd_t)-1 || !fd_is_valid(context->datafd)) {
157       out_err(LEVEL_HIGH,"Trying to set invalid datafd (%d) %s:%d\n",
158           context->datafd,__FILE__,__LINE__);
159       return -1;
160     }
161     FD_SET(context->datafd,fdw);
162     FD_SET(context->datafd,fde);
163     return context->datafd;
164     break;
165   case TOK_STOR:
166     if (context->state != STATE_XFER) {
167       out_log(LEVEL_HIGH,"Assertion failed: state != XFER but current action is STOR. Please report me to authors\n");
168       return -1;
169     }
170     if (context->datafd==(fd_t)-1 || !fd_is_valid(context->datafd)) {
171       out_err(LEVEL_HIGH,"Trying to set invalid datafd (%d) %s:%d\n",
172           context->datafd,__FILE__,__LINE__);
173       return -1;
174     }
175     FD_SET(context->datafd,fdr);
176     FD_SET(context->datafd,fde);
177     return context->datafd;
178     break;
179   }
180   return -1;
181 }
182 
data_check_fd(wzd_context_t * context,fd_set * fdr,fd_set * fdw,fd_set * fde)183 int data_check_fd(wzd_context_t * context, fd_set *fdr, fd_set *fdw, fd_set *fde)
184 {
185   unsigned int action;
186 
187   if (!context) return -1;
188 
189   action = context->current_action.token;
190 
191   switch (action) {
192   case TOK_RETR:
193     if (FD_ISSET(context->datafd,fdw)) return 1;
194     if (FD_ISSET(context->datafd,fde)) return -1;
195     break;
196   case TOK_STOR:
197     if (FD_ISSET(context->datafd,fdr)) return 1;
198     if (FD_ISSET(context->datafd,fde)) return -1;
199     return context->datafd;
200     break;
201   }
202   return 0;
203 }
204 
data_execute(wzd_context_t * context,wzd_user_t * user,fd_set * fdr,fd_set * fdw)205 int data_execute(wzd_context_t * context, wzd_user_t * user, fd_set *fdr, fd_set *fdw)
206 {
207   int n;
208   unsigned int action;
209   int ret;
210 
211   if (!context || !user) return -1;
212 
213   WZD_ASSERT( context->data_buffer != NULL );
214 
215   action = context->current_action.token;
216 
217   switch (action) {
218   case TOK_RETR:
219     n = file_read(context->current_action.current_file,context->data_buffer,mainConfig->data_buffer_length);
220     if (n>0) {
221 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
222       if (context->tls_data_mode == TLS_CLEAR)
223         ret = clear_write(context->datafd,context->data_buffer,(size_t)n,0,HARD_XFER_TIMEOUT,context);
224       else
225 #endif
226         ret = (context->write_fct)(context->datafd,context->data_buffer,(unsigned int)n,0,HARD_XFER_TIMEOUT,context);
227       if (ret <= 0) {
228 /*        out_log(LEVEL_INFO,"INFO error or timeout sending data\n");*/
229         /* error/timeout sending data */
230         data_end_transfer(0 /* is_upload */, 0 /* end_ok */, context);
231 
232         ret = send_message(426,context);
233 
234         context->idle_time_start = time(NULL);
235         return 1;
236       }
237       context->current_action.bytesnow += n;
238 
239       limiter_add_bytes(&mainConfig->global_dl_limiter,limiter_mutex,n,0);
240       limiter_add_bytes(&context->current_dl_limiter,limiter_mutex,n,0);
241 
242       user->stats.bytes_dl_total += n;
243       if (user->ratio)
244         user->credits -= n;
245       context->idle_time_data_start = server_time;
246     } else { /* end */
247       send_message_raw("226- command ok\r\n",context);
248 
249       data_end_transfer(0 /* is_upload */, 1 /* end_ok */, context);
250 
251       ret = send_message(226,context);
252 #ifdef DEBUG
253 out_err(LEVEL_INFO,"Send 226 message returned %d\n",ret);
254 #endif
255 
256       /* user will be invalidated */
257       backend_mod_user(mainConfig->backends->filename, user->uid, user, _USER_BYTESDL | _USER_CREDITS);
258 
259       context->current_action.token = TOK_UNKNOWN;
260       context->idle_time_start = server_time;
261     }
262     break;
263   case TOK_STOR:
264 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
265       if (context->tls_data_mode == TLS_CLEAR)
266         n = clear_read(context->datafd,context->data_buffer,mainConfig->data_buffer_length,0,HARD_XFER_TIMEOUT,context);
267       else
268 #endif
269       n = (context->read_fct)(context->datafd,context->data_buffer,mainConfig->data_buffer_length,0,HARD_XFER_TIMEOUT,context);
270     if (n>0) {
271       if (file_write(context->current_action.current_file,context->data_buffer,(size_t)n) != (ssize_t)n) {
272         out_log(LEVEL_NORMAL,"Write failed %d bytes (returned %d %s)\n",n,errno,strerror(errno));
273       }
274       context->current_action.bytesnow += n;
275 
276       limiter_add_bytes(&mainConfig->global_ul_limiter,limiter_mutex,n,0);
277       limiter_add_bytes(&context->current_ul_limiter,limiter_mutex,n,0);
278 
279       user->stats.bytes_ul_total += n;
280       if (user->ratio)
281         user->credits += (user->ratio * n);
282       context->idle_time_data_start = server_time;
283     } else { /* consider it is finished */
284       off_t current_position;
285 
286       send_message_raw("226- command ok\r\n",context);
287 
288       /** If we don't resume a previous upload, we have to truncate the current file
289        * or we won't be able to overwrite a file by a smaller one
290        */
291       current_position = lseek(context->current_action.current_file,0,SEEK_CUR);
292       ftruncate(context->current_action.current_file,current_position);
293 
294       file_unlock(context->current_action.current_file);
295       data_end_transfer(1 /* is_upload */, 1 /* end_ok */, context);
296 
297       ret = send_message(226,context);
298 #ifdef DEBUG
299       out_err(LEVEL_INFO,"Send 226 message returned %d\n",ret);
300 #endif
301       /* we increment the counter of uploaded files at the end
302        * of the upload
303        */
304       user->stats.files_ul_total++;
305 
306       /* user will be invalidated */
307       backend_mod_user(mainConfig->backends->filename, user->uid, user, _USER_BYTESUL | _USER_CREDITS);
308 
309       context->current_action.token = TOK_UNKNOWN;
310       context->idle_time_start = server_time;
311     }
312     break;
313   }
314 
315   return 0;
316 }
317 
318 /** \brief run local transfer loop for RETR
319  */
do_local_retr(wzd_context_t * context)320 int do_local_retr(wzd_context_t * context)
321 {
322   struct timeval tv;
323   fd_set fds_w;
324   int ret, err;
325   ssize_t count;
326   int file = context->current_action.current_file;
327   int maxfd = context->datafd;
328   wzd_user_t * user = GetUserByID(context->userid);
329   int exit_ok = 0;
330   write_fct_t write_fct;
331   unsigned long crc = 0;
332   int auto_crc = 0;
333 
334   _tls_store_context(context);
335 
336 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
337   if (context->tls_data_mode == TLS_CLEAR)
338     write_fct = clear_write;
339   else
340 #endif
341     write_fct = context->write_fct;
342 
343   context->last_file.crc = 0;
344   ret = config_get_boolean(mainConfig->cfg_file, "GLOBAL", "auto crc", &err);
345   if (err == CF_OK && (ret)) {
346     auto_crc = 1;
347   }
348 
349   do {
350     FD_ZERO(&fds_w);
351 
352     FD_SET(context->datafd,&fds_w);
353 
354     tv.tv_sec=30; tv.tv_usec=0L;
355 
356     ret = select(maxfd+1,NULL,&fds_w,NULL,&tv);
357 
358     if (ret > 0) {
359       count = read(file, context->data_buffer, mainConfig->data_buffer_length);
360       if (count > 0) {
361         ret = (write_fct)(context->datafd,context->data_buffer,(size_t)count,0,0,context);
362 
363         if (ret <= 0) goto _local_retr_exit;
364 
365         context->current_action.bytesnow += count;
366 
367         limiter_add_bytes(&mainConfig->global_dl_limiter,limiter_mutex,count,0);
368         limiter_add_bytes(&context->current_dl_limiter,limiter_mutex,count,0);
369 
370         /* compute incremental crc32 for later use */
371         if (auto_crc) calc_crc32_buffer( context->data_buffer, &crc, count);
372 
373         user->stats.bytes_dl_total += count;
374         if (user->ratio)
375           user->credits -= count;
376         context->idle_time_data_start = server_time;
377       } else {
378         exit_ok = 1;
379         goto _local_retr_exit;
380       }
381     } else {
382       out_log(LEVEL_HIGH,"do_local_retr select returned %d\n",ret);
383       goto _local_retr_exit;
384     }
385   } while (1);
386 
387 _local_retr_exit:
388   if (exit_ok) { /* send header */
389     send_message_raw("226- command ok\r\n",context);
390     context->last_file.crc = crc;
391   }
392 
393   data_end_transfer(0 /* is_upload */, exit_ok /* end_ok */, context);
394 
395   if (exit_ok) {
396     ret = send_message(226,context);
397   } else {
398     ret = send_message(426,context);
399   }
400 
401   /* user will be invalidated */
402   backend_mod_user(mainConfig->backends->filename, user->uid, user, _USER_BYTESDL | _USER_CREDITS);
403 
404   context->current_action.token = TOK_UNKNOWN;
405   context->idle_time_start = server_time;
406   context->is_transferring = 0;
407 
408   out_log(LEVEL_HIGH,"DEBUG transfer thread exiting\n");
409 
410   return 0;
411 }
412 
413 /** \brief run local transfer loop for STOR
414  */
do_local_stor(wzd_context_t * context)415 int do_local_stor(wzd_context_t * context)
416 {
417   struct timeval tv;
418   fd_set fds_r;
419   int ret, err;
420   ssize_t count;
421   int file = context->current_action.current_file;
422   int maxfd = context->datafd;
423   wzd_user_t * user = GetUserByID(context->userid);
424   int exit_ok = 0;
425   read_fct_t read_fct;
426   unsigned long crc = 0;
427   int auto_crc = 0;
428 
429   _tls_store_context(context);
430 
431 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
432   if (context->tls_data_mode == TLS_CLEAR)
433     read_fct = clear_read;
434   else
435 #endif
436     read_fct = context->read_fct;
437 
438   context->last_file.crc = 0;
439   ret = config_get_boolean(mainConfig->cfg_file, "GLOBAL", "auto crc", &err);
440   if (err == CF_OK && (ret)) {
441     auto_crc = 1;
442   }
443 
444   do {
445     FD_ZERO(&fds_r);
446 
447     FD_SET(context->datafd,&fds_r);
448 
449     tv.tv_sec=30; tv.tv_usec=0L;
450 
451     ret = select(maxfd+1,&fds_r,NULL,NULL,&tv);
452 
453     if (ret > 0) {
454       count = (read_fct)(context->datafd,context->data_buffer,mainConfig->data_buffer_length,0,0,context);
455       if (count > 0) {
456         ret = write(file, context->data_buffer, count);
457 
458         if (ret <= 0) goto _local_stor_exit;
459         if (ret != count) {
460           out_log(LEVEL_HIGH,"ERROR short write (%d bytes instead of %d)\n",(int)ret,(int)count);
461           goto _local_stor_exit;
462         }
463 
464         context->current_action.bytesnow += count;
465 
466         limiter_add_bytes(&mainConfig->global_ul_limiter,limiter_mutex,count,0);
467         limiter_add_bytes(&context->current_ul_limiter,limiter_mutex,count,0);
468 
469         /* compute incremental crc32 for later use */
470         if (auto_crc) calc_crc32_buffer( context->data_buffer, &crc, count);
471 
472         user->stats.bytes_ul_total += count;
473         if (user->ratio)
474           user->credits += (user->ratio * ret);
475         context->idle_time_data_start = server_time;
476       } else {
477         exit_ok = 1;
478         goto _local_stor_exit;
479       }
480     } else {
481       out_log(LEVEL_HIGH,"do_local_stor select returned %d\n",ret);
482       goto _local_stor_exit;
483     }
484   } while (1);
485 
486 _local_stor_exit:
487   if (exit_ok) { /* send header */
488     off_t current_position;
489 
490     send_message_raw("226- command ok\r\n",context);
491     context->last_file.crc = crc;
492 
493     /** If we don't resume a previous upload, we have to truncate the current file
494      * or we won't be able to overwrite a file by a smaller one
495      */
496     current_position = lseek(context->current_action.current_file,0,SEEK_CUR);
497     ftruncate(context->current_action.current_file,current_position);
498 
499     /* we increment the counter of uploaded files at the end
500      * of the upload
501      */
502     user->stats.files_ul_total++;
503   }
504 
505   file_unlock(context->current_action.current_file);
506   data_end_transfer(1 /* is_upload */, exit_ok /* end_ok */, context);
507 
508   if (exit_ok) {
509     ret = send_message(226,context);
510   } else {
511     ret = send_message(426,context);
512   }
513 
514   /* user will be invalidated */
515   backend_mod_user(mainConfig->backends->filename, user->uid, user, _USER_BYTESUL | _USER_CREDITS);
516 
517   context->current_action.token = TOK_UNKNOWN;
518   context->idle_time_start = server_time;
519   context->is_transferring = 0;
520 
521   return 0;
522 }
523 
524 /** \brief Create thread for data transfer (RETR)
525  */
data_start_thread_retr(wzd_context_t * context)526 int data_start_thread_retr(wzd_context_t * context)
527 {
528   wzd_thread_t * thread;
529   int ret;
530 
531   thread = malloc(sizeof(wzd_thread_t));
532   ret = wzd_thread_create(thread, NULL, do_local_retr, context);
533 
534   context->transfer_thread = thread;
535 
536   return 0;
537 }
538 
539 /** \brief Create thread for data transfer (STOR)
540  */
data_start_thread_stor(wzd_context_t * context)541 int data_start_thread_stor(wzd_context_t * context)
542 {
543   wzd_thread_t * thread;
544   int ret;
545 
546   thread = malloc(sizeof(wzd_thread_t));
547   ret = wzd_thread_create(thread, NULL, do_local_stor, context);
548 
549   context->transfer_thread = thread;
550 
551   return 0;
552 }
553 
554