GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: ftpserv.c Lines: 635 958 66.3 %
Date: 2026-02-24 22:33:21 Branches: 347 668 51.9 %

Line Branch Exec Source
1
/*
2
* ftpserv.c
3
*
4
*  Created on: Aug 20, 2016
5
*
6
*  Modified on: Apr 04, 2019
7
*
8
*      Author: lightftp
9
*/
10
11
#include "ftpserv.h"
12
#include "cfgparse.h"
13
#include "x_malloc.h"
14
15
static const FTPROUTINE ftpprocs[MAX_CMDS] = {
16
	ftpUSER, ftpQUIT, ftpNOOP, ftpPWD, ftpTYPE, ftpPORT, ftpLIST, ftpCDUP,
17
	ftpCWD, ftpRETR, ftpABOR, ftpDELE, ftpPASV, ftpPASS, ftpREST, ftpSIZE,
18
	ftpMKD, ftpRMD, ftpSTOR, ftpSYST, ftpFEAT, ftpAPPE, ftpRNFR, ftpRNTO,
19
	ftpOPTS, ftpMLSD, ftpAUTH, ftpPBSZ, ftpPROT, ftpEPSV, ftpHELP, ftpSITE
20
};
21
22
static const char *ftpcmds[MAX_CMDS] = {
23
	"USER", "QUIT", "NOOP", "PWD",  "TYPE", "PORT", "LIST", "CDUP",
24
	"CWD",  "RETR", "ABOR", "DELE", "PASV", "PASS", "REST", "SIZE",
25
	"MKD",  "RMD",  "STOR", "SYST", "FEAT", "APPE", "RNFR", "RNTO",
26
	"OPTS", "MLSD", "AUTH", "PBSZ", "PROT", "EPSV", "HELP", "SITE"
27
};
28
29
/*
30
 * FTP_PASSCMD_INDEX
31
 * must be in sync with ftpprocs & ftpcmds "PASS" index
32
 */
33
#define FTP_PASSCMD_INDEX	13
34
35
unsigned int g_newid = 0;
36
37
void delete_last_slash(char *s)
38
{
39

14
	if (*s != 0)
40
	{
41
		/*
42
		 * don't remove root directory sign as special case
43
		 */
44



11
		if ((s[0] == '/') && (s[1] == 0))
45
			return;
46
47

621
		while (s[1] != 0)
48
610
			++s;
49
50

11
		if (*s == '/')
51
10
			*s = 0;
52
	}
53
}
54
55
void add_last_slash(char *s)
56
{
57



4346
	if (*s != 0)
58
	{
59



49354
		while (s[1] != 0)
60
45008
			++s;
61
62



4346
		if (*s != '/')
63
		{
64
2310
			s[1] = '/';
65
2310
			s[2] = 0;
66
		}
67
	}
68
}
69
70
/*
71
 * Cuts off filename from string leaving only path.
72
 * Return value: pointer to a terminating null character at the end of path
73
 */
74
char *filepath(char *s)
75
{
76
	char	*p = s;
77
78

14
	if (*s == 0)
79
		return s;
80
/*
81
 * leave root directory sign untouched
82
 */
83

11
	if (*s == '/')
84
	{
85
1
		++s;
86
		++p;
87
	}
88
89

621
	while (*s != 0) {
90

610
		if (*s == '/')
91
			p = s;
92
610
		++s;
93
	}
94
95
11
	*p = 0;
96
97
11
	return p;
98
}
99
100
/*
101
 * This function filters the path out of ".." members
102
 * not allowing user to escape the home directory
103
 */
