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