| GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
| 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 |
if (*s != 0) |
||
40 |
{ |
||
41 |
/* |
||
42 |
* don't remove root directory sign as special case |
||
43 |
*/ |
||
44 |
if ((s[0] == '/') && (s[1] == 0)) |
||
45 |
return; |
||
46 |
|||
47 |
while (s[1] != 0) |
||
48 |
++s; |
||
49 |
|||
50 |
if (*s == '/') |
||
51 |
*s = 0; |
||
52 |
} |
||
53 |
} |
||
54 |
|||
55 |
void add_last_slash(char *s) |
||
56 |
{ |
||
57 |
✗✗✗✗ ✓✗✓✗ ✓✗✗✗ |
2965 |
if (*s != 0) |
58 |
{ |
||
59 |
✗✗✗✗ ✓✓✗✓ ✗✓✗✗ |
33185 |
while (s[1] != 0) |
60 |
30220 |
++s; |
|
61 |
|||
62 |
✗✗✗✗ ✓✗✗✓ ✗✓✗✗ |
2965 |
if (*s != '/') |
63 |
{ |
||
64 |
1511 |
s[1] = '/'; |
|
65 |
1511 |
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 |
if (*s == 0) |
||
79 |
return s; |
||
80 |
/* |
||
81 |
* leave root directory sign untouched |
||
82 |
*/ |
||
83 |
if (*s == '/') |
||
84 |
{ |
||
85 |
++s; |
||
86 |
++p; |
||
87 |
} |
||
88 |
|||
89 |
while (*s != 0) { |
||
90 |
if (*s == '/') |
||
91 |
p = s; |
||
92 |
++s; |
||
93 |
} |
||
94 |
|||
95 |
*p = 0; |
||
96 |
|||
97 |
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 |
1511 |
void format_path(char *input_path, char *filtered_path) |
|
105 |
{ |
||
106 |
char *p0, *pnext, *fp0; |
||
107 |
size_t sl; |
||
108 |
|||
109 |
✓✗ | 1511 |
if (*input_path == '/') |
110 |
{ |
||
111 |
1511 |
++input_path; |
|
112 |
1511 |
*filtered_path = '/'; |
|
113 |
1511 |
++filtered_path; |
|
114 |
} |
||
115 |
|||
116 |
p0 = input_path; |
||
117 |
pnext = input_path; |
||
118 |
fp0 = filtered_path; |
||
119 |
1511 |
*fp0 = 0; |
|
120 |
|||
121 |
while (1) |
||
122 |
{ |
||
123 |
✓✓ | 27370 |
while ((*pnext != '/') && (*pnext != 0)) |
124 |
16154 |
++pnext; |
|
125 |
|||
126 |
11216 |
sl = pnext - p0; |
|
127 |
|||
128 |
✓✓ | 11216 |
while (sl > 0) |
129 |
{ |
||
130 |
✓✓ | 1373 |
if (sl == 1) |
131 |
✓✓ | 271 |
if (*p0 == '.') |
132 |
break; |
||
133 |
|||
134 |
✓✓ | 1366 |
if (sl == 2) |
135 |
✓✓✗✓ |
74 |
if ((p0[0] == '.') && (p0[1] == '.')) |
136 |
{ |
||
137 |
delete_last_slash(filtered_path); |
||
138 |
fp0 = filepath(filtered_path); |
||
139 |
if (fp0 != filtered_path) |
||
140 |
{ |
||
141 |
*fp0 = '/'; |
||
142 |
++fp0; |
||
143 |
*fp0 = 0; |
||
144 |
} |
||
145 |
break; |
||
146 |
} |
||
147 |
|||
148 |
strncpy(fp0, p0, sl); |
||
149 |
1366 |
fp0 += sl; |
|
150 |
✓✓ | 1366 |
if (*pnext != 0) |
151 |
{ |
||
152 |
407 |
*fp0 = '/'; |
|
153 |
407 |
++fp0; |
|
154 |
} |
||
155 |
1366 |
*fp0 = 0; |
|
156 |
|||
157 |
1366 |
break; |
|
158 |
} |
||
159 |
|||
160 |
✓✓ | 11216 |
if (*pnext == 0) |
161 |
break; |
||
162 |
|||
163 |
9705 |
++pnext; |
|
164 |
p0 = pnext; |
||
165 |
} |
||
166 |
1511 |
} |
|
167 |
|||
168 |
1511 |
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 |
1511 |
total_len = strlen(root_dir)+strlen(current_dir); |
|
174 |
✓✓ | 1511 |
if (params != NULL) |
175 |
1002 |
total_len += strlen(params); |
|
176 |
|||
177 |
✓✗ | 1511 |
if (total_len >= SIZE_OF_GPBUFFER) |
178 |
return NULL; |
||
179 |
|||
180 |
1511 |
tmp = x_malloc(SIZE_OF_GPBUFFER); |
|
181 |
|||
182 |
strcpy(result_path, root_dir); |
||
183 |
add_last_slash(result_path); |
||
184 |
1511 |
user_root = result_path+strlen(result_path); |
|
185 |
|||
186 |
do { |
||
187 |
✓✓ | 1511 |
if ( params == NULL ) |
188 |
{ |
||
189 |
strcpy(tmp, current_dir); |
||
190 |
add_last_slash(tmp); |
||
191 |
break; |
||
192 |
} |
||
193 |
|||
194 |
✓✓ | 1002 |
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 |
1511 |
format_path(tmp, user_root); |
|
204 |
1511 |
free(tmp); |
|
205 |
1511 |
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 |
✓✓✗ | 541 |
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 |
✓✓✗ | 541 |
switch ( context->Mode ) { |
267 |
460 |
case MODE_NORMAL: |
|
268 |
460 |
clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
269 |
460 |
context->DataSocket = clientsocket; |
|
270 |
✓✗ | 460 |
if ( clientsocket == INVALID_SOCKET ) |
271 |
return INVALID_SOCKET; |
||
272 |
|||
273 |
460 |
laddr.sin_family = AF_INET; |
|
274 |
460 |
laddr.sin_port = context->DataPort; |
|
275 |
460 |
laddr.sin_addr.s_addr = context->DataIPv4; |
|
276 |
✓✗ | 460 |
if ( connect(clientsocket, (const struct sockaddr *)&laddr, sizeof(laddr)) == -1 ) { |
277 |
460 |
close(clientsocket); |
|
278 |
460 |
return INVALID_SOCKET; |
|
279 |
} |
||
280 |
break; |
||
281 |
|||
282 |
81 |
case MODE_PASSIVE: |
|
283 |
81 |
asz = sizeof(laddr); |
|
284 |
81 |
clientsocket = accept(context->DataSocket, (struct sockaddr *)&laddr, &asz); |
|
285 |
10 |
close(context->DataSocket); |
|
286 |
10 |
context->DataSocket = clientsocket; |
|
287 |
|||
288 |
✗✓ | 10 |
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 |
15834 |
ssize_t sendstring(PFTPCONTEXT context, const char *Buffer) |
|
303 |
{ |
||
304 |
15834 |
size_t l = strlen(Buffer); |
|
305 |
|||
306 |
✓✗ | 15834 |
if (context->TLS_session == NULL) |
307 |
15834 |
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 |
21098 |
ssize_t writeconsolestr(const char *Buffer) |
|
339 |
{ |
||
340 |
21098 |
size_t l = strlen(Buffer); |
|
341 |
|||
342 |
✓✗ | 21098 |
if ( g_log != -1 ) |
343 |
21098 |
write(g_log, Buffer, l); |
|
344 |
|||
345 |
21098 |
return write(STDOUT_FILENO, Buffer, l); |
|
346 |
} |
||
347 |
|||
348 |
21098 |
int writelogentry(PFTPCONTEXT context, const char *logtext1, const char *logtext2) |
|
349 |
21098 |
{ |
|
350 |
21098 |
char text[SIZE_OF_GPBUFFER]; |
|
351 |
21098 |
time_t itm = time(NULL); |
|
352 |
struct tm ltm; |
||
353 |
|||
354 |
21098 |
localtime_r(&itm, <m); |
|
355 |
|||
356 |
✓✓ | 21098 |
if (context == NULL) |
357 |
{ |
||
358 |
664 |
snprintf(text, sizeof(text), "%02u-%02u-%u %02u:%02u:%02u : %s%s\r\n", |
|
359 |
664 |
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 |
20434 |
snprintf(text, sizeof(text), "%02u-%02u-%u %02u:%02u:%02u S-id=%u : %s%s\r\n", |
|
365 |
20434 |
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 |
21098 |
return writeconsolestr(text); |
|
371 |
} |
||
372 |
|||
373 |
778 |
void WorkerThreadCleanup(PFTPCONTEXT context) |
|
374 |
{ |
||
375 |
int err; |
||
376 |
778 |
void *retv = NULL; |
|
377 |
|||
378 |
✓✓ | 778 |
if ( context->WorkerThreadValid == 0 ) { |
379 |
|||
380 |
/* |
||
381 |
* trying to stop gracefully |
||
382 |
*/ |
||
383 |
71 |
context->WorkerThreadAbort = 1; |
|
384 |
71 |
sleep(2); |
|
385 |
|||
386 |
71 |
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 |
✓✓ | 707 |
if ( context->DataSocket != INVALID_SOCKET ) { |
397 |
381 |
close(context->DataSocket); |
|
398 |
381 |
context->DataSocket = INVALID_SOCKET; |
|
399 |
} |
||
400 |
|||
401 |
✗✓ | 707 |
if ( context->File != -1 ) { |
402 |
close(context->File); |
||
403 |
context->File = -1; |
||
404 |
} |
||
405 |
|||
406 |
707 |
context->DataIPv4 = 0; |
|
407 |
707 |
context->DataPort = 0; |
|
408 |
//writelogentry(context, "WorkerThreadCleanup complete", ""); |
||
409 |
707 |
} |
|
410 |
|||
411 |
1403 |
int ftpUSER(PFTPCONTEXT context, const char *params) |
|
412 |
{ |
||
413 |
✓✓ | 1403 |
if ( params == NULL ) |
414 |
620 |
return sendstring(context, error501); |
|
415 |
|||
416 |
783 |
context->Access = FTP_ACCESS_NOT_LOGGED_IN; |
|
417 |
|||
418 |
/* |
||
419 |
* Save username in GPBuffer for next PASS command |
||
420 |
*/ |
||
421 |
|||
422 |
783 |
writelogentry(context, " USER: ", (char *)params); |
|
423 |
|||
424 |
783 |
strcpy(context->GPBuffer, interm331); |
|
425 |
783 |
strcat(context->GPBuffer, params); |
|
426 |
783 |
strcat(context->GPBuffer, interm331_tail); |
|
427 |
783 |
sendstring(context, context->GPBuffer); |
|
428 |
|||
429 |
783 |
strcpy(context->GPBuffer, params); |
|
430 |
783 |
return 1; |
|
431 |
} |
||
432 |
|||
433 |
429 |
int ftpQUIT(PFTPCONTEXT context, const char *params) |
|
434 |
{ |
||
435 |
429 |
writelogentry(context, " QUIT", ""); |
|
436 |
429 |
sendstring(context, success221); |
|
437 |
/* |
||
438 |
* retrun 0 to break command processing loop |
||
439 |
*/ |
||
440 |
429 |
return 0; |
|
441 |
} |
||
442 |
|||
443 |
28 |
int ftpNOOP(PFTPCONTEXT context, const char *params) |
|
444 |
{ |
||
445 |
28 |
return sendstring(context, success200); |
|
446 |
} |
||
447 |
|||
448 |
779 |
int ftpPWD(PFTPCONTEXT context, const char *params) |
|
449 |
{ |
||
450 |
✓✓ | 779 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
451 |
159 |
return sendstring(context, error530); |
|
452 |
|||
453 |
620 |
strcpy(context->GPBuffer, "257 \""); |
|
454 |
620 |
strcat(context->GPBuffer, context->CurrentDir); |
|
455 |
620 |
strcat(context->GPBuffer, "\" is a current directory.\r\n"); |
|
456 |
620 |
return sendstring(context, context->GPBuffer); |
|
457 |
} |
||
458 |
|||
459 |
114 |
int ftpTYPE(PFTPCONTEXT context, const char *params) |
|
460 |
{ |
||
461 |
✓✓ | 114 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
462 |
50 |
return sendstring(context, error530); |
|
463 |
|||
464 |
✓✓ | 64 |
if (params == NULL) |
465 |
13 |
return sendstring(context, error501); |
|
466 |
|||
467 |
✓✓✓ | 51 |
switch (*params) |
468 |
{ |
||
469 |
35 |
case 'A': |
|
470 |
case 'a': |
||
471 |
35 |
return sendstring(context, success200_1); |
|
472 |
1 |
case 'I': |
|
473 |
case 'i': |
||
474 |
1 |
return sendstring(context, success200_2); |
|
475 |
} |
||
476 |
|||
477 |
15 |
return sendstring(context, error501); |
|
478 |
} |
||
479 |
|||
480 |
1130 |
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 |
✓✓ | 1130 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
487 |
266 |
return sendstring(context, error530); |
|
488 |
|||
489 |
✓✓ | 864 |
if ( params == NULL ) |
490 |
38 |
return sendstring(context, error501); |
|
491 |
|||
492 |
✓✓ | 3712 |
for (c = 0; c < 4; c++) { |
493 |
3060 |
DataIP += ((unsigned char)strtoul(p, NULL, 10)) << c*8; |
|
494 |
✓✓ | 7920 |
while ( (*p >= '0') && (*p <= '9') ) |
495 |
4860 |
p++; |
|
496 |
✓✓ | 3060 |
if ( *p == 0 ) |
497 |
break; |
||
498 |
2886 |
p++; |
|
499 |
} |
||
500 |
|||
501 |
✓✓ | 1806 |
for (c = 0; c < 2; c++) { |
502 |
1468 |
DataPort += ((unsigned char)strtoul(p, NULL, 10)) << c*8; |
|
503 |
✓✓ | 4479 |
while ( (*p >= '0') && (*p <= '9') ) |
504 |
3011 |
p++; |
|
505 |
✓✓ | 1468 |
if ( *p == 0 ) |
506 |
break; |
||
507 |
980 |
p++; |
|
508 |
} |
||
509 |
|||
510 |
✓✓ | 826 |
if ( DataIP != context->ClientIPv4 ) |
511 |
414 |
return sendstring(context, error501); |
|
512 |
|||
513 |
412 |
context->DataIPv4 = DataIP; |
|
514 |
412 |
context->DataPort = DataPort; |
|
515 |
412 |
context->Mode = MODE_NORMAL; |
|
516 |
|||
517 |
412 |
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 |
467 |
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 |
467 |
pthread_mutex_lock(&context->MTLock); |
|
658 |
✗✓ | 467 |
pthread_cleanup_push(cleanup_handler, context); |
659 |
ret = 0; |
||
660 |
467 |
TLS_datasession = NULL; |
|
661 |
|||
662 |
467 |
clientsocket = create_datasocket(context); |
|
663 |
✗✓ | 402 |
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 |
✗✓ | 402 |
if (TLS_datasession != NULL) |
683 |
{ |
||
684 |
gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR); |
||
685 |
gnutls_deinit(TLS_datasession); |
||
686 |
} |
||
687 |
|||
688 |
402 |
writelogentry(context, " LIST complete", ""); |
|
689 |
|||
690 |
✓✗ | 402 |
if (clientsocket == INVALID_SOCKET) { |
691 |
402 |
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 |
402 |
context->WorkerThreadValid = -1; |
|
704 |
402 |
pthread_cleanup_pop(0); |
|
705 |
402 |
pthread_mutex_unlock(&context->MTLock); |
|
706 |
402 |
return NULL; |
|
707 |
} |
||
708 |
|||
709 |
859 |
int ftpLIST(PFTPCONTEXT context, const char *params) |
|
710 |
{ |
||
711 |
struct stat filestats; |
||
712 |
pthread_t tid; |
||
713 |
|||
714 |
✓✓ | 859 |
if (context->Access == FTP_ACCESS_NOT_LOGGED_IN) |
715 |
198 |
return sendstring(context, error530); |
|
716 |
✓✓ | 661 |
if (context->WorkerThreadValid == 0) |
717 |
58 |
return sendstring(context, error550_t); |
|
718 |
|||
719 |
✓✓ | 603 |
if (params != NULL) |
720 |
{ |
||
721 |
✓✗✗✓ |
136 |
if ((strcmp(params, "-a") == 0) || (strcmp(params, "-l") == 0)) |
722 |
params = NULL; |
||
723 |
} |
||
724 |
|||
725 |
✓✗ | 603 |
if (finalpath( |
726 |
603 |
context->RootDir, |
|
727 |
603 |
context->CurrentDir, |
|
728 |
(char *)params, context->GPBuffer) == NULL) |
||
729 |
return 0; |
||
730 |
|||
731 |
✓✓ | 603 |
while (stat(context->GPBuffer, &filestats) == 0) |
732 |
{ |
||
733 |
✓✗ | 467 |
if ( !S_ISDIR(filestats.st_mode) ) |
734 |
break; |
||
735 |
|||
736 |
467 |
sendstring(context, interm150); |
|
737 |
467 |
writelogentry(context, " LIST", (char *)params); |
|
738 |
467 |
context->WorkerThreadAbort = 0; |
|
739 |
|||
740 |
467 |
pthread_mutex_lock(&context->MTLock); |
|
741 |
|||
742 |
467 |
context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&list_thread, context); |
|
743 |
✓✗ | 467 |
if ( context->WorkerThreadValid == 0 ) |
744 |
467 |
context->WorkerThreadId = tid; |
|
745 |
else |
||
746 |
sendstring(context, error451); |
||
747 |
|||
748 |
467 |
pthread_mutex_unlock(&context->MTLock); |
|
749 |
|||
750 |
467 |
return 1; |
|
751 |
} |
||
752 |
|||
753 |
136 |
return sendstring(context, error550); |
|
754 |
} |
||
755 |
|||
756 |
203 |
int ftpCDUP(PFTPCONTEXT context, const char *params) |
|
757 |
{ |
||
758 |
✓✓ | 203 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
759 |
40 |
return sendstring(context, error530); |
|
760 |
|||
761 |
✓✗ | 163 |
if ( strcmp(context->CurrentDir, "/") == 0 ) |
762 |
163 |
return sendstring(context, success250); |
|
763 |
|||
764 |
delete_last_slash(context->CurrentDir); |
||
765 |
filepath(context->CurrentDir); |
||
766 |
|||
767 |
writelogentry(context, " CDUP", ""); |
||
768 |
return sendstring(context, success250); |
||
769 |
} |
||
770 |
|||
771 |
22 |
int ftpCWD(PFTPCONTEXT context, const char *params) |
|
772 |
{ |
||
773 |
struct stat filestats; |
||
774 |
size_t rl; |
||
775 |
|||
776 |
✓✓ | 22 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
777 |
3 |
return sendstring(context, error530); |
|
778 |
|||
779 |
✓✓ | 19 |
if ( params == NULL ) |
780 |
7 |
return sendstring(context, error501); |
|
781 |
|||
782 |
✓✗ | 12 |
if (finalpath( |
783 |
12 |
context->RootDir, |
|
784 |
12 |
context->CurrentDir, |
|
785 |
(char *)params, context->GPBuffer) == NULL) |
||
786 |
return 0; |
||
787 |
|||
788 |
✗✓ | 12 |
if ( stat(context->GPBuffer, &filestats) == 0 ) |
789 |
if ( S_ISDIR(filestats.st_mode) ) |
||
790 |
{ |
||
791 |
rl = strlen(context->RootDir); |
||
792 |
strcpy(context->CurrentDir, context->GPBuffer+rl); |
||
793 |
writelogentry(context, " CWD: ", context->CurrentDir); |
||
794 |
return sendstring(context, success250); |
||
795 |
} |
||
796 |
|||
797 |
12 |
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 |
59 |
int ftpRETR(PFTPCONTEXT context, const char *params) |
|
912 |
{ |
||
913 |
struct stat filestats; |
||
914 |
pthread_t tid; |
||
915 |
|||
916 |
✓✓ | 59 |
if (context->Access == FTP_ACCESS_NOT_LOGGED_IN) |
917 |
11 |
return sendstring(context, error530); |
|
918 |
✓✓ | 48 |
if (context->WorkerThreadValid == 0) |
919 |
14 |
return sendstring(context, error550_t); |
|
920 |
✓✓ | 34 |
if ( params == NULL ) |
921 |
17 |
return sendstring(context, error501); |
|
922 |
|||
923 |
✗✓ | 17 |
if ( context->File != -1 ) { |
924 |
close(context->File); |
||
925 |
context->File = -1; |
||
926 |
} |
||
927 |
|||
928 |
✓✗ | 17 |
if (finalpath( |
929 |
17 |
context->RootDir, |
|
930 |
17 |
context->CurrentDir, |
|
931 |
(char *)params, context->GPBuffer) == NULL) |
||
932 |
return 0; |
||
933 |
|||
934 |
✓✓ | 17 |
while (stat(context->GPBuffer, &filestats) == 0) |
935 |
{ |
||
936 |
✗✓ | 1 |
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 |
17 |
return sendstring(context, error550); |
|
957 |
} |
||
958 |
|||
959 |
159 |
int ftpABOR(PFTPCONTEXT context, const char *params) |
|
960 |
{ |
||
961 |
✓✓ | 159 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
962 |
42 |
return sendstring(context, error530); |
|
963 |
|||
964 |
117 |
writelogentry(context, " ABORT command", NULL); |
|
965 |
117 |
WorkerThreadCleanup(context); |
|
966 |
114 |
return sendstring(context, success226); |
|
967 |
} |
||
968 |
|||
969 |
121 |
int ftpDELE(PFTPCONTEXT context, const char *params) |
|
970 |
{ |
||
971 |
✓✓ | 121 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
972 |
21 |
return sendstring(context, error530); |
|
973 |
✓✓ | 100 |
if ( context->Access < FTP_ACCESS_FULL ) |
974 |
49 |
return sendstring(context, error550_r); |
|
975 |
✓✓ | 51 |
if ( params == NULL ) |
976 |
17 |
return sendstring(context, error501); |
|
977 |
|||
978 |
✓✗ | 34 |
if (finalpath( |
979 |
34 |
context->RootDir, |
|
980 |
34 |
context->CurrentDir, |
|
981 |
(char *)params, context->GPBuffer) == NULL) |
||
982 |
return 0; |
||
983 |
|||
984 |
✗✓ | 34 |
if ( unlink(context->GPBuffer) == 0 ) { |
985 |
sendstring(context, success250); |
||
986 |
writelogentry(context, " DELE: ", (char *)params); |
||
987 |
} |
||
988 |
else |
||
989 |
34 |
sendstring(context, error550_r); |
|
990 |
|||
991 |
return 1; |
||
992 |
} |
||
993 |
|||
994 |
582 |
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 |
✓✓ | 582 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1005 |
{ |
||
1006 |
93 |
sendstring(context, error530); |
|
1007 |
93 |
break; |
|
1008 |
} |
||
1009 |
|||
1010 |
✓✓ | 489 |
if ( context->WorkerThreadValid == 0 ) |
1011 |
{ |
||
1012 |
47 |
sendstring(context, error550_t); |
|
1013 |
47 |
break; |
|
1014 |
} |
||
1015 |
|||
1016 |
✓✓ | 442 |
if ( context->DataSocket != INVALID_SOCKET ) |
1017 |
251 |
close(context->DataSocket); |
|
1018 |
|||
1019 |
442 |
context->DataSocket = INVALID_SOCKET; |
|
1020 |
|||
1021 |
442 |
datasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
1022 |
✗✓ | 442 |
if (datasocket == INVALID_SOCKET) |
1023 |
{ |
||
1024 |
sendstring(context, error451); |
||
1025 |
break; |
||
1026 |
} |
||
1027 |
|||
1028 |
✓✗ | 442 |
for (c = g_cfg.PasvPortBase; c <= g_cfg.PasvPortMax; c++) { |
1029 |
442 |
clock_gettime(CLOCK_REALTIME, &rtctime); |
|
1030 |
memset(&laddr, 0, sizeof(laddr)); |
||
1031 |
442 |
laddr.sin_family = AF_INET; |
|
1032 |
442 |
laddr.sin_port = htons((in_port_t)(g_cfg.PasvPortBase + |
|
1033 |
(rtctime.tv_nsec % (g_cfg.PasvPortMax-g_cfg.PasvPortBase)))); |
||
1034 |
442 |
laddr.sin_addr.s_addr = context->ServerIPv4; |
|
1035 |
442 |
socketret = bind(datasocket, (struct sockaddr *)&laddr, sizeof(laddr)); |
|
1036 |
✗✓ | 442 |
if ( socketret == 0 ) |
1037 |
break; |
||
1038 |
} |
||
1039 |
|||
1040 |
✗✓ | 442 |
if ( socketret != 0 ) { |
1041 |
close(datasocket); |
||
1042 |
sendstring(context, error451); |
||
1043 |
break; |
||
1044 |
} |
||
1045 |
|||
1046 |
442 |
socketret = listen(datasocket, SOMAXCONN); |
|
1047 |
✗✓ | 442 |
if (socketret != 0) { |
1048 |
close(datasocket); |
||
1049 |
sendstring(context, error451); |
||
1050 |
break; |
||
1051 |
} |
||
1052 |
|||
1053 |
✓✗ | 442 |
if ((context->ClientIPv4 & g_cfg.LocalIPMask) == (context->ServerIPv4 & g_cfg.LocalIPMask)) |
1054 |
{ |
||
1055 |
442 |
context->DataIPv4 = context->ServerIPv4; |
|
1056 |
442 |
writelogentry(context, " local client.", ""); |
|
1057 |
} else { |
||
1058 |
context->DataIPv4 = g_cfg.ExternalInterface; |
||
1059 |
writelogentry(context, " nonlocal client.", ""); |
||
1060 |
} |
||
1061 |
|||
1062 |
442 |
context->DataPort = laddr.sin_port; |
|
1063 |
442 |
context->DataSocket = datasocket; |
|
1064 |
442 |
context->Mode = MODE_PASSIVE; |
|
1065 |
|||
1066 |
result = 1; |
||
1067 |
442 |
break; |
|
1068 |
} |
||
1069 |
|||
1070 |
582 |
return result; |
|
1071 |
} |
||
1072 |
|||
1073 |
280 |
int ftpEPSV (PFTPCONTEXT context, const char *params) |
|
1074 |
{ |
||
1075 |
✓✓ | 280 |
if (pasv(context) == 0) |
1076 |
return 1; |
||
1077 |
|||
1078 |
202 |
snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, success229, |
|
1079 |
202 |
ntohs(context->DataPort)); |
|
1080 |
|||
1081 |
202 |
writelogentry(context, " entering extended passive mode", ""); |
|
1082 |
|||
1083 |
202 |
return sendstring(context, context->GPBuffer); |
|
1084 |
} |
||
1085 |
|||
1086 |
302 |
int ftpPASV(PFTPCONTEXT context, const char *params) |
|
1087 |
{ |
||
1088 |
✓✓ | 302 |
if (pasv(context) == 0) |
1089 |
return 1; |
||
1090 |
|||
1091 |
240 |
snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, success227, |
|
1092 |
context->DataIPv4 & 0xff, |
||
1093 |
240 |
(context->DataIPv4 >> 8) & 0xff, |
|
1094 |
240 |
(context->DataIPv4 >> 16) & 0xff, |
|
1095 |
240 |
(context->DataIPv4 >> 24) & 0xff, |
|
1096 |
context->DataPort & 0xff, |
||
1097 |
240 |
(context->DataPort >> 8) & 0xff); |
|
1098 |
|||
1099 |
240 |
writelogentry(context, " entering passive mode", ""); |
|
1100 |
|||
1101 |
240 |
return sendstring(context, context->GPBuffer); |
|
1102 |
} |
||
1103 |
|||
1104 |
1621 |
int ftpPASS(PFTPCONTEXT context, const char *params) |
|
1105 |
{ |
||
1106 |
char temptext[256]; |
||
1107 |
|||
1108 |
✓✓ | 1621 |
if ( params == NULL ) |
1109 |
14 |
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 |
✓✓ | 1607 |
if (!ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "pswd", temptext, sizeof(temptext))) |
1117 |
609 |
return sendstring(context, error530_r); |
|
1118 |
|||
1119 |
✓✓✓✓ |
998 |
if ( (strcmp(temptext, params) == 0) || (temptext[0] == '*') ) |
1120 |
{ |
||
1121 |
808 |
memset(context->RootDir, 0, sizeof(context->RootDir)); |
|
1122 |
memset(temptext, 0, sizeof(temptext)); |
||
1123 |
|||
1124 |
808 |
ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "root", context->RootDir, sizeof(context->RootDir)); |
|
1125 |
808 |
ParseConfig(g_cfg.ConfigFile, context->GPBuffer, "accs", temptext, sizeof(temptext)); |
|
1126 |
|||
1127 |
808 |
context->Access = FTP_ACCESS_NOT_LOGGED_IN; |
|
1128 |
do { |
||
1129 |
|||
1130 |
✓✓ | 808 |
if ( strcasecmp(temptext, "admin") == 0 ) { |
1131 |
627 |
context->Access = FTP_ACCESS_FULL; |
|
1132 |
627 |
break; |
|
1133 |
} |
||
1134 |
|||
1135 |
✓✓ | 181 |
if ( strcasecmp(temptext, "upload") == 0 ) { |
1136 |
61 |
context->Access = FTP_ACCESS_CREATENEW; |
|
1137 |
61 |
break; |
|
1138 |
} |
||
1139 |
|||
1140 |
✓✗ | 120 |
if ( strcasecmp(temptext, "readonly") == 0 ) { |
1141 |
120 |
context->Access = FTP_ACCESS_READONLY; |
|
1142 |
120 |
break; |
|
1143 |
} |
||
1144 |
|||
1145 |
return sendstring(context, error530_b); |
||
1146 |
} while (0); |
||
1147 |
|||
1148 |
808 |
writelogentry(context, " PASS->successful logon", ""); |
|
1149 |
} |
||
1150 |
else |
||
1151 |
190 |
return sendstring(context, error530_r); |
|
1152 |
|||
1153 |
808 |
return sendstring(context, success230); |
|
1154 |
} |
||
1155 |
|||
1156 |
114 |
int ftpREST(PFTPCONTEXT context, const char *params) |
|
1157 |
{ |
||
1158 |
✓✓ | 114 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1159 |
19 |
return sendstring(context, error530); |
|
1160 |
|||
1161 |
✓✓ | 95 |
if ( params == NULL ) |
1162 |
31 |
return sendstring(context, error501); |
|
1163 |
|||
1164 |
64 |
context->RestPoint = strtoull(params, NULL, 10); |
|
1165 |
64 |
snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, "%s %llu\r\n", |
|
1166 |
interm350, (unsigned long long int)context->RestPoint); |
||
1167 |
|||
1168 |
64 |
return sendstring(context, context->GPBuffer); |
|
1169 |
} |
||
1170 |
|||
1171 |
100 |
int ftpSIZE(PFTPCONTEXT context, const char *params) |
|
1172 |
{ |
||
1173 |
struct stat filestats; |
||
1174 |
|||
1175 |
✓✓ | 100 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1176 |
24 |
return sendstring(context, error530); |
|
1177 |
✓✓ | 76 |
if ( params == NULL ) |
1178 |
29 |
return sendstring(context, error501); |
|
1179 |
|||
1180 |
✓✗ | 47 |
if (finalpath( |
1181 |
47 |
context->RootDir, |
|
1182 |
47 |
context->CurrentDir, |
|
1183 |
(char *)params, context->GPBuffer) == NULL) |
||
1184 |
return 0; |
||
1185 |
|||
1186 |
✓✓ | 47 |
if ( stat(context->GPBuffer, &filestats) == 0 ) |
1187 |
{ |
||
1188 |
1 |
snprintf(context->GPBuffer, SIZE_OF_GPBUFFER, "213 %llu\r\n", |
|
1189 |
1 |
(unsigned long long int)filestats.st_size); |
|
1190 |
1 |
sendstring(context, context->GPBuffer); |
|
1191 |
} |
||
1192 |
else |
||
1193 |
46 |
sendstring(context, error550); |
|
1194 |
|||
1195 |
return 1; |
||
1196 |
} |
||
1197 |
|||
1198 |
699 |
int ftpMKD(PFTPCONTEXT context, const char *params) |
|
1199 |
{ |
||
1200 |
✓✓ | 699 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1201 |
163 |
return sendstring(context, error530); |
|
1202 |
✓✓ | 536 |
if ( context->Access < FTP_ACCESS_CREATENEW ) |
1203 |
29 |
return sendstring(context, error550_r); |
|
1204 |
✓✓ | 507 |
if ( params == NULL ) |
1205 |
32 |
return sendstring(context, error501); |
|
1206 |
|||
1207 |
✓✗ | 475 |
if (finalpath( |
1208 |
475 |
context->RootDir, |
|
1209 |
475 |
context->CurrentDir, |
|
1210 |
(char *)params, context->GPBuffer) == NULL) |
||
1211 |
return 0; |
||
1212 |
|||
1213 |
✓✓ | 475 |
if ( mkdir(context->GPBuffer, 0755) == 0 ) { |
1214 |
399 |
sendstring(context, success257); |
|
1215 |
399 |
writelogentry(context, " MKD: ", (char *)params); |
|
1216 |
} |
||
1217 |
else |
||
1218 |
76 |
sendstring(context, error550_r); |
|
1219 |
|||
1220 |
return 1; |
||
1221 |
} |
||
1222 |
|||
1223 |
33 |
int ftpRMD(PFTPCONTEXT context, const char *params) |
|
1224 |
{ |
||
1225 |
✓✓ | 33 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1226 |
10 |
return sendstring(context, error530); |
|
1227 |
✓✓ | 23 |
if ( context->Access < FTP_ACCESS_FULL ) |
1228 |
6 |
return sendstring(context, error550_r); |
|
1229 |
✓✓ | 17 |
if ( params == NULL ) |
1230 |
14 |
return sendstring(context, error501); |
|
1231 |
|||
1232 |
✓✗ | 3 |
if (finalpath( |
1233 |
3 |
context->RootDir, |
|
1234 |
3 |
context->CurrentDir, |
|
1235 |
(char *)params, context->GPBuffer) == NULL) |
||
1236 |
return 0; |
||
1237 |
|||
1238 |
✗✓ | 3 |
if ( rmdir(context->GPBuffer) == 0 ) { |
1239 |
sendstring(context, success250); |
||
1240 |
writelogentry(context, " DELE: ", (char *)params); |
||
1241 |
} |
||
1242 |
else |
||
1243 |
3 |
sendstring(context, error550_r); |
|
1244 |
|||
1245 |
return 1; |
||
1246 |
} |
||
1247 |
|||
1248 |
32 |
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 |
32 |
pthread_mutex_lock(&context->MTLock); |
|
1259 |
✗✓ | 32 |
pthread_cleanup_push(cleanup_handler, context); |
1260 |
|||
1261 |
f = -1; |
||
1262 |
sz_total = 0; |
||
1263 |
buffer = NULL; |
||
1264 |
32 |
TLS_datasession = NULL; |
|
1265 |
32 |
clock_gettime(CLOCK_MONOTONIC, &t); |
|
1266 |
32 |
lt0 = t.tv_sec*1e9 + t.tv_nsec; |
|
1267 |
|||
1268 |
32 |
clientsocket = create_datasocket(context); |
|
1269 |
✗✓ | 26 |
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 |
26 |
clock_gettime(CLOCK_MONOTONIC, &t); |
|
1298 |
26 |
lt1 = t.tv_sec*1e9 + t.tv_nsec; |
|
1299 |
|||
1300 |
✗✓ | 26 |
if (f != -1) |
1301 |
close(f); |
||
1302 |
|||
1303 |
26 |
context->File = -1; |
|
1304 |
|||
1305 |
✗✓ | 26 |
if (TLS_datasession != NULL) |
1306 |
{ |
||
1307 |
gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR); |
||
1308 |
gnutls_deinit(TLS_datasession); |
||
1309 |
} |
||
1310 |
|||
1311 |
/* calculating performance */ |
||
1312 |
✗✓ | 26 |
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 |
✓✗ | 26 |
if (clientsocket == INVALID_SOCKET) { |
1322 |
26 |
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 |
26 |
context->WorkerThreadValid = -1; |
|
1335 |
26 |
pthread_cleanup_pop(0); |
|
1336 |
26 |
pthread_mutex_unlock(&context->MTLock); |
|
1337 |
26 |
return NULL; |
|
1338 |
} |
||
1339 |
|||
1340 |
88 |
int ftpSTOR(PFTPCONTEXT context, const char *params) |
|
1341 |
{ |
||
1342 |
struct stat filestats; |
||
1343 |
pthread_t tid; |
||
1344 |
|||
1345 |
✓✓ | 88 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1346 |
9 |
return sendstring(context, error530); |
|
1347 |
✓✓ | 79 |
if ( context->Access < FTP_ACCESS_CREATENEW ) |
1348 |
6 |
return sendstring(context, error550_r); |
|
1349 |
✓✓ | 73 |
if ( params == NULL ) |
1350 |
31 |
return sendstring(context, error501); |
|
1351 |
✓✓ | 42 |
if ( context->WorkerThreadValid == 0 ) |
1352 |
10 |
return sendstring(context, error550_t); |
|
1353 |
|||
1354 |
✗✓ | 32 |
if ( context->File != -1 ) { |
1355 |
close(context->File); |
||
1356 |
context->File = -1; |
||
1357 |
} |
||
1358 |
|||
1359 |
✓✗ | 32 |
if (finalpath( |
1360 |
32 |
context->RootDir, |
|
1361 |
32 |
context->CurrentDir, |
|
1362 |
(char *)params, context->GPBuffer) == NULL) |
||
1363 |
return 0; |
||
1364 |
|||
1365 |
✓✓ | 32 |
if ( context->Access == FTP_ACCESS_FULL ) |
1366 |
22 |
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 |
32 |
sendstring(context, interm150); |
|
1375 |
32 |
writelogentry(context, " STOR: ", (char *)params); |
|
1376 |
32 |
context->WorkerThreadAbort = 0; |
|
1377 |
|||
1378 |
32 |
pthread_mutex_lock(&context->MTLock); |
|
1379 |
|||
1380 |
32 |
context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&stor_thread, context); |
|
1381 |
✓✗ | 32 |
if ( context->WorkerThreadValid == 0 ) |
1382 |
32 |
context->WorkerThreadId = tid; |
|
1383 |
else |
||
1384 |
sendstring(context, error451); |
||
1385 |
|||
1386 |
32 |
pthread_mutex_unlock(&context->MTLock); |
|
1387 |
|||
1388 |
32 |
return 1; |
|
1389 |
} |
||
1390 |
|||
1391 |
712 |
int ftpSYST(PFTPCONTEXT context, const char *params) |
|
1392 |
{ |
||
1393 |
712 |
return sendstring(context, success215); |
|
1394 |
} |
||
1395 |
|||
1396 |
35 |
int ftpHELP(PFTPCONTEXT context, const char *params) |
|
1397 |
{ |
||
1398 |
35 |
return sendstring(context, success214); |
|
1399 |
} |
||
1400 |
|||
1401 |
60 |
int ftpSITE(PFTPCONTEXT context, const char *params) |
|
1402 |
{ |
||
1403 |
✓✓ | 60 |
if ( params != NULL ) |
1404 |
✗✓ | 23 |
if (strcasecmp(params, "help") == 0) |
1405 |
return sendstring(context, "200 chmod\r\n"); |
||
1406 |
|||
1407 |
60 |
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 |
146 |
int ftpAPPE(PFTPCONTEXT context, const char *params) |
|
1491 |
{ |
||
1492 |
struct stat filestats; |
||
1493 |
pthread_t tid; |
||
1494 |
|||
1495 |
✓✓ | 146 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1496 |
23 |
return sendstring(context, error530); |
|
1497 |
✓✓ | 123 |
if ( context->Access < FTP_ACCESS_FULL ) |
1498 |
39 |
return sendstring(context, error550_r); |
|
1499 |
✓✓ | 84 |
if ( params == NULL ) |
1500 |
8 |
return sendstring(context, error501); |
|
1501 |
✓✓ | 76 |
if ( context->WorkerThreadValid == 0 ) |
1502 |
28 |
return sendstring(context, error550_t); |
|
1503 |
|||
1504 |
✗✓ | 48 |
if ( context->File != -1 ) { |
1505 |
close(context->File); |
||
1506 |
context->File = -1; |
||
1507 |
} |
||
1508 |
|||
1509 |
✓✗ | 48 |
if (finalpath( |
1510 |
48 |
context->RootDir, |
|
1511 |
48 |
context->CurrentDir, |
|
1512 |
(char *)params, context->GPBuffer) == NULL) |
||
1513 |
return 0; |
||
1514 |
|||
1515 |
/* |
||
1516 |
* stat must NOT fail |
||
1517 |
*/ |
||
1518 |
✓✓ | 48 |
while (stat(context->GPBuffer, &filestats) == 0) |
1519 |
{ |
||
1520 |
/* |
||
1521 |
* do not try to "append" for directories |
||
1522 |
*/ |
||
1523 |
✗✓ | 1 |
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 |
48 |
return sendstring(context, error550); |
|
1544 |
} |
||
1545 |
|||
1546 |
88 |
int ftpRNFR(PFTPCONTEXT context, const char *params) |
|
1547 |
{ |
||
1548 |
struct stat filestats; |
||
1549 |
|||
1550 |
✓✓ | 88 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1551 |
12 |
return sendstring(context, error530); |
|
1552 |
✓✓ | 76 |
if ( context->Access < FTP_ACCESS_FULL ) |
1553 |
8 |
return sendstring(context, error550_r); |
|
1554 |
✓✓ | 68 |
if ( params == NULL ) |
1555 |
26 |
return sendstring(context, error501); |
|
1556 |
|||
1557 |
✓✗ | 42 |
if (finalpath( |
1558 |
42 |
context->RootDir, |
|
1559 |
42 |
context->CurrentDir, |
|
1560 |
(char *)params, context->GPBuffer) == NULL) |
||
1561 |
return 0; |
||
1562 |
|||
1563 |
✗✓ | 42 |
if ( stat(context->GPBuffer, &filestats) == 0 ) |
1564 |
{ |
||
1565 |
writelogentry(context, " RNFR: ", context->GPBuffer); |
||
1566 |
sendstring(context, interm350_ren); |
||
1567 |
} |
||
1568 |
else |
||
1569 |
42 |
sendstring(context, error550); |
|
1570 |
|||
1571 |
return 1; |
||
1572 |
} |
||
1573 |
|||
1574 |
189 |
int ftpRNTO(PFTPCONTEXT context, const char *params) |
|
1575 |
{ |
||
1576 |
char *_text; |
||
1577 |
|||
1578 |
✓✓ | 189 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1579 |
22 |
return sendstring(context, error530); |
|
1580 |
✓✓ | 167 |
if ( context->Access < FTP_ACCESS_FULL ) |
1581 |
49 |
return sendstring(context, error550_r); |
|
1582 |
✓✓ | 118 |
if ( params == NULL ) |
1583 |
13 |
return sendstring(context, error501); |
|
1584 |
|||
1585 |
105 |
_text = x_malloc(SIZE_OF_GPBUFFER); |
|
1586 |
|||
1587 |
✗✓ | 105 |
if (finalpath( |
1588 |
105 |
context->RootDir, |
|
1589 |
105 |
context->CurrentDir, |
|
1590 |
(char *)params, _text) == NULL) |
||
1591 |
{ |
||
1592 |
free(_text); |
||
1593 |
return 0; |
||
1594 |
} |
||
1595 |
|||
1596 |
✓✓ | 105 |
if ( rename(context->GPBuffer, _text) == 0 ) |
1597 |
{ |
||
1598 |
7 |
writelogentry(context, " RNTO: ", _text); |
|
1599 |
7 |
sendstring(context, success250); |
|
1600 |
} |
||
1601 |
else |
||
1602 |
98 |
sendstring(context, error550); |
|
1603 |
|||
1604 |
105 |
free(_text); |
|
1605 |
105 |
return 1; |
|
1606 |
} |
||
1607 |
|||
1608 |
65 |
int ftpOPTS(PFTPCONTEXT context, const char *params) |
|
1609 |
{ |
||
1610 |
✓✓ | 65 |
if ( params != NULL ) |
1611 |
✗✓ | 30 |
if (strcasecmp(params, "utf8 on") == 0) |
1612 |
return sendstring(context, "200 Always in UTF8 mode.\r\n"); |
||
1613 |
|||
1614 |
65 |
writelogentry(context, " unsupported OPTS: ", params); |
|
1615 |
65 |
return sendstring(context, error500); |
|
1616 |
} |
||
1617 |
|||
1618 |
95 |
int ftpAUTH(PFTPCONTEXT context, const char *params) |
|
1619 |
{ |
||
1620 |
✓✓ | 95 |
if ( params == NULL ) |
1621 |
37 |
return sendstring(context, error501); |
|
1622 |
|||
1623 |
✗✓ | 58 |
if ( strcasecmp(params, "TLS") == 0 ) |
1624 |
/* InitTLSSession will send reply */ |
||
1625 |
return InitTLSSession(&context->TLS_session, context->ControlSocket, 1); |
||
1626 |
else |
||
1627 |
58 |
return sendstring(context, error504); |
|
1628 |
} |
||
1629 |
|||
1630 |
48 |
int ftpPBSZ (PFTPCONTEXT context, const char *params) |
|
1631 |
{ |
||
1632 |
✓✓ | 48 |
if ( params == NULL ) |
1633 |
36 |
return sendstring(context, error501); |
|
1634 |
|||
1635 |
✓✗ | 12 |
if ( context->TLS_session == NULL ) |
1636 |
12 |
return sendstring(context, error503); |
|
1637 |
|||
1638 |
context->BlockSize = strtoul(params, NULL, 10); |
||
1639 |
return sendstring(context, success200); |
||
1640 |
} |
||
1641 |
|||
1642 |
550 |
int ftpPROT (PFTPCONTEXT context, const char *params) |
|
1643 |
{ |
||
1644 |
✓✓ | 550 |
if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN ) |
1645 |
115 |
return sendstring(context, error530); |
|
1646 |
|||
1647 |
✓✓ | 435 |
if ( params == NULL ) |
1648 |
26 |
return sendstring(context, error501); |
|
1649 |
|||
1650 |
✓✗ | 409 |
if ( context->TLS_session == NULL ) |
1651 |
409 |
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 |
42 |
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 |
42 |
pthread_mutex_lock(&context->MTLock); |
|
1730 |
✗✓ | 42 |
pthread_cleanup_push(cleanup_handler, context); |
1731 |
ret = 0; |
||
1732 |
42 |
TLS_datasession = NULL; |
|
1733 |
|||
1734 |
42 |
clientsocket = create_datasocket(context); |
|
1735 |
✗✓ | 42 |
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 |
✗✓ | 42 |
if (TLS_datasession != NULL) |
1755 |
{ |
||
1756 |
gnutls_bye(TLS_datasession, GNUTLS_SHUT_RDWR); |
||
1757 |
gnutls_deinit(TLS_datasession); |
||
1758 |
} |
||
1759 |
|||
1760 |
42 |
writelogentry(context, " LIST complete", ""); |
|
1761 |
|||
1762 |
✓✗ | 42 |
if (clientsocket == INVALID_SOCKET) { |
1763 |
42 |
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 |
42 |
context->WorkerThreadValid = -1; |
|
1776 |
42 |
pthread_cleanup_pop(0); |
|
1777 |
42 |
pthread_mutex_unlock(&context->MTLock); |
|
1778 |
42 |
return NULL; |
|
1779 |
} |
||
1780 |
|||
1781 |
168 |
int ftpMLSD(PFTPCONTEXT context, const char *params) |
|
1782 |
{ |
||
1783 |
struct stat filestats; |
||
1784 |
pthread_t tid; |
||
1785 |
|||
1786 |
✓✓ | 168 |
if (context->Access == FTP_ACCESS_NOT_LOGGED_IN) |
1787 |
43 |
return sendstring(context, error530); |
|
1788 |
✓✓ | 125 |
if (context->WorkerThreadValid == 0) |
1789 |
32 |
return sendstring(context, error550_t); |
|
1790 |
|||
1791 |
✓✗ | 93 |
if (finalpath( |
1792 |
93 |
context->RootDir, |
|
1793 |
93 |
context->CurrentDir, |
|
1794 |
(char *)params, context->GPBuffer) == NULL) |
||
1795 |
return 0; |
||
1796 |
|||
1797 |
✓✓ | 93 |
while (stat(context->GPBuffer, &filestats) == 0) |
1798 |
{ |
||
1799 |
✓✗ | 42 |
if ( !S_ISDIR(filestats.st_mode) ) |
1800 |
break; |
||
1801 |
|||
1802 |
42 |
sendstring(context, interm150); |
|
1803 |
42 |
writelogentry(context, " MLSD-LIST ", (char *)params); |
|
1804 |
42 |
context->WorkerThreadAbort = 0; |
|
1805 |
|||
1806 |
42 |
pthread_mutex_lock(&context->MTLock); |
|
1807 |
|||
1808 |
42 |
context->WorkerThreadValid = pthread_create(&tid, NULL, (__ptr_thread_start_routine)&msld_thread, context); |
|
1809 |
✓✗ | 42 |
if ( context->WorkerThreadValid == 0 ) |
1810 |
42 |
context->WorkerThreadId = tid; |
|
1811 |
else |
||
1812 |
sendstring(context, error451); |
||
1813 |
|||
1814 |
42 |
pthread_mutex_unlock(&context->MTLock); |
|
1815 |
|||
1816 |
42 |
return 1; |
|
1817 |
} |
||
1818 |
|||
1819 |
51 |
return sendstring(context, error550); |
|
1820 |
} |
||
1821 |
|||
1822 |
14935 |
int recvcmd(PFTPCONTEXT context, char *buffer, size_t buffer_size) |
|
1823 |
{ |
||
1824 |
ssize_t l, p = 0; |
||
1825 |
|||
1826 |
✓✗ | 14935 |
if ( buffer_size < 5 ) |
1827 |
return 0; |
||
1828 |
|||
1829 |
memset(buffer, 0, buffer_size); |
||
1830 |
14935 |
--buffer_size; |
|
1831 |
|||
1832 |
✓✓ | 15207 |
while (buffer_size > 0) |
1833 |
{ |
||
1834 |
✓✗ | 15205 |
if (context->TLS_session == NULL) |
1835 |
✗✓ | 15205 |
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 |
✓✓ | 15205 |
if ( l <= 0 ) |
1840 |
return 0; |
||
1841 |
|||
1842 |
14975 |
buffer_size -= l; |
|
1843 |
14975 |
p += l; |
|
1844 |
|||
1845 |
✓✓ | 14975 |
if ( p >= 2 ) |
1846 |
✓✓✓✓ |
14965 |
if ( (buffer[p-2] == '\r') && (buffer[p-1] == '\n') ) |
1847 |
{ |
||
1848 |
14703 |
buffer[p-2] = 0; |
|
1849 |
14703 |
return 1; |
|
1850 |
} |
||
1851 |
} |
||
1852 |
|||
1853 |
return 0; |
||
1854 |
} |
||
1855 |
|||
1856 |
664 |
void *ftp_client_thread(SOCKET *s) |
|
1857 |
664 |
{ |
|
1858 |
FTPCONTEXT ctx __attribute__ ((aligned (16))); |
||
1859 |
664 |
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 |
664 |
ctx.Access = FTP_ACCESS_NOT_LOGGED_IN; |
|
1868 |
664 |
ctx.ControlSocket = *s; |
|
1869 |
664 |
ctx.GPBuffer = x_malloc(SIZE_OF_GPBUFFER); |
|
1870 |
|||
1871 |
memset(&laddr, 0, sizeof(laddr)); |
||
1872 |
664 |
asz = sizeof(laddr); |
|
1873 |
✓✗ | 664 |
while ( getsockname(ctx.ControlSocket, (struct sockaddr *)&laddr, &asz) == 0 ) |
1874 |
{ |
||
1875 |
664 |
ctx.ServerIPv4 = laddr.sin_addr.s_addr; |
|
1876 |
|||
1877 |
memset(&laddr, 0, sizeof(laddr)); |
||
1878 |
664 |
asz = sizeof(laddr); |
|
1879 |
✓✗ | 664 |
if ( getpeername(ctx.ControlSocket, (struct sockaddr *)&laddr, &asz) != 0 ) |
1880 |
break; |
||
1881 |
|||
1882 |
664 |
ctx.ClientIPv4 = laddr.sin_addr.s_addr; |
|
1883 |
664 |
ctx.Mode = MODE_NORMAL; |
|
1884 |
664 |
ctx.WorkerThreadAbort = 0; |
|
1885 |
664 |
ctx.WorkerThreadValid = -1; |
|
1886 |
664 |
ctx.SessionID = __sync_add_and_fetch(&g_newid, 1); |
|
1887 |
664 |
ctx.File = -1; |
|
1888 |
664 |
ctx.DataSocket = INVALID_SOCKET; |
|
1889 |
|||
1890 |
664 |
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 |
664 |
pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE_NP); |
|
1895 |
#endif |
||
1896 |
664 |
pthread_mutex_init(&ctx.MTLock, &m_attr); |
|
1897 |
|||
1898 |
664 |
ctx.CurrentDir[0] = '/'; |
|
1899 |
664 |
sendstring(&ctx, success220); |
|
1900 |
|||
1901 |
memset(&rcvbuf, 0, sizeof(rcvbuf)); |
||
1902 |
|||
1903 |
664 |
snprintf(rcvbuf, sizeof(rcvbuf), "<- New user IP=%u.%u.%u.%u:%u", |
|
1904 |
laddr.sin_addr.s_addr & 0xff, |
||
1905 |
664 |
(laddr.sin_addr.s_addr >> 8 ) & 0xff, |
|
1906 |
664 |
(laddr.sin_addr.s_addr >> 16 ) & 0xff, |
|
1907 |
664 |
(laddr.sin_addr.s_addr >> 24 ) & 0xff, |
|
1908 |
664 |
ntohs(laddr.sin_port) |
|
1909 |
); |
||
1910 |
|||
1911 |
664 |
writelogentry(&ctx, rcvbuf, ""); |
|
1912 |
|||
1913 |
✓✗ | 14935 |
while ( ctx.ControlSocket != INVALID_SOCKET ) { |
1914 |
✓✓ | 14935 |
if ( !recvcmd(&ctx, rcvbuf, sizeof(rcvbuf)) ) |
1915 |
break; |
||
1916 |
|||
1917 |
i = 0; |
||
1918 |
✓✓✓✓ |
24066 |
while ((rcvbuf[i] != 0) && (isalpha(rcvbuf[i]) == 0)) |
1919 |
9363 |
++i; |
|
1920 |
|||
1921 |
14703 |
cmd = &rcvbuf[i]; |
|
1922 |
✓✓ | 128051 |
while ((rcvbuf[i] != 0) && (rcvbuf[i] != ' ')) |
1923 |
113348 |
++i; |
|
1924 |
|||
1925 |
14703 |
cmdlen = &rcvbuf[i] - cmd; |
|
1926 |
✓✓ | 22992 |
while (rcvbuf[i] == ' ') |
1927 |
8289 |
++i; |
|
1928 |
|||
1929 |
✓✓ | 14703 |
if (rcvbuf[i] == 0) |
1930 |
params = NULL; |
||
1931 |
else |
||
1932 |
7403 |
params = &rcvbuf[i]; |
|
1933 |
|||
1934 |
cmdno = -1; |
||
1935 |
rv = 1; |
||
1936 |
✓✓ | 261761 |
for (c=0; c<MAX_CMDS; c++) |
1937 |
✓✓ | 257770 |
if (strncasecmp(cmd, ftpcmds[c], cmdlen) == 0) |
1938 |
{ |
||
1939 |
cmdno = c; |
||
1940 |
10712 |
rv = ftpprocs[c](&ctx, params); |
|
1941 |
10709 |
break; |
|
1942 |
} |
||
1943 |
|||
1944 |
✓✓ | 14700 |
if ( cmdno != FTP_PASSCMD_INDEX ) |
1945 |
13079 |
writelogentry(&ctx, " @@ CMD: ", rcvbuf); |
|
1946 |
else |
||
1947 |
1621 |
writelogentry(&ctx, " @@ CMD: ", "PASS ***"); |
|
1948 |
|||
1949 |
✓✓ | 14700 |
if ( cmdno == -1 ) |
1950 |
3991 |
sendstring(&ctx, error500); |
|
1951 |
|||
1952 |
✓✓ | 14700 |
if ( rv <= 0 ) |
1953 |
break; |
||
1954 |
}; |
||
1955 |
|||
1956 |
661 |
WorkerThreadCleanup(&ctx); |
|
1957 |
|||
1958 |
593 |
pthread_mutex_destroy(&ctx.MTLock); |
|
1959 |
593 |
pthread_mutexattr_destroy(&m_attr); |
|
1960 |
593 |
writelogentry(&ctx, "User disconnected", ""); |
|
1961 |
593 |
break; |
|
1962 |
} |
||
1963 |
|||
1964 |
✗✓ | 593 |
if (ctx.TLS_session != NULL) |
1965 |
gnutls_deinit(ctx.TLS_session); |
||
1966 |
|||
1967 |
593 |
free(ctx.GPBuffer); |
|
1968 |
593 |
close(ctx.ControlSocket); |
|
1969 |
593 |
*s = INVALID_SOCKET; |
|
1970 |
593 |
return NULL; |
|
1971 |
} |
||
1972 |
|||
1973 |
664 |
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 |
664 |
ftpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
1988 |
✗✓ | 664 |
if ( ftpsocket == INVALID_SOCKET ) |
1989 |
{ |
||
1990 |
printf("\r\n socket create error\r\n"); |
||
1991 |
return 0; |
||
1992 |
} |
||
1993 |
|||
1994 |
664 |
rv = 1; |
|
1995 |
664 |
setsockopt(ftpsocket, SOL_SOCKET, SO_REUSEADDR, &rv, sizeof(rv)); |
|
1996 |
|||
1997 |
664 |
scb = (SOCKET *)x_malloc(sizeof(SOCKET)*g_cfg.MaxUsers); |
|
1998 |
✓✓ | 7304 |
for (i = 0; i<g_cfg.MaxUsers; i++) |
1999 |
6640 |
scb[i] = INVALID_SOCKET; |
|
2000 |
|||
2001 |
memset(&laddr, 0, sizeof(laddr)); |
||
2002 |
664 |
laddr.sin_family = AF_INET; |
|
2003 |
664 |
laddr.sin_port = htons(g_cfg.Port); |
|
2004 |
664 |
laddr.sin_addr.s_addr = g_cfg.BindToInterface; |
|
2005 |
664 |
socketret = bind(ftpsocket, (struct sockaddr *)&laddr, sizeof(laddr)); |
|
2006 |
✗✓ | 664 |
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 |
664 |
writelogentry(NULL, success220, ""); |
|
2014 |
|||
2015 |
664 |
socketret = listen(ftpsocket, SOMAXCONN); |
|
2016 |
✓✗ | 664 |
while ( socketret == 0 ) { |
2017 |
memset(&laddr, 0, sizeof(laddr)); |
||
2018 |
664 |
asz = sizeof(laddr); |
|
2019 |
664 |
clientsocket = accept(ftpsocket, (struct sockaddr *)&laddr, &asz); |
|
2020 |
✓✗ | 664 |
if (clientsocket != INVALID_SOCKET) { |
2021 |
664 |
rv = -1; |
|
2022 |
✓✗ | 664 |
for (i=0; i<g_cfg.MaxUsers; i++) { |
2023 |
✓✗ | 664 |
if ( scb[i] == INVALID_SOCKET ) { |
2024 |
|||
2025 |
664 |
scb[i] = clientsocket; |
|
2026 |
664 |
rv = pthread_create(&th, NULL, (__ptr_thread_start_routine)&ftp_client_thread, &scb[i]); |
|
2027 |
✗✓ | 664 |
if ( rv != 0 ) |
2028 |
scb[i] = INVALID_SOCKET; |
||
2029 |
|||
2030 |
break; |
||
2031 |
} |
||
2032 |
} |
||
2033 |
|||
2034 |
✗✓ | 664 |
if ( rv != 0 ) { |
2035 |
sendstring_plaintext(clientsocket, NOSLOTS); |
||
2036 |
close(clientsocket); |
||
2037 |
} |
||
2038 |
} |
||
2039 |
//Terminate the main thread when the child thread terminates |
||
2040 |
664 |
pthread_join(th, NULL); |
|
2041 |
593 |
break; |
|
2042 |
} |
||
2043 |
|||
2044 |
593 |
free(scb); |
|
2045 |
593 |
close(ftpsocket); |
|
2046 |
|||
2047 |
593 |
return NULL; |
|
2048 |
} |
| Generated by: GCOVR (Version 4.2) |