104
2227
void format_path(char *input_path, char *filtered_path)
105
{
106
	char	*p0, *pnext, *fp0;
107
	size_t	sl;
108
109
2227
	if (*input_path == '/')
110
	{
111
2227
		++input_path;
112
2227
		*filtered_path = '/';
113
2227
		++filtered_path;
114
	}
115
116
	p0 = input_path;
117
	pnext = input_path;
118
	fp0 = filtered_path;
119
2227
	*fp0 = 0;
120
121
	while (1)
122
	{
123
42652
		while ((*pnext != '/') && (*pnext != 0))
124
26447
			++pnext;
125
126
16205
		sl = pnext - p0;
127
128
16205
		while (sl > 0)
129
		{
130
4141
			if (sl == 1)
131
1789
				if (*p0 == '.')
132
					break;
133
134
4067
			if (sl == 2)
135

407
				if ((p0[0] == '.') && (p0[1] == '.'))
136
				{
137
					delete_last_slash(filtered_path);
138
					fp0 = filepath(filtered_path);
139
13
					if (fp0 != filtered_path)
140
					{
141
10
						*fp0 = '/';
142
10
						++fp0;
143
10
						*fp0 = 0;
144
					}
145
					break;
146
				}
147
148
			strncpy(fp0, p0, sl);
149
4054
			fp0 += sl;
150
4054
			if (*pnext != 0)
151
			{
152
2678
				*fp0 = '/';
153
2678
				++fp0;
154
			}
155
4054
			*fp0 = 0;
156
157
4054
			break;
158
		}
159
160
16205
		if (*pnext == 0)
161
			break;
162
163
13978
		++pnext;
164
		p0 = pnext;
165
	}
166
2227
}
167
168
2227
char *finalpath(char *root_dir, char *current_dir, char *params, char *result_path)
169
{
170
	char	*tmp, *user_root;
171
	size_t	total_len;
172
173
2227
	total_len = strlen(root_dir)+strlen(current_dir);
174
2227
	if (params != NULL)
175
1393
		total_len += strlen(params);
176
177
2227
	if (total_len >= SIZE_OF_GPBUFFER)
178
		return NULL;
179
180
2227
	tmp = x_malloc(SIZE_OF_GPBUFFER);
181
182
	strcpy(result_path, root_dir);
183
	add_last_slash(result_path);
184
2227
	user_root = result_path+strlen(result_path);
185
186
	do {
187
2227
		if ( params == NULL )
188
		{
189
			strcpy(tmp, current_dir);
190
			add_last_slash(tmp);
191
			break;
192
		}
193
194
1393
		if ( params[0] != '/' )
195
		{
196
			strcpy(tmp, current_dir);
197
			add_last_slash(tmp);
198
		}
199
200
		strcat(tmp, params);
201
	} while (0);
202
203
2227
	format_path(tmp, user_root);
204
2227
	free(tmp);
205
2227
	return result_path;
206
}
207
208
static void cleanup_handler(void *arg)
209
{
210
	PFTPCONTEXT context = (PFTPCONTEXT)arg;
211
212
	pthread_mutex_unlock(&context->MTLock);
213
}
214
215
ssize_t sendstring_plaintext(SOCKET s, const char *Buffer)
216
{
217
	return (send(s, Buffer, strlen(Buffer), MSG_NOSIGNAL) >= 0);
218
}
219
220
int InitTLSSession(gnutls_session_t *session, SOCKET s, int send_success_string)
221
{
222
	int ret;
223
224
	while (session != NULL)
225
	{
226
		if (gnutls_init(session, GNUTLS_SERVER | GNUTLS_NO_SIGNAL) < 0)
227
			break;
228
229
		if (gnutls_priority_set(*session, priority_cache) < 0)
230
			break;
231
232
		if (gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0)
233
			break;
234
235
		gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
236
		gnutls_handshake_set_timeout(*session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
237
		gnutls_transport_set_int2(*session, s, s);
238
239
		if (send_success_string != 0)
240
			sendstring_plaintext(s, success234);
241
242
		do {
243
			ret = gnutls_handshake(*session);
244
		} while ((ret < 0) && (gnutls_error_is_fatal(ret) == 0));
245
246
		if (ret < 0)
247
		{
248
			gnutls_deinit(*session);
249
			*session = NULL;
250
		}
251
252
		return 1;
253
	}
254
255
	return sendstring_plaintext(s, error500_auth);
256
}
257
258
856
SOCKET create_datasocket(PFTPCONTEXT context)
259
{
260
	SOCKET				clientsocket = INVALID_SOCKET;
261
	struct sockaddr_in	laddr;
262
	socklen_t			asz;
263
264
	memset(&laddr, 0, sizeof(laddr));
265
266
856
	switch ( context->Mode ) {
267
737
	case MODE_NORMAL:
268
737
		clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
269
737
		context->DataSocket = clientsocket;
270
737
		if ( clientsocket == INVALID_SOCKET )
271
			return INVALID_SOCKET;
272
273
737
		laddr.sin_family = AF_INET;
274
737
		laddr.sin_port = context->DataPort;
275
737
		laddr.sin_addr.s_addr = context->DataIPv4;
276
737
		if ( connect(clientsocket, (const struct sockaddr *)&laddr, sizeof(laddr)) == -1 ) {
277
737
			close(clientsocket);
278
737
			return INVALID_SOCKET;
279
		}
280
		break;
281
282
119
	case MODE_PASSIVE:
283
119
		asz = sizeof(laddr);
284
119
		clientsocket = accept(context->DataSocket, (struct sockaddr *)&laddr, &asz);
285
71
		close(context->DataSocket);
286
71
		context->DataSocket = clientsocket;
287
288
71
		if ( clientsocket == INVALID_SOCKET )
289
			return INVALID_SOCKET;
290
291
		context->DataIPv4 = 0;
292
		context->DataPort = 0;
293
		context->Mode = MODE_NORMAL;
294
		break;
295
296
	default:
297
		return INVALID_SOCKET;
298
	}
299
	return clientsocket;
300
}
301
302
15833
ssize_t sendstring(PFTPCONTEXT context, const char *Buffer)
303
{
304
15833
	size_t	l = strlen(Buffer);
305
306
15833
	if (context->TLS_session == NULL)
307
15833
		return (send(context->ControlSocket, Buffer, l, MSG_NOSIGNAL) >= 0);
308
	else
309
		return (gnutls_record_send(context->TLS_session, Buffer, l) >= 0);
310
}
311
312
ssize_t sendstring_auto(SOCKET s, gnutls_session_t session, const char *Buffer)
313
{
314
	size_t	l = strlen(Buffer);
315
316
	if (session == NULL)
317
		return (send(s, Buffer, l, MSG_NOSIGNAL) >= 0);
318
	else
319
		return (gnutls_record_send(session, Buffer, l) >= 0);
320
}
321
322
ssize_t send_auto(int __fd, gnutls_session_t session, const void *__buf, size_t __n)
323
{
324
	if (session == NULL)
325
		return (send(__fd, __buf, __n, MSG_NOSIGNAL));
326
	else
327
		return (gnutls_record_send(session, __buf, __n));
328
}
329
330
ssize_t recv_auto(int __fd, gnutls_session_t session, void *__buf, size_t __n)
331
{
332
	if (session == NULL)
333
		return (recv(__fd, __buf, __n, 0));
334
	else
335
		return (gnutls_record_recv(session, __buf, __n));
336
}
337
338
21510
ssize_t writeconsolestr(const char *Buffer)
339
{
340
21510
	size_t	l = strlen(Buffer);
341
342
21510
	if ( g_log != -1 )
343
21510
		write(g_log, Buffer, l);
344
345
21510
	return write(STDOUT_FILENO, Buffer, l);
346
}
347
348
21510
int writelogentry(PFTPCONTEXT context, const char *logtext1, const char *logtext2)
349
21510
{
350
21510
	char		text[SIZE_OF_GPBUFFER];
351
21510
	time_t		itm = time(NULL);
352
	struct tm	ltm;
353
354
21510
	localtime_r(&itm, &ltm);
355
356
21510
	if (context == NULL)
357
	{
358
668
		snprintf(text, sizeof(text), "%02u-%02u-%u %02u:%02u:%02u : %s%s\r\n",
359
668
				ltm.tm_mday, ltm.tm_mon+1, ltm.tm_year+1900,
360
				ltm.tm_hour, ltm.tm_min, ltm.tm_sec, logtext1, logtext2);
361
	}
362
	else
363
	{
364
20842
		snprintf(text, sizeof(text), "%02u-%02u-%u %02u:%02u:%02u S-id=%u : %s%s\r\n",
365
20842
				ltm.tm_mday, ltm.tm_mon+1, ltm.tm_year+1900,
366
				ltm.tm_hour, ltm.tm_min, ltm.tm_sec,
367
				context->SessionID, logtext1, logtext2);
368
	}
369
370
21510
	return writeconsolestr(text);
371
}
372
373
803
void WorkerThreadCleanup(PFTPCONTEXT context)
374
{
375
	int					err;
376
803
	void				*retv = NULL;
377
378
803
	if ( context->WorkerThreadValid == 0 ) {
379
380
		/*
381
		 * trying to stop gracefully
382
		 */
383
48
		context->WorkerThreadAbort = 1;
384
48
		sleep(2);
385
386
48
		err = pthread_join(context->WorkerThreadId, &retv);
387
		if ( err != 0)
388
		{
389
			writelogentry(context, "Enter cancel", "");
390
			pthread_cancel(context->WorkerThreadId);
391
		}
392
393
		context->WorkerThreadValid = -1;
394
	}
395
396
755
	if ( context->DataSocket != INVALID_SOCKET ) {
397
476
		close(context->DataSocket);
398
476
		context->DataSocket = INVALID_SOCKET;
399
	}
400
401
755
	if ( context->File != -1 ) {
402
		close(context->File);
403
		context->File = -1;
404
	}
405
406
755
	context->DataIPv4 = 0;
407
755
	context->DataPort = 0;
408
	//writelogentry(context, "WorkerThreadCleanup complete", "");
409
755
}
410
411
856
int ftpUSER(PFTPCONTEXT context, const char *params)
412
{
413
856
	if ( params == NULL )
414
151
		return sendstring(context, error501);
415
416
705
	context->Access = FTP_ACCESS_NOT_LOGGED_IN;
417
418
	/*
419
	 * Save username in GPBuffer for next PASS command
420
	 */
421
422
705
	writelogentry(context, " USER: ", (char *)params);
423
424
705
	strcpy(context->GPBuffer, interm331);
425
705
	strcat(context->GPBuffer, params);
426
705
	strcat(context->GPBuffer, interm331_tail);
427
705
	sendstring(context, context->GPBuffer);
428
429
705
	strcpy(context->GPBuffer, params);
430
705
	return 1;
431
}
432
433
490
int ftpQUIT(PFTPCONTEXT context, const char *params)
434
{
435
490
	writelogentry(context, " QUIT", "");
436
490
	sendstring(context, success221);
437
	/*
438
	 * retrun 0 to break command processing loop
439
	 */
440
490
	return 0;
441
}
442
443
7
int ftpNOOP(PFTPCONTEXT context, const char *params)
444
{
445
7
	return sendstring(context, success200);
446
}
447
448
574
int ftpPWD(PFTPCONTEXT context, const char *params)
449
{
450
574
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
451
161
		return sendstring(context, error530);
452
453
413
	strcpy(context->GPBuffer, "257 \"");
454
413
	strcat(context->GPBuffer, context->CurrentDir);
455
413
	strcat(context->GPBuffer, "\" is a current directory.\r\n");
456
413
	return sendstring(context, context->GPBuffer);
457
}
458
459
111
int ftpTYPE(PFTPCONTEXT context, const char *params)
460
{
461
111
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
462
14
		return sendstring(context, error530);
463
464
97
	if (params == NULL)
465
46
		return sendstring(context, error501);
466
467
51
	switch (*params)
468
	{
469
17
	case 'A':
470
	case 'a':
471
17
		return sendstring(context, success200_1);
472
10
	case 'I':
473
	case 'i':
474
10
		return sendstring(context, success200_2);
475
	}
476
477
24
	return sendstring(context, error501);
478
}
479
480
961
int ftpPORT(PFTPCONTEXT context, const char *params)
481
{
482
	int			c;
483
	in_addr_t	DataIP = 0, DataPort = 0;
484
	char		*p = (char *)params;
485
486
961
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
487
204
		return sendstring(context, error530);
488
489
757
	if ( params == NULL )
490
86
		return sendstring(context, error501);
491
492
3264
	for (c = 0; c < 4; c++) {
493
2641
		DataIP += ((unsigned char)strtoul(p, NULL, 10)) << c*8;
494
11284
		while ( (*p >= '0') && (*p <= '9') )
495
8643
			p++;
496
2641
		if ( *p == 0 )
497
			break;
498
2593
		p++;
499
	}
500
501
1615
	for (c = 0; c < 2; c++) {
502
1282
		DataPort += ((unsigned char)strtoul(p, NULL, 10)) << c*8;
503
11794
		while ( (*p >= '0') && (*p <= '9') )
504
10512
			p++;
505
1282
		if ( *p == 0 )
506
			break;
507
944
		p++;
508
	}
509
510
671
	if ( DataIP != context->ClientIPv4 )
511
288
		return sendstring(context, error501);
512
513
383
	context->DataIPv4 = DataIP;
514
383
	context->DataPort = DataPort;
515
383
	context->Mode = MODE_NORMAL;
516
517
383
	return sendstring(context, success200);
518
}
519
520
/*
521
filemode.c -- make a string describing file modes
522
523
  Copyright (C) 1985, 1990, 1993, 1998-2000, 2004, 2006, 2009-2018 Free
524
  Software Foundation, Inc.
525
*/
526
527
/* Return a character indicating the type of file described by
528
   file mode BITS:
529
   '-' regular file
530
   'b' block special file
531
   'c' character special file
532
   'C' high performance ("contiguous data") file
533
   'd' directory
534
   'D' door
535
   'l' symbolic link
536
   'm' multiplexed file (7th edition Unix; obsolete)
537
   'n' network special file (HP-UX)
538
   'p' fifo (named pipe)
539
   'P' port
540
   's' socket
541
   'w' whiteout (4.4BSD)
542
   '?' some other file type  */
543
544
static char
545
ftypelet (mode_t bits)
546
{
547
  /* These are the most common, so test for them first.  */
548
  if (S_ISREG (bits))
549
    return '-';
550
  if (S_ISDIR (bits))
551
    return 'd';
552
553
  /* Other letters standardized by POSIX 1003.1-2004.  */
554
  if (S_ISBLK (bits))
555
    return 'b';
556
  if (S_ISCHR (bits))
557
    return 'c';
558
  if (S_ISLNK (bits))
559
    return 'l';
560
  if (S_ISFIFO (bits))
561
    return 'p';
562
563
  /* Other file types (though not letters) standardized by POSIX.  */
564
  if (S_ISSOCK (bits))
565
    return 's';
566
567
  return '?';
568
}
569
570
/* Like filemodestring, but rely only on MODE.  */
571
572
void
573
strmode (mode_t mode, char *str)
574
{
575
  str[0] = ftypelet (mode);
576
  str[1] = mode & S_IRUSR ? 'r' : '-';
577
  str[2] = mode & S_IWUSR ? 'w' : '-';
578
  str[3] = (mode & S_ISUID
579
            ? (mode & S_IXUSR ? 's' : 'S')
580
            : (mode & S_IXUSR ? 'x' : '-'));
581
  str[4] = mode & S_IRGRP ? 'r' : '-';
582
  str[5] = mode & S_IWGRP ? 'w' : '-';
583
  str[6] = (mode & S_ISGID
584
            ? (mode & S_IXGRP ? 's' : 'S')
585
            : (mode & S_IXGRP ? 'x' : '-'));
586
  str[7] = mode & S_IROTH ? 'r' : '-';
587
  str[8] = mode & S_IWOTH ? 'w' : '-';
588
  str[9] = (mode & S_ISVTX
589
            ? (mode & S_IXOTH ? 't' : 'T')
590
            : (mode & S_IXOTH ? 'x' : '-'));
591
  str[10] = ' ';
592
  str[11] = '\0';
593
}
594
595
/*
596
	END  filemode.c
597
*/
598
599
int list_sub (char *dirname, SOCKET s, gnutls_session_t session, struct dirent *entry)
600
{
601
	char			text[SIZE_OF_GPBUFFER],
602
					sacl[12];
603
604
	struct stat		filestats;
605
	struct tm		ftm_fields;
606
	time_t			deltatime;
607
608
	if (strcmp(entry->d_name, ".") == 0)
609
		return 1;
610
	if (strcmp(entry->d_name, "..") == 0)
611
		return 1;
612
613
	strcpy(text, dirname);
614
	add_last_slash(text);
615
	strcat(text, entry->d_name);
616
617
	if ( lstat(text, &filestats) == 0 )
618
	{
619
		strmode(filestats.st_mode, sacl);
620
621
		localtime_r(&filestats.st_mtime, &ftm_fields);
622
		deltatime = time(NULL) - filestats.st_mtime;
623
624
		if (deltatime <= 180*24*60*60) {
625
			snprintf(text, sizeof(text), "%s %lu %lu %lu %llu %s %02u %02u:%02u %s\r\n",
626
				sacl, filestats.st_nlink,
627
				(unsigned long int)filestats.st_uid,
628
				(unsigned long int)filestats.st_gid,
629
				(unsigned long long int)filestats.st_size,
630
				shortmonths[(ftm_fields.tm_mon)], ftm_fields.tm_mday,
631
				ftm_fields.tm_hour, ftm_fields.tm_min, entry->d_name);
632
		}
633
		else
634
		{
635
			snprintf(text, sizeof(text), "%s %lu %lu %lu %llu %s %02u %02u %s\r\n",
636
				sacl, filestats.st_nlink,
637
				(unsigned long int)filestats.st_uid,
638
				(unsigned long int)filestats.st_gid,
639
				(unsigned long long int)filestats.st_size,
640
				shortmonths[(ftm_fields.tm_mon)], ftm_fields.tm_mday,
641
				ftm_fields.tm_year + 1900, entry->d_name);
642
		}
643
644
	}
645
646
	return sendstring_auto(s, session, text);
647
}
648
649
784
void *list_thread(PFTPCONTEXT context)
650
{
651
	SOCKET				clientsocket;
652
	gnutls_session_t	TLS_datasession;
653
	int					ret;
654
	DIR					*pdir;
655
	struct dirent		*entry;
656
657
784
	pthread_mutex_lock(&context->MTLock);
658
784
	pthread_cleanup_push(cleanup_handler, context);
659
	ret = 0;
660
784
	TLS_datasession = NULL;
661
662
784
	clientsocket = create_datasocket(context);
663
749
	while (clientsocket != INVALID_SOCKET)
664
	{
665
		if (context->TLS_session != NULL)
666
			InitTLSSession(&TLS_datasession, clientsocket, 0);
667
668
		pdir = opendir(context->GPBuffer);
669
		if (pdir == NULL)
670
			break;
671
672
		while ((entry = readdir(pdir)) != NULL) {
673
			ret = list_sub(context->GPBuffer, clientsocket, TLS_datasession, entry);
674
			if ( (ret == 0) || (context->WorkerThreadAbort != 0 ))
675
				break;
676
		}
677
678
		closedir(pdir);
679
		break;
680
	}
681
682
749
	if (TLS_datasession != NULL)
683
	{
684
		gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR);
685
		gnutls_deinit(TLS_datasession);
686
	}
687
688
749
	writelogentry(context, " LIST complete", "");
689
690
749
	if (clientsocket == INVALID_SOCKET) {
691
749
		sendstring(context, error451);
692
	}
693
	else {
694
		if ((context->WorkerThreadAbort == 0) && (ret != 0))
695
			sendstring(context, success226);
696
		else
697
			sendstring(context, error426);
698
699
		close(clientsocket);
700
		context->DataSocket = INVALID_SOCKET;
701
	}
702
703
749
	context->WorkerThreadValid = -1;
704
749
	pthread_cleanup_pop(0);
705
749
	pthread_mutex_unlock(&context->MTLock);
706
749
	return NULL;
707
}
708
709
1095
int ftpLIST(PFTPCONTEXT context, const char *params)
710
{
711
	struct	stat	filestats;
712
	pthread_t		tid;
713
714
1095
	if (context->Access == FTP_ACCESS_NOT_LOGGED_IN)
715
200
		return sendstring(context, error530);
716
895
	if (context->WorkerThreadValid == 0)
717
37
		return sendstring(context, error550_t);
718
719
858
	if (params != NULL)
720
	{
721

74
		if ((strcmp(params, "-a") == 0) || (strcmp(params, "-l") == 0))
722
			params = NULL;
723
	}
724
725
858
	if (finalpath(
726
858
			context->RootDir,
727
858
			context->CurrentDir,
728
			(char *)params, context->GPBuffer) == NULL)
729
		return 0;
730
731
858
	while (stat(context->GPBuffer, &filestats) == 0)
732
	{
733
784
		if ( !S_ISDIR(filestats.st_mode) )
734
			break;
735
736
784
		sendstring(context, interm150);
737
784
		writelogentry(context, " LIST", (char *)params);
738
784
		context->WorkerThreadAbort = 0;
739
740
784
		pthread_mutex_lock(&context->MTLock);
741
742
784
		context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&list_thread, context);
743
784
		if ( context->WorkerThreadValid == 0 )
744
784
			context->WorkerThreadId = tid;
745
		else
746
			sendstring(context, error451);
747
748
784
		pthread_mutex_unlock(&context->MTLock);
749
750
784
		return 1;
751
	}
