Lets keep our version of opftpd in the CVS
[oftpd.git] / src / ftp_command.c
1 /*
2  * $Id$
3  */
4
5 #include <config.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <netdb.h>
14 #include "ftp_command.h"
15 #include "af_portability.h"
16 #include "daemon_assert.h"
17
18 /* argument types */
19 #define ARG_NONE              0
20 #define ARG_STRING            1
21 #define ARG_OPTIONAL_STRING   2
22 #define ARG_HOST_PORT         3
23 #define ARG_TYPE              4
24 #define ARG_STRUCTURE         5
25 #define ARG_MODE              6
26 #define ARG_OFFSET            7
27 #define ARG_HOST_PORT_LONG    8
28 #define ARG_HOST_PORT_EXT     9
29 #define ARG_OPTIONAL_NUMBER  10
30
31 /* our FTP commands */
32 struct {
33     char *name;
34     int arg_type;
35 } command_def[] = {
36     { "USER", ARG_STRING          },
37     { "PASS", ARG_STRING          },
38     { "CWD",  ARG_STRING          },
39     { "CDUP", ARG_NONE            },
40     { "QUIT", ARG_NONE            },
41     { "PORT", ARG_HOST_PORT       },
42     { "LPRT", ARG_HOST_PORT_LONG  },
43     { "EPRT", ARG_HOST_PORT_EXT   },
44     { "PASV", ARG_NONE            },
45     { "LPSV", ARG_NONE            },
46     { "EPSV", ARG_OPTIONAL_NUMBER },
47     { "TYPE", ARG_TYPE            },
48     { "STRU", ARG_STRUCTURE       },
49     { "MODE", ARG_MODE            },
50     { "RETR", ARG_STRING          },
51     { "STOR", ARG_STRING          },
52     { "PWD",  ARG_NONE            },
53     { "LIST", ARG_OPTIONAL_STRING },
54     { "NLST", ARG_OPTIONAL_STRING },
55     { "SYST", ARG_NONE            },
56     { "HELP", ARG_OPTIONAL_STRING },
57     { "NOOP", ARG_NONE            },
58     { "REST", ARG_OFFSET          },
59     { "SIZE", ARG_STRING          },
60     { "MDTM", ARG_STRING          }
61 };
62
63 #define NUM_COMMAND (sizeof(command_def) / sizeof(command_def[0]))
64
65 /* prototypes */
66 static const char *copy_string(char *dst, const char *src);
67 static const char *parse_host_port(struct sockaddr_in *addr, const char *s);
68 static const char *parse_number(int *num, const char *s, int max_num);
69 static const char *parse_offset(off_t *ofs, const char *s);
70 static const char *parse_host_port_long(sockaddr_storage_t *sa, const char *s);
71 static const char *parse_host_port_ext(sockaddr_storage_t *sa, const char *s); 
72
73 int ftp_command_parse(const char *input, ftp_command_t *cmd)
74 {
75     int i;
76     int len;
77     int match;
78     ftp_command_t tmp;
79     int c;
80     const char *optional_number;
81
82     daemon_assert(input != NULL);
83     daemon_assert(cmd != NULL);
84
85     /* see if our input starts with a valid command */
86     match = -1;
87     for (i=0; (i<NUM_COMMAND) && (match == -1); i++) {
88         len = strlen(command_def[i].name);
89         if (strncasecmp(input, command_def[i].name, len) == 0) {
90             match = i;
91         }
92     }
93
94     /* if we didn't find a match, return error */
95     if (match == -1) {
96         return 0;
97     }
98     daemon_assert(match >= 0);
99     daemon_assert(match < NUM_COMMAND);
100
101     /* copy our command */
102     strcpy(tmp.command, command_def[match].name);
103
104     /* advance input past the command */
105     input += strlen(command_def[match].name);
106
107     /* now act based on the command */
108     switch (command_def[match].arg_type) {
109
110         case ARG_NONE:
111             tmp.num_arg = 0;
112             break;
113
114         case ARG_STRING:
115             if (*input != ' ') {
116                 return 0;
117             }
118             ++input;
119             input = copy_string(tmp.arg[0].string, input);
120             tmp.num_arg = 1;
121             break;
122
123         case ARG_OPTIONAL_STRING:
124             if (*input == ' ') {
125                 ++input;
126                 input = copy_string(tmp.arg[0].string, input);
127                 tmp.num_arg = 1;
128             } else {
129                 tmp.num_arg = 0;
130             }
131             break;
132
133         case ARG_HOST_PORT:
134             if (*input != ' ') {
135                 return 0;
136             }
137             input++;
138
139             /* parse the host & port information (if any) */
140             input = parse_host_port(&tmp.arg[0].host_port, input);
141             if (input == NULL) {
142                 return 0;
143             }
144             tmp.num_arg = 1;
145             break;
146
147        case ARG_HOST_PORT_LONG:
148             if (*input != ' ') {
149                 return 0;
150             }
151             input++;
152  
153             /* parse the host & port information (if any) */
154             input = parse_host_port_long(&tmp.arg[0].host_port, input);
155             if (input == NULL) {
156                 return 0;
157             }
158             tmp.num_arg = 1;
159             break;    
160
161         case ARG_HOST_PORT_EXT:
162             if (*input != ' ') {
163                 return 0;
164             }
165             input++;
166  
167             /* parse the host & port information (if any) */
168             input = parse_host_port_ext(&tmp.arg[0].host_port, input);
169             if (input == NULL) {
170                 return 0;
171             }
172             tmp.num_arg = 1;
173             break;
174  
175         /* the optional number may also be "ALL" */
176         case ARG_OPTIONAL_NUMBER:
177             if (*input == ' ') {
178                 ++input;
179                 optional_number = parse_number(&tmp.arg[0].num, input, 255);
180                 if (optional_number != NULL) {
181                     input = optional_number;
182                 } else {
183                     if ((tolower(input[0]) == 'a') &&
184                         (tolower(input[1]) == 'l') &&
185                         (tolower(input[2]) == 'l')) 
186                     {
187                         tmp.arg[0].num = EPSV_ALL;
188                         input += 3;
189                     } else {
190                         return 0;
191                     }
192                 }
193                 tmp.num_arg = 1;
194             } else {
195                 tmp.num_arg = 0;
196             }
197             break;     
198
199         case ARG_TYPE:
200             if (*input != ' ') {
201                 return 0;
202             }
203             input++;
204
205             c = toupper(*input);
206             if ((c == 'A') || (c == 'E')) {
207                 tmp.arg[0].string[0] = c;
208                 tmp.arg[0].string[1] = '\0';
209                 input++;
210
211                 if (*input == ' ') {
212                     input++;
213                     c = toupper(*input);
214                     if ((c != 'N') && (c != 'T') && (c != 'C')) {
215                         return 0;
216                     }
217                     tmp.arg[1].string[0] = c;
218                     tmp.arg[1].string[1] = '\0';
219                     input++;
220                     tmp.num_arg = 2;
221                 } else {
222                     tmp.num_arg = 1;
223                 }
224             } else if (c == 'I') {
225                 tmp.arg[0].string[0] = 'I';
226                 tmp.arg[0].string[1] = '\0';
227                 input++;
228                 tmp.num_arg = 1;
229             } else if (c == 'L') {
230                 tmp.arg[0].string[0] = 'L';
231                 tmp.arg[0].string[1] = '\0';
232                 input++;
233                 input = parse_number(&tmp.arg[1].num, input, 255);
234                 if (input == NULL) {
235                     return 0;
236                 }
237                 tmp.num_arg = 2;
238             } else {
239                 return 0;
240             }
241
242             break;
243
244         case ARG_STRUCTURE:
245             if (*input != ' ') {
246                 return 0;
247             }
248             input++;
249
250             c = toupper(*input);
251             if ((c != 'F') && (c != 'R') && (c != 'P')) {
252                 return 0;
253             }
254             input++;
255             tmp.arg[0].string[0] = c;
256             tmp.arg[0].string[1] = '\0';
257             tmp.num_arg = 1;
258             break;
259
260         case ARG_MODE:
261             if (*input != ' ') {
262                 return 0;
263             }
264             input++;
265
266             c = toupper(*input);
267             if ((c != 'S') && (c != 'B') && (c != 'C')) {
268                 return 0;
269             }
270             input++;
271             tmp.arg[0].string[0] = c;
272             tmp.arg[0].string[1] = '\0';
273             tmp.num_arg = 1;
274             break;
275
276         case ARG_OFFSET:
277             if (*input != ' ') {
278                 return 0;
279             }
280             input++;
281             input = parse_offset(&tmp.arg[0].offset, input);
282             if (input == NULL) {
283                 return 0;
284             }
285             tmp.num_arg = 1;
286             break;
287
288         default:
289             daemon_assert(0);
290     } 
291
292     /* check for our ending newline */
293     if (*input != '\n') {
294         return 0;
295     }
296
297     /* return our result */
298     *cmd = tmp;
299     return 1;
300 }
301
302 /* copy a string terminated with a newline */
303 static const char *copy_string(char *dst, const char *src)
304 {
305     int i;
306
307     daemon_assert(dst != NULL);
308     daemon_assert(src != NULL);
309
310     for (i=0; (i<=MAX_STRING_LEN) && (src[i]!='\0') && (src[i]!='\n'); i++) {
311         dst[i] = src[i];
312     }
313     dst[i] = '\0';
314
315     return src+i;
316 }
317
318
319 static const char *parse_host_port(struct sockaddr_in *addr, const char *s)
320 {
321     int i;
322     int octets[6];
323     char addr_str[16];
324     int port;
325     struct in_addr in_addr;
326
327     daemon_assert(addr != NULL);
328     daemon_assert(s != NULL);
329
330     /* scan in 5 pairs of "#," */
331     for (i=0; i<5; i++) {
332         s = parse_number(&octets[i], s, 255);
333         if (s == NULL) {
334             return NULL;
335         }
336         if (*s != ',') {
337             return NULL;
338         }
339         s++;
340     }
341
342     /* scan in ending "#" */
343     s = parse_number(&octets[5], s, 255);
344     if (s == NULL) {
345         return NULL;
346     }
347
348     daemon_assert(octets[0] >= 0);
349     daemon_assert(octets[0] <= 255);
350     daemon_assert(octets[1] >= 0);
351     daemon_assert(octets[1] <= 255);
352     daemon_assert(octets[2] >= 0);
353     daemon_assert(octets[2] <= 255);
354     daemon_assert(octets[3] >= 0);
355     daemon_assert(octets[3] <= 255);
356
357     /* convert our number to a IP/port */
358     sprintf(addr_str, "%d.%d.%d.%d", 
359             octets[0], octets[1], octets[2], octets[3]);
360     port = (octets[4] << 8) | octets[5];
361 #ifdef HAVE_INET_ATON
362     if (inet_aton(addr_str, &in_addr) == 0) {
363         return NULL;
364     }
365 #else
366     in_addr.s_addr = inet_addr(addr_str);
367     if (in_addr.s_addr == -1) {
368         return NULL;
369     }
370 #endif
371     addr->sin_family = AF_INET;
372     addr->sin_addr = in_addr;
373     addr->sin_port = htons(port);
374
375     /* return success */
376     return s;
377 }
378
379 /* note: returns success even for unknown address families */
380 /*       this is okay, as long as subsequent uses VERIFY THE FAMILY first */
381 static const char *parse_host_port_long(sockaddr_storage_t *sa, const char *s)
382 {   
383     int i;
384     int family;
385     int tmp;
386     int addr_len;
387     unsigned char addr[255];
388     int port_len;
389     unsigned char port[255];
390
391     /* we are family */
392     s = parse_number(&family, s, 255);
393     if (s == NULL) {
394         return NULL;
395     }
396     if (*s != ',') {
397         return NULL;
398     }
399     s++;
400
401     /* parse host length */
402     s = parse_number(&addr_len, s, 255);
403     if (s == NULL) {
404         return NULL;
405     }
406     if (*s != ',') {
407         return NULL;
408     }
409     s++;
410
411     /* parse address */
412     for (i=0; i<addr_len; i++) {
413         daemon_assert(i < sizeof(addr)/sizeof(addr[0]));
414         s = parse_number(&tmp, s, 255);
415         addr[i] = tmp;
416         if (s == NULL) {
417             return NULL;
418         }
419         if (*s != ',') {
420             return NULL;
421         }
422         s++;
423     }
424
425     /* parse port length */
426     s = parse_number(&port_len, s, 255);
427     if (s == NULL) {
428         return NULL;
429     }
430
431     /* parse port */
432     for (i=0; i<port_len; i++) {
433         if (*s != ',') {
434             return NULL;
435         }
436         s++;
437         daemon_assert(i < sizeof(port)/sizeof(port[0]));
438         s = parse_number(&tmp, s, 255);
439         port[i] = tmp;
440     }
441
442     /* okay, everything parses, load the address if possible */
443     if (family == 4) {
444         SAFAM(sa) = AF_INET;
445         if (addr_len != sizeof(struct in_addr)) {
446             return NULL;
447         }
448         if (port_len != 2) {
449             return NULL;
450         }
451         memcpy(&SINADDR(sa), addr, addr_len);
452         SINPORT(sa) = htons((port[0] << 8) + port[1]);
453     }
454 #ifdef INET6
455     else if (family == 6) {
456         SAFAM(sa) = AF_INET6;
457         if (addr_len != sizeof(struct in6_addr)) {
458             return NULL;
459         }
460         if (port_len != 2) {
461             return NULL;
462         }
463         memcpy(&SIN6ADDR(sa), addr, addr_len);
464         SINPORT(sa) = htons((port[0] << 8) + port[1]);
465     }
466 #endif
467     else {
468         SAFAM(sa) = -1;
469     }
470
471     /* return new pointer */
472     return s;
473 }
474
475 static const char *parse_host_port_ext(sockaddr_storage_t *sa, const char *s)
476
477     int delimeter;
478     int family;
479     char *p;
480     int len;
481     char addr_str[256];
482     int port;
483
484     /* get delimeter character */
485     if ((*s < 33) || (*s > 126)) {
486         return NULL;
487     }
488     delimeter = *s;
489     s++;
490
491     /* get address family */
492     if (*s == '1') {
493         family = AF_INET;
494     } 
495 #ifdef INET6
496     else if (*s == '2') {
497         family = AF_INET6;
498     }
499 #endif
500     else {
501         return NULL;
502     }
503     s++;
504     if (*s != delimeter) { 
505         return NULL;
506     }
507     s++;
508
509     /* get address string */
510     p = strchr(s, delimeter);
511     if (p == NULL) {
512         return NULL;
513     }
514     len = p - s;
515     if (len >= sizeof(addr_str)) {
516         return NULL;
517     }
518     memcpy(addr_str, s, len);
519     addr_str[len] = '\0';
520     s = p+1;
521
522     /* get port */
523     s = parse_number(&port, s, 65535);
524     if (s == NULL) {
525         return NULL;
526     }
527     if (*s != delimeter) {
528         return NULL;
529     }
530     s++;
531
532     /* now parse the value passed */
533 #ifndef INET6
534     {
535         struct in_addr in_addr;
536
537 #ifdef HAVE_INET_ATON
538         if (inet_aton(addr_str, &in_addr) == 0) {
539             return NULL;
540         }
541 #else
542         in_addr.s_addr = inet_addr(addr_str);
543         if (in_addr.s_addr == -1) {
544             return NULL;
545         }
546 #endif /* HAVE_INET_ATON */
547
548         SIN4ADDR(sa) = in_addr;
549     }
550 #else
551     {
552         struct addrinfo hints;
553         struct *res;
554
555         memset(&hints, 0, sizeof(hints));
556         hints.ai_flags = AI_NUMERICHOST;
557         hints.ai_family = family;
558
559         if (getaddrinfo(addr_str, NULL, &hints, &res) != 0) {
560             return NULL;
561         }
562
563         memcpy(sa, res->ai_addr, res->ai_addrlen);
564
565         freeaddrinfo(res);
566     }
567 #endif /* INET6 */
568
569     SAFAM(sa) = family;
570     SINPORT(sa) = htons(port);
571
572     /* return new pointer */
573     return s;
574 }
575
576 /* scan the string for a number from 0 to max_num */
577 /* returns the next non-numberic character */
578 /* returns NULL if not at least one digit */
579 static const char *parse_number(int *num, const char *s, int max_num)
580 {
581     int tmp;
582     int cur_digit;
583     
584     daemon_assert(s != NULL);
585     daemon_assert(num != NULL);
586     
587     /* handle first character */
588     if (!isdigit(*s)) {
589         return NULL;
590     }
591     tmp = (*s - '0');
592     s++;
593
594     /* handle subsequent characters */
595     while (isdigit(*s)) {
596         cur_digit = (*s - '0');
597
598         /* check for overflow */
599         if ((max_num - cur_digit) < tmp) {
600             return NULL;
601         }
602
603         tmp *= 10;
604         tmp += cur_digit;
605         s++;
606     }
607
608     daemon_assert(tmp >= 0);
609     daemon_assert(tmp <= max_num);
610
611     /* return the result */
612     *num = tmp;
613     return s;
614 }
615
616 static const char *parse_offset(off_t *ofs, const char *s)
617 {
618     off_t tmp_ofs;
619     int cur_digit;
620     off_t max_ofs;
621 #if SIZEOF_UNSIGNED_LONG >= SIZEOF_OFF_T
622     unsigned long cutoff, cutlim;
623 #elif SIZEOF_UNSIGNED_LONG_LONG && SIZEOF_UNSIGNED_LONG_LONG >= SIZEOF_OFF_T
624     unsigned long long cutoff, cutlim;
625 #else
626 # error cannot figure out a type larger or equal to off_t
627 #endif
628
629     daemon_assert(ofs != NULL);
630     daemon_assert(s != NULL);
631
632     /* calculate maximum allowable offset based on size of off_t */
633 #if SIZEOF_OFF_T == 8
634     max_ofs = (off_t)9223372036854775807LL;
635 #elif SIZEOF_OFF_T == 4
636     max_ofs = (off_t)2147483647LL;
637 #elif SIZEOF_OFF_T == 2
638     max_ofs = (off_t)32767LL;
639 #else
640 # error sizeof(off_t) unknown
641 #endif
642
643     cutoff = max_ofs / 10;
644     cutlim = max_ofs % 10;
645
646     /* handle first character */
647     if (!isdigit(*s)) {
648         return NULL;
649     }
650     tmp_ofs = (*s - '0');
651     s++;
652
653     /* handle subsequent characters */
654     while (isdigit(*s)) {
655         cur_digit = (*s - '0');
656
657         if (tmp_ofs > cutoff || (tmp_ofs == cutoff && cur_digit > cutlim))
658           return NULL; /*overflow*/
659
660         tmp_ofs *= 10;
661         tmp_ofs += cur_digit;
662         s++;
663     }
664
665     /* return */
666     *ofs = tmp_ofs;
667     return s;
668 }
669