752
753
74
	return sendstring(context, error550);
754
}
755
756
159
int ftpCDUP(PFTPCONTEXT context, const char *params)
757
{
758
159
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
759
19
		return sendstring(context, error530);
760
761
140
	if ( strcmp(context->CurrentDir, "/") == 0 )
762
139
		return sendstring(context, success250);
763
764
	delete_last_slash(context->CurrentDir);
765
	filepath(context->CurrentDir);
766
767
1
	writelogentry(context, " CDUP", "");
768
1
	return sendstring(context, success250);
769
}
770
771
116
int ftpCWD(PFTPCONTEXT context, const char *params)
772
{
773
	struct	stat	filestats;
774
	size_t			rl;
775
776
116
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
777
14
		return sendstring(context, error530);
778
779
102
	if ( params == NULL )
780
26
		return sendstring(context, error501);
781
782
76
	if (finalpath(
783
76
			context->RootDir,
784
76
			context->CurrentDir,
785
			(char *)params, context->GPBuffer) == NULL)
786
		return 0;
787
788
76
	if ( stat(context->GPBuffer, &filestats) == 0 )
789
25
		if ( S_ISDIR(filestats.st_mode) )
790
		{
791
25
			rl = strlen(context->RootDir);
792
25
			strcpy(context->CurrentDir, context->GPBuffer+rl);
793
25
			writelogentry(context, " CWD: ", context->CurrentDir);
794
25
			return sendstring(context, success250);
795
		}
796
797
51
	return sendstring(context, error550);
798
}
799
800
void *retr_thread(PFTPCONTEXT context)
801
{
802
	volatile SOCKET		clientsocket;
803
	int					sent_ok, f;
804
	off_t				offset;
805
	ssize_t				sz, sz_total;
806
	size_t				buffer_size;
807
	char				*buffer;
808
	struct timespec		t;
809
	signed long long	lt0, lt1, dtx;
810
	gnutls_session_t	TLS_datasession;
811
812
	pthread_mutex_lock(&context->MTLock);
813
	pthread_cleanup_push(cleanup_handler, context);
814
815
	sent_ok = 0;
816
	sz_total = 0;
817
	buffer = NULL;
818
	TLS_datasession = NULL;
819
	f = -1;
820
	clock_gettime(CLOCK_MONOTONIC, &t);
821
	lt0 = t.tv_sec*1e9 + t.tv_nsec;
822
823
	buffer = malloc(TRANSMIT_BUFFER_SIZE);
824
	while (buffer != NULL)
825
	{
826
        clientsocket = create_datasocket(context);
827
        if (clientsocket == INVALID_SOCKET)
828
            break;
829
830
		if (context->TLS_session != NULL)
831
		{
832
			InitTLSSession(&TLS_datasession, clientsocket, 0);
833
			buffer_size = gnutls_record_get_max_size(TLS_datasession);
834
			if (buffer_size > TRANSMIT_BUFFER_SIZE)
835
				buffer_size = TRANSMIT_BUFFER_SIZE;
836
		}
837
		else
838
			buffer_size = TRANSMIT_BUFFER_SIZE;
839
840
		f = open(context->GPBuffer, O_RDONLY);
841
		context->File = f;
842
		if (f == -1)
843
			break;
844
845
		offset = lseek(f, context->RestPoint, SEEK_SET);
846
		if (offset != context->RestPoint)
847
			break;
848
849
		while ( context->WorkerThreadAbort == 0 ) {
850
			sz = read(f, buffer, buffer_size);
851
			if (sz <= 0)
852
				break;
853
854
			sz_total += sz;
855
856
			if (send_auto(clientsocket, TLS_datasession, buffer, sz) == sz)
857
				sent_ok = 1;
858
			else
859
			{
860
				sent_ok = 0;
861
				break;
862
			}
863
		}
864
865
		break;
866
	}
867
868
	clock_gettime(CLOCK_MONOTONIC, &t);
869
	lt1 = t.tv_sec*1e9 + t.tv_nsec;
870
871
	if (f != -1)
872
		close(f);
873
874
	context->File = -1;
875
876
	if (TLS_datasession != NULL)
877
	{
878
		gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR);
879
		gnutls_deinit(TLS_datasession);
880
	}
881
882
	/* calculating performance */
883
	dtx = lt1 - lt0;
884
885
    if (buffer != NULL) {
886
	    sprintf(buffer,  " RETR complete. %zd bytes (%f MBytes) total sent in %f seconds (%f MBytes/s)",
887
	    	sz_total, sz_total/1048576.0, dtx/1000000000.0, (1000000000.0*sz_total)/dtx/1048576);
888
        writelogentry(context, buffer, "");
889
        free(buffer);
890
    }
891
892
	if (clientsocket == INVALID_SOCKET) {
893
		sendstring(context, error451);
894
	}
895
	else {
896
		if ((context->WorkerThreadAbort == 0) && (sent_ok != 0))
897
			sendstring(context, success226);
898
		else
899
			sendstring(context, error426);
900
901
		close(clientsocket);
902
		context->DataSocket = INVALID_SOCKET;
903
	}
904
905
	context->WorkerThreadValid = -1;
906
	pthread_cleanup_pop(0);
907
	pthread_mutex_unlock(&context->MTLock);
908
	return NULL;
909
}
910
911
102
int ftpRETR(PFTPCONTEXT context, const char *params)
912
{
913
	struct	stat	filestats;
914
	pthread_t		tid;
915
916
102
	if (context->Access == FTP_ACCESS_NOT_LOGGED_IN)
917
37
		return sendstring(context, error530);
918
65
	if (context->WorkerThreadValid == 0)
919
13
		return sendstring(context, error550_t);
920
52
	if ( params == NULL )
921
28
		return sendstring(context, error501);
922
923
24
	if ( context->File != -1 ) {
924
		close(context->File);
925
		context->File = -1;
926
	}
927
928
24
	if (finalpath(
929
24
			context->RootDir,
930
24
			context->CurrentDir,
931
			(char *)params, context->GPBuffer) == NULL)
932
		return 0;
933
934
24
	while (stat(context->GPBuffer, &filestats) == 0)
935
	{
936
4
		if ( S_ISDIR(filestats.st_mode) )
937
			break;
938
939
		sendstring(context, interm150);
940
		writelogentry(context, " RETR: ", (char *)params);
941
		context->WorkerThreadAbort = 0;
942
943
		pthread_mutex_lock(&context->MTLock);
944
945
		context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&retr_thread, context);
946
		if ( context->WorkerThreadValid == 0 )
947
			context->WorkerThreadId = tid;
948
		else
949
			sendstring(context, error451);
950
951
		pthread_mutex_unlock(&context->MTLock);
952
953
		return 1;
954
	}
955
956
24
	return sendstring(context, error550);
957
}
958
959
175
int ftpABOR(PFTPCONTEXT context, const char *params)
960
{
961
175
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
962
39
		return sendstring(context, error530);
963
964
136
	writelogentry(context, " ABORT command", NULL);
965
136
	WorkerThreadCleanup(context);
966
135
	return sendstring(context, success226);
967
}
968
969
350
int ftpDELE(PFTPCONTEXT context, const char *params)
970
{
971
350
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
972
22
		return sendstring(context, error530);
973
328
	if ( context->Access < FTP_ACCESS_FULL )
974
53
		return sendstring(context, error550_r);
975
275
	if ( params == NULL )
976
146
		return sendstring(context, error501);
977
978
129
	if (finalpath(
979
129
			context->RootDir,
980
129
			context->CurrentDir,
981
			(char *)params, context->GPBuffer) == NULL)
982
		return 0;
983
984
129
	if ( unlink(context->GPBuffer) == 0 ) {
985
		sendstring(context, success250);
986
		writelogentry(context, " DELE: ", (char *)params);
987
	}
988
	else
989
129
		sendstring(context, error550_r);
990
991
	return 1;
992
}
993
994
449
int pasv(PFTPCONTEXT context)
995
{
996
	SOCKET				datasocket;
997
	struct sockaddr_in	laddr;
998
	int					socketret = -1, result = 0;
999
	unsigned long		c;
1000
	struct	timespec	rtctime;
1001
1002
	while (1)
1003
	{
1004
449
		if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1005
		{
1006
82
			sendstring(context, error530);
1007
82
			break;
1008
		}
1009
1010
367
		if ( context->WorkerThreadValid == 0 )
1011
		{
1012
55
			sendstring(context, error550_t);
1013
55
			break;
1014
		}
1015
1016
312
		if ( context->DataSocket != INVALID_SOCKET )
1017
197
			close(context->DataSocket);
1018
1019
312
		context->DataSocket = INVALID_SOCKET;
1020
1021
312
		datasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1022
312
		if (datasocket == INVALID_SOCKET)
1023
		{
1024
			sendstring(context, error451);
1025
			break;
1026
		}
1027
1028
312
		for (c = g_cfg.PasvPortBase; c <= g_cfg.PasvPortMax; c++) {
1029
312
			clock_gettime(CLOCK_REALTIME, &rtctime);
1030
			memset(&laddr, 0, sizeof(laddr));
1031
312
			laddr.sin_family = AF_INET;
1032
312
			laddr.sin_port = htons((in_port_t)(g_cfg.PasvPortBase +
1033
				(rtctime.tv_nsec % (g_cfg.PasvPortMax-g_cfg.PasvPortBase))));
1034
312
			laddr.sin_addr.s_addr = context->ServerIPv4;
1035
312
			socketret = bind(datasocket, (struct sockaddr *)&laddr, sizeof(laddr));
1036
312
			if ( socketret == 0 )
1037
				break;
1038
		}
1039
1040
312
		if ( socketret != 0 ) {
1041
			close(datasocket);
1042
			sendstring(context, error451);
1043
			break;
1044
		}
1045
1046
312
		socketret = listen(datasocket, SOMAXCONN);
1047
312
		if (socketret != 0) {
1048
			close(datasocket);
1049
			sendstring(context, error451);
1050
			break;
1051
		}
1052
1053
312
		if ((context->ClientIPv4 & g_cfg.LocalIPMask) == (context->ServerIPv4 & g_cfg.LocalIPMask))
1054
		{
1055
312
			context->DataIPv4 = context->ServerIPv4;
1056
312
			writelogentry(context, " local client.", "");
1057
		} else {
1058
			context->DataIPv4 = g_cfg.ExternalInterface;
1059
			writelogentry(context, " nonlocal client.", "");
1060
		}
1061
1062
312
		context->DataPort = laddr.sin_port;
1063
312
		context->DataSocket = datasocket;
1064
312
		context->Mode = MODE_PASSIVE;
1065
1066
		result = 1;
1067
312
		break;
1068
	}
1069
1070
449
	return result;
1071
}
1072
1073
163
int ftpEPSV (PFTPCONTEXT context, const char *params)
1074
{
1075
163
	if (pasv(context) == 0)
1076
		return 1;
1077
1078
111
	snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, success229,
1079
111
			ntohs(context->DataPort));
1080
1081
111
	writelogentry(context, " entering extended passive mode", "");
1082
1083
111
	return sendstring(context, context->GPBuffer);
1084
}
1085
1086
286
int ftpPASV(PFTPCONTEXT context, const char *params)
1087
{
1088
286
	if (pasv(context) == 0)
1089
		return 1;
1090
1091
201
	snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, success227,
1092
			context->DataIPv4 & 0xff,
1093
201
			(context->DataIPv4 >> 8) & 0xff,
1094
201
			(context->DataIPv4 >> 16) & 0xff,
1095
201
			(context->DataIPv4 >> 24) & 0xff,
1096
			context->DataPort & 0xff,
1097
201
			(context->DataPort >> 8) & 0xff);
1098
1099
201
	writelogentry(context, " entering passive mode", "");
1100
1101
201
	return sendstring(context, context->GPBuffer);
1102
}
1103
1104
1811
int ftpPASS(PFTPCONTEXT context, const char *params)
1105
{
1106
	char	temptext[256];
1107
1108
1811
	if ( params == NULL )
1109
67
		return sendstring(context, error501);
1110
1111
	memset(temptext, 0, sizeof(temptext));
1112
1113
	/*
1114
	 * we have username saved in context->GPBuffer from USER command
1115
	 */
1116
1744
	if (!ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "pswd", temptext, sizeof(temptext)))
1117
773
		return sendstring(context, error530_r);
1118
1119

971
	if ( (strcmp(temptext, params) == 0) || (temptext[0] == '*') )
1120
	{
1121
796
		memset(context->RootDir, 0, sizeof(context->RootDir));
1122
		memset(temptext, 0, sizeof(temptext));
1123
1124
796
		ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "root", context->RootDir, sizeof(context->RootDir));
1125
796
		ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "accs", temptext, sizeof(temptext));
1126
1127
796
		context->Access = FTP_ACCESS_NOT_LOGGED_IN;
1128
		do {
1129
1130
796
			if ( strcasecmp(temptext, "admin") == 0 ) {
1131
547
				context->Access = FTP_ACCESS_FULL;
1132
547
				break;
1133
			}
1134
1135
249
			if ( strcasecmp(temptext, "upload") == 0 ) {
1136
28
				context->Access = FTP_ACCESS_CREATENEW;
1137
28
				break;
1138
			}
1139
1140
221
			if ( strcasecmp(temptext, "readonly") == 0 ) {
1141
221
				context->Access = FTP_ACCESS_READONLY;
1142
221
				break;
1143
			}
1144
1145
			return sendstring(context, error530_b);
1146
		} while (0);
1147
1148
796
		writelogentry(context, " PASS->successful logon", "");
1149
	}
1150
	else
1151
175
		return sendstring(context, error530_r);
1152
1153
796
	return sendstring(context, success230);
1154
}
1155
1156
47
int ftpREST(PFTPCONTEXT context, const char *params)
1157
{
1158
47
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1159
13
		return sendstring(context, error530);
1160
1161
34
	if ( params == NULL )
1162
12
		return sendstring(context, error501);
1163
1164
22
	context->RestPoint = strtoull(params, NULL, 10);
1165
22
	snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, "%s %llu\r\n",
1166
			interm350, (unsigned long long int)context->RestPoint);
1167
1168
22
	return sendstring(context, context->GPBuffer);
1169
}
1170
1171
150
int ftpSIZE(PFTPCONTEXT context, const char *params)
1172
{
1173
	struct stat		filestats;
1174
1175
150
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1176
29
		return sendstring(context, error530);
1177
121
	if ( params == NULL )
1178
87
		return sendstring(context, error501);
1179
1180
34
	if (finalpath(
1181
34
			context->RootDir,
1182
34
			context->CurrentDir,
1183
			(char *)params, context->GPBuffer) == NULL)
1184
		return 0;
1185
1186
34
	if ( stat(context->GPBuffer, &filestats) == 0 )
1187
	{
1188
2
		snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, "213 %llu\r\n",
1189
2
				(unsigned long long int)filestats.st_size);
1190
2
		sendstring(context, context->GPBuffer);
1191
	}
1192
	else
1193
32
		sendstring(context, error550);
1194
1195
	return 1;
1196
}
1197
1198
1142
int ftpMKD(PFTPCONTEXT context, const char *params)
1199
{
1200
1142
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1201
218
		return sendstring(context, error530);
1202
924
	if ( context->Access < FTP_ACCESS_CREATENEW )
1203
82
		return sendstring(context, error550_r);
1204
842
	if ( params == NULL )
1205
58
		return sendstring(context, error501);
1206
1207
784
	if (finalpath(
1208
784
			context->RootDir,
1209
784
			context->CurrentDir,
1210
			(char *)params, context->GPBuffer) == NULL)
1211
		return 0;
1212
1213
784
	if ( mkdir(context->GPBuffer, 0755) == 0 ) {
1214
691
		sendstring(context, success257);
1215
691
		writelogentry(context, " MKD: ", (char *)params);
1216
	}
1217
	else
1218
93
		sendstring(context, error550_r);
1219
1220
	return 1;
1221
}
1222
1223
55
int ftpRMD(PFTPCONTEXT context, const char *params)
1224
{
1225
55
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1226
15
		return sendstring(context, error530);
1227
40
	if ( context->Access < FTP_ACCESS_FULL )
1228
14
		return sendstring(context, error550_r);
1229
26
	if ( params == NULL )
1230
6
		return sendstring(context, error501);
1231
1232
20
	if (finalpath(
1233
20
			context->RootDir,
1234
20
			context->CurrentDir,
1235
			(char *)params, context->GPBuffer) == NULL)
1236
		return 0;
1237
1238
20
	if ( rmdir(context->GPBuffer) == 0 ) {
1239
1
		sendstring(context, success250);
1240
1
		writelogentry(context, " DELE: ", (char *)params);
1241
	}
1242
	else
1243
19
		sendstring(context, error550_r);
1244
1245
	return 1;
1246
}
1247
1248
22
void *stor_thread(PFTPCONTEXT context)
1249
{
1250
	SOCKET				clientsocket;
1251
	int					f;
1252
	ssize_t				sz, sz_total;
1253
	char				*buffer;
1254
	struct timespec		t;
1255
	signed long long	lt0, lt1, dtx;
1256
	gnutls_session_t	TLS_datasession;
1257
1258
22
	pthread_mutex_lock(&context->MTLock);
1259
22
	pthread_cleanup_push(cleanup_handler, context);
1260
1261
	f = -1;
1262
	sz_total = 0;
1263
	buffer = NULL;
1264
22
	TLS_datasession = NULL;
1265
22
	clock_gettime(CLOCK_MONOTONIC, &t);
1266
22
	lt0 = t.tv_sec*1e9 + t.tv_nsec;
1267
1268
22
	clientsocket = create_datasocket(context);
1269
21
	while (clientsocket != INVALID_SOCKET)
1270
	{
1271
		buffer = malloc(TRANSMIT_BUFFER_SIZE);
1272
		if (buffer == NULL)
1273
			break;
1274
1275
		if (context->TLS_session != NULL)
1276
			InitTLSSession(&TLS_datasession, clientsocket, 0);
1277
1278
		f = open(context->GPBuffer, context->CreateMode, S_IRWXU | S_IRGRP | S_IROTH);
1279
		context->File = f;
1280
		if (f == -1)
1281
			break;
1282
1283
		while ( context->WorkerThreadAbort == 0 ) {
1284
			sz = recv_auto(clientsocket, TLS_datasession, buffer, TRANSMIT_BUFFER_SIZE);
1285
			if (sz > 0)
1286
			{
1287
				sz_total += sz;
1288
				write(f, buffer, sz);
1289
			}
1290
			else
1291
				break;
1292
		}
1293
1294
		break;
1295
	}
1296
1297
21
	clock_gettime(CLOCK_MONOTONIC, &t);
1298
21
	lt1 = t.tv_sec*1e9 + t.tv_nsec;
1299
1300
21
	if (f != -1)
1301
		close(f);
1302
1303
21
	context->File = -1;
1304
1305
21
	if (TLS_datasession != NULL)
1306
	{
1307
		gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR);
1308
		gnutls_deinit(TLS_datasession);
1309
	}
1310
1311
	/* calculating performance */
1312
21
	if (buffer != NULL)
1313
	{
1314
		dtx = lt1 - lt0;
1315
		sprintf(buffer,  " STOR complete. %zd bytes (%f MBytes) total sent in %f seconds (%f MBytes/s)",
1316
				sz_total, sz_total/1048576.0, dtx/1000000000.0, (1000000000.0*sz_total)/dtx/1048576);
1317
		writelogentry(context, buffer, "");
1318
		free(buffer);
1319
	}
1320
1321
21
	if (clientsocket == INVALID_SOCKET) {
1322
21
		sendstring(context, error451);
1323
	}
1324
	else {
1325
		if (context->WorkerThreadAbort == 0)
1326
			sendstring(context, success226);
1327
		else
1328
			sendstring(context, error426);
1329
1330
		close(clientsocket);
1331
		context->DataSocket = INVALID_SOCKET;
1332
	}
1333
1334
21
	context->WorkerThreadValid = -1;
1335
21
	pthread_cleanup_pop(0);
1336
21
	pthread_mutex_unlock(&context->MTLock);
1337
21
	return NULL;
1338
}
1339
1340
62
int ftpSTOR(PFTPCONTEXT context, const char *params)
1341
{
1342
	struct	stat	filestats;
1343
	pthread_t		tid;
1344
1345
62
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1346
13
		return sendstring(context, error530);
1347
49
	if ( context->Access < FTP_ACCESS_CREATENEW )
1348
11
		return sendstring(context, error550_r);
1349
38
	if ( params == NULL )
1350
13
		return sendstring(context, error501);
1351
25
	if ( context->WorkerThreadValid == 0 )
1352
3
		return sendstring(context, error550_t);
1353
1354
22
	if ( context->File != -1 ) {
1355
		close(context->File);
1356
		context->File = -1;
1357
	}
1358
1359
22
	if (finalpath(
1360
22
			context->RootDir,
1361
22
			context->CurrentDir,
1362
			(char *)params, context->GPBuffer) == NULL)
1363
		return 0;
1364
1365
22
	if ( context->Access == FTP_ACCESS_FULL )
1366
12
		context->CreateMode = O_CREAT | O_WRONLY| O_TRUNC;
1367
	else
1368
	{
1369
10
		context->CreateMode = O_CREAT | O_WRONLY| O_EXCL;
1370
10
		if (stat(context->GPBuffer, &filestats) == 0)
1371
			return sendstring(context, error550_r);
1372
	}
1373
1374
22
	sendstring(context, interm150);
1375
22
	writelogentry(context, " STOR: ", (char *)params);
1376
22
	context->WorkerThreadAbort = 0;
1377
1378
22
	pthread_mutex_lock(&context->MTLock);
1379
1380
22
	context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&stor_thread, context);
1381
22
	if ( context->WorkerThreadValid == 0 )
1382
22
		context->WorkerThreadId = tid;
1383
	else
1384
		sendstring(context, error451);
1385
1386
22
	pthread_mutex_unlock(&context->MTLock);
1387
1388
22
	return 1;
1389
}
1390
1391
801
int ftpSYST(PFTPCONTEXT context, const char *params)
1392
{
1393
801
	return sendstring(context, success215);
1394
}
1395
1396
45
int ftpHELP(PFTPCONTEXT context, const char *params)
1397
{
1398
45
	return sendstring(context, success214);
1399
}
1400
1401
35
int ftpSITE(PFTPCONTEXT context, const char *params)
1402
{
1403
35
	if ( params != NULL )
1404
12
		if (strcasecmp(params, "help") == 0)
1405
			return sendstring(context, "200 chmod\r\n");
1406
1407
35
	return sendstring(context, error500);
1408
}
1409
1410
13
int ftpFEAT(PFTPCONTEXT context, const char *params)
1411
{
1412
13
	return sendstring(context, success211);
1413
}
1414
1415
void *append_thread(PFTPCONTEXT context)
1416
{
1417
	SOCKET				clientsocket;
1418
	int					f;
1419
	ssize_t				sz;
1420
	char				*buffer = NULL;
1421
	gnutls_session_t	TLS_datasession;
1422
1423
	pthread_mutex_lock(&context->MTLock);
1424
	pthread_cleanup_push(cleanup_handler, context);
1425
	TLS_datasession = NULL;
1426
	f = -1;
1427
1428
	clientsocket = create_datasocket(context);
1429
	while (clientsocket != INVALID_SOCKET)
1430
	{
1431
		if (context->TLS_session != NULL)
1432
			InitTLSSession(&TLS_datasession, clientsocket, 0);
1433
1434
		f = open(context->GPBuffer, O_RDWR);
1435
		context->File = f;
1436
		if (f == -1)
1437
			break;
1438
1439
		lseek(f, 0, SEEK_END);
1440
		buffer = malloc(TRANSMIT_BUFFER_SIZE);
1441
		if (buffer == NULL)
1442
			break;
1443
1444
		while ( context->WorkerThreadAbort == 0 ) {
1445
			sz = recv_auto(clientsocket, TLS_datasession, buffer, TRANSMIT_BUFFER_SIZE);
1446
			if (sz > 0)
1447
				write(f, buffer, sz);
1448
			else
1449
				break;
1450
		}
1451
1452
		break;
1453
	}
1454
1455
	if (buffer != NULL)
1456
		free(buffer);
1457
1458
	if (f != -1)
1459
		close(f);
1460
1461
	context->File = -1;
1462
1463
	if (TLS_datasession != NULL)
1464
	{
1465
		gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR);
1466
		gnutls_deinit(TLS_datasession);
1467
	}
1468
1469
	writelogentry(context, " STOR complete", "");
1470
1471
	if (clientsocket == INVALID_SOCKET) {
1472
		sendstring(context, error451);
1473
	}
1474
	else {
1475
		if (context->WorkerThreadAbort == 0)
1476
			sendstring(context, success226);
1477
		else
1478
			sendstring(context, error426);
1479
1480
		close(clientsocket);
1481
		context->DataSocket = INVALID_SOCKET;
1482
	}
1483
1484
	context->WorkerThreadValid = -1;
1485
	pthread_cleanup_pop(0);
1486
	pthread_mutex_unlock(&context->MTLock);
1487
	return NULL;
1488
}
1489
1490
86
int ftpAPPE(PFTPCONTEXT context, const char *params)
1491
{
1492
	struct	stat	filestats;
1493
	pthread_t		tid;
1494
1495
86
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1496
17
		return sendstring(context, error530);
1497
69
	if ( context->Access < FTP_ACCESS_FULL )
1498
19
		return sendstring(context, error550_r);
1499
50
	if ( params == NULL )
1500
21
		return sendstring(context, error501);
1501
29
	if ( context->WorkerThreadValid == 0 )
1502
10
		return sendstring(context, error550_t);
1503
1504
19
	if ( context->File != -1 ) {
1505
		close(context->File);
1506
		context->File = -1;
1507
	}
1508
1509
19
	if (finalpath(
1510
19
			context->RootDir,
1511
19
			context->CurrentDir,
1512
			(char *)params, context->GPBuffer) == NULL)
1513
		return 0;
1514
1515
	/*
1516
	 * stat must NOT fail
1517
	 */
1518
19
	while (stat(context->GPBuffer, &filestats) == 0)
1519
	{
1520
		/*
1521
		 * do not try to "append" for directories
1522
		*/
1523
		if ( S_ISDIR(filestats.st_mode) )
1524
			break;
1525
1526
		sendstring(context, interm150);
1527
		writelogentry(context, " APPE: ", (char *)params);
1528
		context->WorkerThreadAbort = 0;
1529
1530
		pthread_mutex_lock(&context->MTLock);
1531
1532
		context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&append_thread, context);
1533
		if ( context->WorkerThreadValid == 0 )
1534
			context->WorkerThreadId = tid;
1535
		else
1536
			sendstring(context, error451);
1537
1538
		pthread_mutex_unlock(&context->MTLock);
1539
1540
		return 1;
1541
	}
1542
1543
19
	return sendstring(context, error550);
1544
}
1545
1546
166
int ftpRNFR(PFTPCONTEXT context, const char *params)
1547
{
1548
	struct stat		filestats;
1549
1550
166
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1551
14
		return sendstring(context, error530);
1552
152
	if ( context->Access < FTP_ACCESS_FULL )
1553
18
		return sendstring(context, error550_r);
1554
134
	if ( params == NULL )
1555
6
		return sendstring(context, error501);
1556
1557
128
	if (finalpath(
1558
128
			context->RootDir,
1559
128
			context->CurrentDir,
1560
			(char *)params, context->GPBuffer) == NULL)
1561
		return 0;
1562
1563
128
	if ( stat(context->GPBuffer, &filestats) == 0 )
1564
	{
1565
		writelogentry(context, " RNFR: ", context->GPBuffer);
1566
		sendstring(context, interm350_ren);
1567
	}
1568
	else
1569
128
		sendstring(context, error550);
1570
1571
	return 1;
1572
}
1573
1574
226
int ftpRNTO(PFTPCONTEXT context, const char *params)
1575
{
1576
	char	*_text;
1577
1578
226
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1579
28
		return sendstring(context, error530);
1580
198
	if ( context->Access < FTP_ACCESS_FULL )
1581
49
		return sendstring(context, error550_r);
1582
149
	if ( params == NULL )
1583
105
		return sendstring(context, error501);
1584
1585
44
	_text = x_malloc(SIZE_OF_GPBUFFER);
1586
1587
44
	if (finalpath(
1588
44
			context->RootDir,
1589
44
			context->CurrentDir,
1590
			(char *)params, _text) == NULL)
1591
	{
1592
		free(_text);
1593
		return 0;
1594
	}
1595
1596
44
	if ( rename(context->GPBuffer, _text) == 0 )
1597
	{
1598
13
		writelogentry(context, " RNTO: ", _text);
1599
13
		sendstring(context, success250);
1600
	}
1601
	else
1602
31
		sendstring(context, error550);
1603
1604
44
	free(_text);
1605
44
	return 1;
1606
}
1607
1608
72
int ftpOPTS(PFTPCONTEXT context, const char *params)
1609
{
1610
72
	if ( params != NULL )
1611
52
		if (strcasecmp(params, "utf8 on") == 0)
1612
			return sendstring(context, "200 Always in UTF8 mode.\r\n");
1613
1614
72
	writelogentry(context, " unsupported OPTS: ", params);
1615
72
	return sendstring(context, error500);
1616
}
1617
1618
356
int ftpAUTH(PFTPCONTEXT context, const char *params)
1619
{
1620
356
	if ( params == NULL )
1621
43
		return sendstring(context, error501);
1622
1623
313
	if ( strcasecmp(params, "TLS") == 0 )
1624
		/* InitTLSSession will send reply */
1625
		return InitTLSSession(&context->TLS_session, context->ControlSocket, 1);
1626
	else
1627
313
		return sendstring(context, error504);
1628
}
1629
1630
18
int ftpPBSZ (PFTPCONTEXT context, const char *params)
1631
{
1632
18
	if ( params == NULL )
1633
12
		return sendstring(context, error501);
1634
1635
6
	if ( context->TLS_session == NULL )
1636
6
		return sendstring(context, error503);
1637
1638
	context->BlockSize = strtoul(params, NULL, 10);
1639
	return sendstring(context, success200);
1640
}
1641
1642
71
int ftpPROT (PFTPCONTEXT context, const char *params)
1643
{
1644
71
	if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1645
16
		return sendstring(context, error530);
1646
1647
55
	if ( params == NULL )
1648
3
			return sendstring(context, error501);
1649
1650
52
	if ( context->TLS_session == NULL )
1651
52
		return sendstring(context, error503);
1652
1653
	switch (*params)
1654
	{
1655
	case 'C':
1656
		context->DataProtectionLevel = 0;
1657
		return sendstring(context, success200);
1658
		break;
1659
1660
	case 'P':
1661
		context->DataProtectionLevel = 100;
1662
		return sendstring(context, success200);
1663
		break;
1664
1665
	default:
1666
		return sendstring(context, error504);
1667
	}
1668
}
1669
1670
int mlsd_sub (char *dirname, SOCKET s, gnutls_session_t session, struct dirent *entry)
1671
{
1672
	char			text[SIZE_OF_GPBUFFER], *entrytype, *sizetype;
1673
	struct stat		filestats;
1674
	struct tm		ftm_fields;
1675
1676
	if (strcmp(entry->d_name, ".") == 0)
1677
		return 1;
1678
	if (strcmp(entry->d_name, "..") == 0)
1679
		return 1;
1680
1681
	strcpy(text, dirname);
1682
	add_last_slash(text);
1683
	strcat(text, entry->d_name);
1684
1685
	if ( lstat(text, &filestats) == 0 )
1686
	{
1687
		if ( S_ISDIR(filestats.st_mode) )
1688
		{
1689
			entrytype = "dir";
1690
			sizetype = "sizd";
1691
		}
1692
		else
1693
		{
1694
			entrytype = "file";
1695
			sizetype = "size";
1696
		}
1697
1698
		if (S_ISLNK(filestats.st_mode))
1699
		{
1700
			entrytype = "OS.unix=slink";
1701
		}
1702
1703
		localtime_r(&filestats.st_mtime, &ftm_fields);
1704
		++ftm_fields.tm_mon;
1705
1706
		snprintf(text, sizeof(text),
1707
				"type=%s;%s=%llu;UNIX.mode=%lo;UNIX.owner=%lu;UNIX.group=%lu;modify=%u%02u%02u%02u%02u%02u; %s\r\n",
1708
				entrytype, sizetype,
1709
				(unsigned long long int)filestats.st_size,
1710
				(unsigned long int)filestats.st_mode,
1711
				(unsigned long int)filestats.st_uid,
1712
				(unsigned long int)filestats.st_gid,
1713
				ftm_fields.tm_year + 1900, ftm_fields.tm_mon, ftm_fields.tm_mday,
1714
				ftm_fields.tm_hour, ftm_fields.tm_min, ftm_fields.tm_sec, entry->d_name
1715
				);
1716
	}
1717
1718
	return sendstring_auto(s, session, text);
1719
}
1720
1721
50
void *msld_thread(PFTPCONTEXT context)
1722
{
1723
	SOCKET				clientsocket;
1724
	gnutls_session_t	TLS_datasession;
1725
	int					ret;
1726
	DIR					*pdir;
1727
	struct dirent		*entry;
1728
1729
50
	pthread_mutex_lock(&context->MTLock);
1730
50
	pthread_cleanup_push(cleanup_handler, context);
1731
	ret = 0;
1732
50
	TLS_datasession = NULL;
1733
1734
50
	clientsocket = create_datasocket(context);
1735
38
	while (clientsocket != INVALID_SOCKET)
1736
	{
1737
		if (context->TLS_session != NULL)
1738
			InitTLSSession(&TLS_datasession, clientsocket, 0);
1739
1740
		pdir = opendir(context->GPBuffer);
1741
		if (pdir == NULL)
1742
			break;
1743
1744
		while ((entry = readdir(pdir)) != NULL) {
1745
			ret = mlsd_sub(context->GPBuffer, clientsocket, TLS_datasession, entry);
1746
			if ( (ret == 0) || (context->WorkerThreadAbort != 0 ))
1747
				break;
1748
		}
1749
1750
		closedir(pdir);
1751
		break;
1752
	}
1753
1754
38
	if (TLS_datasession != NULL)
1755
	{
1756
		gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR);
1757
		gnutls_deinit(TLS_datasession);
1758
	}
1759
1760
38
	writelogentry(context, " LIST complete", "");
1761
1762
38
	if (clientsocket == INVALID_SOCKET) {
1763
38
		sendstring(context, error451);
1764
	}
1765
	else {
1766
		if ((context->WorkerThreadAbort == 0) && (ret != 0))
1767
			sendstring(context, success226);
1768
		else
1769
			sendstring(context, error426);
1770
1771
		close(clientsocket);
1772
		context->DataSocket = INVALID_SOCKET;
1773
	}
1774
1775
38
	context->WorkerThreadValid = -1;
1776
38
	pthread_cleanup_pop(0);
1777
38
	pthread_mutex_unlock(&context->MTLock);
1778
38
	return NULL;
1779
}
1780
1781
138
int ftpMLSD(PFTPCONTEXT context, const char *params)
1782
{
1783
	struct	stat	filestats;
1784
	pthread_t		tid;
1785
1786
138
	if (context->Access == FTP_ACCESS_NOT_LOGGED_IN)
1787
31
		return sendstring(context, error530);
1788
107
	if (context->WorkerThreadValid == 0)
1789
18
		return sendstring(context, error550_t);
1790
1791
89
	if (finalpath(
1792
89
			context->RootDir,
1793
89
			context->CurrentDir,
1794
			(char *)params, context->GPBuffer) == NULL)
1795
		return 0;
1796
1797
89
	while (stat(context->GPBuffer, &filestats) == 0)
1798
	{
1799
50
		if ( !S_ISDIR(filestats.st_mode) )
1800
			break;
1801
1802
50
		sendstring(context, interm150);
1803
50
		writelogentry(context, " MLSD-LIST ", (char *)params);
1804
50
		context->WorkerThreadAbort = 0;
1805
1806
50
		pthread_mutex_lock(&context->MTLock);
1807
1808
50
		context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&msld_thread, context);
1809
50
		if ( context->WorkerThreadValid == 0 )
1810
50
			context->WorkerThreadId = tid;
1811
		else
1812
			sendstring(context, error451);
1813
1814
50
		pthread_mutex_unlock(&context->MTLock);
1815
1816
50
		return 1;
1817
	}
1818
1819
39
	return sendstring(context, error550);
1820
}
1821
1822
14534
int recvcmd(PFTPCONTEXT context, char *buffer, size_t buffer_size)
1823
{
1824
	ssize_t	l, p = 0;
1825
1826
14534
	if ( buffer_size < 5 )
1827
		return 0;
1828
1829
	memset(buffer, 0, buffer_size);
1830
14534
	--buffer_size;
1831
1832
14816
	while (buffer_size > 0)
1833
	{
1834
14792
		if (context->TLS_session == NULL)
1835
14792
			l = recv(context->ControlSocket, buffer+p, buffer_size, 0);
1836
		else
1837
			l = gnutls_record_recv(context->TLS_session, buffer+p, buffer_size);
1838
1839
14792
		if ( l <= 0 )
1840
			return 0;
1841
1842
14640
		buffer_size -= l;
1843
14640
		p += l;
1844
1845
14640
		if ( p >= 2 )
1846

14631
			if ( (buffer[p-2] == '\r') && (buffer[p-1] == '\n') )
1847
			{
1848
14358
				buffer[p-2] = 0;
1849
14358
				return 1;
1850
			}
1851
	}
1852
1853
	return 0;
1854
}
1855
1856
668
void *ftp_client_thread(SOCKET *s)
1857
668
{
1858
	FTPCONTEXT				ctx __attribute__ ((aligned (16)));
1859
668
	char					*cmd, *params, rcvbuf[FTP_PATH_MAX*2];
1860
	int						c, cmdno, rv;
1861
	size_t					i, cmdlen;
1862
	socklen_t				asz;
1863
	struct sockaddr_in		laddr;
1864
	pthread_mutexattr_t		m_attr;
1865
1866
	memset(&ctx, 0, sizeof(ctx));
1867
668
	ctx.Access = FTP_ACCESS_NOT_LOGGED_IN;
1868
668
	ctx.ControlSocket = *s;
1869
668
	ctx.GPBuffer = x_malloc(SIZE_OF_GPBUFFER);
1870
1871
	memset(&laddr, 0, sizeof(laddr));
1872
668
	asz = sizeof(laddr);
1873
668
	while ( getsockname(ctx.ControlSocket, (struct sockaddr *)&laddr, &asz) == 0 )
1874
	{
1875
668
		ctx.ServerIPv4 = laddr.sin_addr.s_addr;
1876
1877
		memset(&laddr, 0, sizeof(laddr));
1878
668
		asz = sizeof(laddr);
1879
668
		if ( getpeername(ctx.ControlSocket, (struct sockaddr *)&laddr, &asz) != 0 )
1880
			break;
1881
1882
668
		ctx.ClientIPv4 = laddr.sin_addr.s_addr;
1883
668
		ctx.Mode = MODE_NORMAL;
1884
668
		ctx.WorkerThreadAbort = 0;
1885
668
		ctx.WorkerThreadValid = -1;
1886
668
		ctx.SessionID = __sync_add_and_fetch(&g_newid, 1);
1887
668
		ctx.File = -1;
1888
668
		ctx.DataSocket = INVALID_SOCKET;
1889
1890
668
		pthread_mutexattr_init(&m_attr);
1891
#if defined(PTHREAD_MUTEX_RECURSIVE) || defined(__FreeBSD__)
1892
		pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
1893
#else
1894
668
        pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE_NP);
1895
#endif
1896
668
        pthread_mutex_init(&ctx.MTLock, &m_attr);
1897
1898
668
		ctx.CurrentDir[0] = '/';
1899
668
		sendstring(&ctx, success220);
1900
1901
		memset(&rcvbuf, 0, sizeof(rcvbuf));
1902
1903
668
		snprintf(rcvbuf, sizeof(rcvbuf), "<- New user IP=%u.%u.%u.%u:%u",
1904
				laddr.sin_addr.s_addr & 0xff,
1905
668
				(laddr.sin_addr.s_addr >> 8 ) & 0xff,
1906
668
				(laddr.sin_addr.s_addr >> 16 ) & 0xff,
1907
668
				(laddr.sin_addr.s_addr >> 24 ) & 0xff,
1908
668
				ntohs(laddr.sin_port)
1909
				);
1910
1911
668
		writelogentry(&ctx, rcvbuf, "");
1912
1913
14534
		while ( ctx.ControlSocket != INVALID_SOCKET ) {
1914
14534
			if ( !recvcmd(&ctx, rcvbuf, sizeof(rcvbuf)) )
1915
				break;
1916
1917
			i = 0;
1918

22234
			while ((rcvbuf[i] != 0) && (isalpha(rcvbuf[i]) == 0))
1919
7876
				++i;
1920
1921
14358
			cmd = &rcvbuf[i];
1922
104621
			while ((rcvbuf[i] != 0) && (rcvbuf[i] != ' '))
1923
90263
				++i;
1924
1925
14358
			cmdlen = &rcvbuf[i] - cmd;
1926
26427
			while (rcvbuf[i] == ' ')
1927
12069
				++i;
1928
1929
14358
			if (rcvbuf[i] == 0)
1930
				params = NULL;
1931
			else
1932
7919
				params = &rcvbuf[i];
1933
1934
			cmdno = -1;
1935
			rv = 1;
1936
253181
			for (c=0; c<MAX_CMDS; c++)
1937
249562
				if (strncasecmp(cmd, ftpcmds[c], cmdlen) == 0)
1938
				{
1939
					cmdno = c;
1940
10739
					rv = ftpprocs[c](&ctx, params);
1941
10738
					break;
1942
				}
1943
1944
14357
			if ( cmdno != FTP_PASSCMD_INDEX )
1945
12546
				writelogentry(&ctx, " @@ CMD: ", rcvbuf);
1946
			else
1947
1811
				writelogentry(&ctx, " @@ CMD: ", "PASS ***");
1948
1949
14357
			if ( cmdno == -1 )
1950
3619
				sendstring(&ctx, error500);
1951
1952
14357
			if ( rv <= 0 )
1953
				break;
1954
		};
1955
1956
667
		WorkerThreadCleanup(&ctx);
1957
1958
620
		pthread_mutex_destroy(&ctx.MTLock);
1959
620
		pthread_mutexattr_destroy(&m_attr);
1960
620
		writelogentry(&ctx, "User disconnected", "");
1961
620
		break;
1962
	}
1963
1964
620
	if (ctx.TLS_session != NULL)
1965
		gnutls_deinit(ctx.TLS_session);
1966
1967
620
	free(ctx.GPBuffer);
1968
620
	close(ctx.ControlSocket);
1969
620
	*s = INVALID_SOCKET;
1970
620
	return NULL;
1971
}
1972
1973
668
void *ftpmain(void *p)
1974
{
1975
	struct sockaddr_in	laddr;
1976
1977
	int		ftpsocket = INVALID_SOCKET,
1978
			clientsocket,
1979
			*scb = NULL,
1980
			socketret,
1981
			rv;
1982
1983
	socklen_t	asz;
1984
	uint32_t	i;
1985
	pthread_t	th;
1986
1987
668
	ftpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1988
668
	if ( ftpsocket == INVALID_SOCKET )
1989
	{
1990
		printf("\r\n socket create error\r\n");
1991
		return 0;
1992
	}
1993
1994
668
	rv = 1;
1995
668
	setsockopt(ftpsocket, SOL_SOCKET, SO_REUSEADDR, &rv, sizeof(rv));
1996
1997
668
	scb = (SOCKET *)x_malloc(sizeof(SOCKET)*g_cfg.MaxUsers);
1998
7348
	for (i = 0; i<g_cfg.MaxUsers; i++)
1999
6680
		scb[i] = INVALID_SOCKET;
2000
2001
	memset(&laddr, 0, sizeof(laddr));
2002
668
	laddr.sin_family = AF_INET;
2003
668
	laddr.sin_port = htons(g_cfg.Port);
2004
668
	laddr.sin_addr.s_addr = g_cfg.BindToInterface;
2005
668
	socketret = bind(ftpsocket, (struct sockaddr *)&laddr, sizeof(laddr));
2006
668
	if  ( socketret != 0 ) {
2007
		printf("\r\n Failed to start server. Can not bind to address\r\n\r\n");
2008
		free(scb);
2009
		close(ftpsocket);
2010
		return 0;
2011
	}
2012
2013
668
	writelogentry(NULL, success220, "");
2014
2015
668
	socketret = listen(ftpsocket, SOMAXCONN);
2016
668
	while ( socketret == 0 ) {
2017
		memset(&laddr, 0, sizeof(laddr));
2018
668
		asz = sizeof(laddr);
2019
668
		clientsocket = accept(ftpsocket, (struct sockaddr *)&laddr, &asz);
2020
668
		if (clientsocket != INVALID_SOCKET) {
2021
668
			rv = -1;
2022
668
			for (i=0; i<g_cfg.MaxUsers; i++) {
2023
668
				if ( scb[i] == INVALID_SOCKET ) {
2024
2025
668
					scb[i] = clientsocket;
2026
668
					rv = pthread_create(&th, NULL, (__ptr_thread_start_routine)&ftp_client_thread, &scb[i]);
2027
668
					if ( rv != 0 )
2028
						scb[i] = INVALID_SOCKET;
2029
2030
					break;
2031
				}
2032
			}
2033
2034
668
			if ( rv != 0 ) {
2035
				sendstring_plaintext(clientsocket, NOSLOTS);
2036
				close(clientsocket);
2037
			}
2038
		}
2039
    //Terminate the main thread when the child thread terminates
2040
668
		pthread_join(th, NULL);
2041
620
    break;
2042
	}
2043
2044
620
	free(scb);
2045
620
	close(ftpsocket);
2046
2047
620
	return NULL;
2048
}