source: rtems/cpukit/ftpd/ftpd.c @ 9b28bea5

4.104.114.84.95
Last change on this file since 9b28bea5 was df49c60, checked in by Joel Sherrill <joel.sherrill@…>, on 06/12/00 at 15:00:15

Merged from 4.5.0-beta3a

  • Property mode set to 100644
File size: 42.1 KB
Line 
1/* FIXME: 1. Parse command is a hack.  We can do better.
2 *        2. chdir is a hack.  We can do better.
3 *        3. PWD doesn't work.
4 *        4. Some sort of access control?
5 *
6 *  FTP Server Daemon
7 *
8 *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
9 *
10 *  $Id$
11 */
12
13/**************************************************************************
14 *                                 ftpd.c                                 *
15 **************************************************************************
16 * Description:                                                           *
17 *                                                                        *
18 *    This file contains the daemon which services requests that appear   *
19 *    on the FTP port.  This server is compatible with FTP, but it        *
20 *    also provides 'hooks' to make it usable in situations where files   *
21 *    are not used/necessary.  Once the server is started, it runs        *
22 *    forever.                                                            *
23 *                                                                        *
24 *                                                                        *
25 *    Organization:                                                       *
26 *                                                                        *
27 *       The FTP daemon is started upon boot.  It runs all the time       *
28 *       and waits for connections on the known FTP port (21).  When      *
29 *       a connection is made, it starts a 'session' task.  That          *
30 *       session then interacts with the remote host.  When the session   *
31 *       is complete, the session task deletes itself.  The daemon still  *
32 *       runs, however.                                                   *
33 *                                                                        *
34 *                                                                        *
35 * Supported commands are:                                                *
36 *                                                                        *
37 * RETR xxx     - Sends a file from the client.                           *
38 * STOR xxx     - Receives a file from the client.  xxx = filename.       *
39 * LIST xxx     - Sends a file list to the client.                        *
40 *                (LIST xxx isn't working yet...)                         *
41 * USER         - Does nothing.                                           *
42 * PASS         - Does nothing.                                           *
43 * SYST         - Replies with the system type (`RTEMS').                 *
44 * DELE xxx     - Delete file xxx.                                        *
45 * MKD xxx      - Create directory xxx.                                   *
46 * RMD xxx      - Remove directory xxx.                                   *
47 * PWD          - Print working directory.                                *
48 * CWD xxx      - Change working directory.                               *
49 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
50 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
51 *                      and port (x*256 + y).                             *
52 *                                                                        *
53 *                                                                        *
54 * The public routines contained in this file are:                        *
55 *                                                                        *
56 *    rtems_initialize_ftpd_start - Starts the server daemon, then        *
57 *                                  returns to its caller.                *
58 *                                                                        *
59 *                                                                        *
60 * The private routines contained in this file are:                       *
61 *                                                                        *
62 *    rtems_ftpd_send_reply    - Sends a reply code and text through the  *
63 *                               control port.                            *
64 *    rtems_ftpd_command_retrieve - Performs to "RETR" command.           *
65 *    rtems_ftpd_command_store - Performs the "STOR" command.             *
66 *    rtems_ftpd_command_list  - Performs the "LIST" command.             *
67 *    rtems_ftpd_command_port  - Opens a data port (the "PORT" command).  *
68 *    rtems_ftpd_parse_command - Parses an incoming command.              *
69 *    rtmes_ftpd_session       - Begins a service session.                *
70 *    rtems_ftpd_daemon        - Listens on the FTP port for service      *
71 *                               requests.                                *
72 *------------------------------------------------------------------------*
73 * Jake Janovetz                                                          *
74 * University of Illinois                                                 *
75 * 1406 West Green Street                                                 *
76 * Urbana IL  61801                                                       *
77 **************************************************************************
78 * Change History:                                                        *
79 *  12/01/97 - Creation (JWJ)                                             *
80 *************************************************************************/
81
82#include <stdio.h>
83#include <stdlib.h>
84#include <string.h>
85#include <unistd.h>
86#include <fcntl.h>
87#include <dirent.h>
88
89#include <rtems.h>
90#include <rtems/rtems_bsdnet.h>
91#include <rtems/error.h>
92#include <syslog.h>
93
94#include <sys/types.h>
95#include <sys/socket.h>
96#include <arpa/ftp.h>
97#include <netinet/in.h>
98
99#include "ftpd.h"
100
101
102extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
103
104/**************************************************************************
105 * Meanings of first and second digits of reply codes:
106 *
107 * Reply:  Description:
108 *-------- --------------
109 *  1yz    Positive preliminary reply.  The action is being started but
110 *         expect another reply before sending another command.
111 *  2yz    Positive completion reply.  A new command can be sent.
112 *  3yz    Positive intermediate reply.  The command has been accpeted
113 *         but another command must be sent.
114 *  4yz    Transient negative completion reply.  The requested action did
115 *         not take place, but the error condition is temporary so the
116 *         command can be reissued later.
117 *  5yz    Permanent negative completion reply.  The command was not 
118 *         accepted and should not be retried.
119 *-------------------------------------------------------------------------
120 *  x0z    Syntax errors.
121 *  x1z    Information.
122 *  x2z    Connections.  Replies referring to the control or data
123 *         connections.
124 *  x3z    Authentication and accounting.  Replies for the login or
125 *         accounting commands.
126 *  x4z    Unspecified.
127 *  x5z    Filesystem status.
128 *************************************************************************/
129
130
131/**************************************************************************
132 * SessionInfo structure.
133 *
134 * The following structure is allocated for each session.  The pointer
135 * to this structure is contained in the tasks notepad entry.
136 *************************************************************************/
137typedef struct
138{
139   struct sockaddr_in  data_addr;   /* Data address for PORT commands */
140   FILE                *ctrl_fp;    /* File pointer for control connection */
141   char                cwd[255];    /* Current working directory */
142                                    /* Login -- future use -- */
143   int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
144} FTPD_SessionInfo_t;
145
146
147#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.0-JWJ) ready."
148#define FTPD_WELCOME_MESSAGE \
149   "Welcome to the RTEMS FTP server.\n" \
150   "\n" \
151   "Login accepted.\n"
152
153
154/**************************************************************************
155 * Function: rtems_ftpd_send_reply                                        *
156 **************************************************************************
157 * Description:                                                           *
158 *                                                                        *
159 *    This procedure sends a reply to the client via the control          *
160 *    connection.                                                         *
161 *                                                                        *
162 *                                                                        *
163 * Inputs:                                                                *
164 *                                                                        *
165 *    int  code  - The 3-digit reply code.                                *
166 *    char *text - Reply text.                                            *
167 *                                                                        *
168 * Output:                                                                *
169 *                                                                        *
170 *    none                                                                *
171 *                                                                        *
172 **************************************************************************
173 * Change History:                                                        *
174 *  12/01/97 - Creation (JWJ)                                             *
175 *************************************************************************/
176static void
177rtems_ftpd_send_reply(int code, char *text)
178{
179   rtems_status_code   sc;
180   FTPD_SessionInfo_t  *info = NULL;
181   char                str[80];
182
183
184   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
185                            (rtems_unsigned32 *)&info);
186
187   /***********************************************************************
188    * code must be a 3-digit number.
189    **********************************************************************/
190   if ((code < 100) || (code > 999))
191   {
192      syslog(LOG_ERR, "ftpd: Code not 3-digits.");
193      return;
194   }
195
196   /***********************************************************************
197    * If a text reply exists, add it to the reply data.
198    **********************************************************************/
199   if (text != NULL)
200   {
201      sprintf(str, "%d %.70s\r\n", code, text);
202      fprintf(info->ctrl_fp, "%d %.70s\r\n", code, text);
203   }
204   else
205   {
206      sprintf(str, "%d\r\n", code);
207      fprintf(info->ctrl_fp, "%d\r\n", code);
208   }
209   fflush(info->ctrl_fp);
210}
211
212
213/**************************************************************************
214 * Function: rtems_ftpd_command_retrieve                                  *
215 **************************************************************************
216 * Description:                                                           *
217 *                                                                        *
218 *    This performs the "RETR" command.  A data connection must already   *
219 *    be open (via the "PORT" command.)  Here, we send the data to the    *
220 *    connection.                                                         *
221 *                                                                        *
222 *                                                                        *
223 * Inputs:                                                                *
224 *                                                                        *
225 *    char *filename   - Source filename.                                 *
226 *                                                                        *
227 * Output:                                                                *
228 *                                                                        *
229 *    int  - 0 for reply sent.                                            *
230 *           1 for no reply sent.                                         *
231 *                                                                        *
232 **************************************************************************
233 * Change History:                                                        *
234 *  04/29/98 - Creation (JWJ)                                             *
235 *************************************************************************/
236static int
237rtems_ftpd_command_retrieve(char *filename)
238{
239   int                 s;
240   int                 n;
241   int                 fd;
242   unsigned char       *bufr;
243   rtems_status_code   sc;
244   FTPD_SessionInfo_t  *info = NULL;
245
246
247   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
248                            (rtems_unsigned32 *)&info);
249
250   if ((fd = open(filename, O_RDONLY)) == -1)
251   {
252      rtems_ftpd_send_reply(450, "Error opening file.");
253      return(0);
254   }
255
256   bufr = (unsigned char *)malloc(BUFSIZ);
257   if (bufr == NULL)
258   {
259      rtems_ftpd_send_reply(440, "Server error - malloc fail.");
260      close(fd);
261      return(0);
262   }
263
264   /***********************************************************************
265    * Connect to the data connection (PORT made in an earlier PORT call).
266    **********************************************************************/
267   rtems_ftpd_send_reply(150, "BINARY data connection.");
268   s = socket(AF_INET, SOCK_STREAM, 0);
269   if (connect(s, (struct sockaddr *)&info->data_addr,
270               sizeof(struct sockaddr)) < 0)
271   {
272      rtems_ftpd_send_reply(420, "Server error - could not connect socket.");
273      free(bufr);
274      close(fd);
275      close(s);
276      return(1);
277   }
278
279   /***********************************************************************
280    * Send the data over the ether.
281    **********************************************************************/
282   while ((n = read(fd, bufr, BUFSIZ)) > 0)
283   {
284      send(s, bufr, n, 0);
285      bufr[n-1] = '\0';
286   }
287
288   if (n == 0)
289   {
290      rtems_ftpd_send_reply(210, "File sent successfully.");
291   }
292   else
293   {
294      rtems_ftpd_send_reply(450, "Retrieve failed.");
295   }
296
297   if (close(s) != 0)
298   {
299      syslog(LOG_ERR, "ftpd: Error closing data socket");
300   }
301
302   free(bufr);
303   close(fd);
304   return(0);
305}
306
307
308/**************************************************************************
309 * Function: rtems_ftpd_command_store                                     *
310 **************************************************************************
311 * Description:                                                           *
312 *                                                                        *
313 *    This performs the "STOR" command.  A data connection must already   *
314 *    be open (via the "PORT" command.)  Here, we get the data from the   *
315 *    connection and figure out what to do with it.                       *
316 *                                                                        *
317 *                                                                        *
318 * Inputs:                                                                *
319 *                                                                        *
320 *    char *filename   - Destination filename.                            *
321 *                                                                        *
322 * Output:                                                                *
323 *                                                                        *
324 *    int  - 0 for success.                                               *
325 *           1 for failure.                                               *
326 *                                                                        *
327 **************************************************************************
328 * Change History:                                                        *
329 *  12/01/97 - Creation (JWJ)                                             *
330 *************************************************************************/
331static int
332rtems_ftpd_command_store(char *filename)
333{
334   char                   *bufr;
335   int                    s;
336   int                    n;
337   unsigned long          size = 0;
338   rtems_status_code      sc;
339   FTPD_SessionInfo_t     *info = NULL;
340   struct rtems_ftpd_hook *usehook = NULL;
341
342
343   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
344                            (rtems_unsigned32 *)&info);
345
346   bufr = (char *)malloc(BUFSIZ * sizeof(char));
347   if (bufr == NULL)
348   {
349      rtems_ftpd_send_reply(440, "Server error - malloc fail.");
350      return(1);
351   }
352
353   rtems_ftpd_send_reply(150, "BINARY data connection.");
354
355   s = socket(AF_INET, SOCK_STREAM, 0);
356   if (connect(s, (struct sockaddr *)&info->data_addr,
357               sizeof(struct sockaddr)) < 0)
358   {
359      free(bufr);
360      close(s);
361      return(1);
362   }
363
364
365   /***********************************************************************
366    * File: "/dev/null" just throws the data away.
367    * Otherwise, search our list of hooks to see if we need to do something
368    *   special.
369    **********************************************************************/
370   if (!strncmp("/dev/null", filename, 9))
371   {
372      while ((n = read(s, bufr, BUFSIZ)) > 0);
373   }
374   else if (rtems_ftpd_configuration.hooks != NULL)
375   {
376      struct rtems_ftpd_hook *hook;
377      int i;
378
379      i = 0;
380      hook = &rtems_ftpd_configuration.hooks[i++];
381      while (hook->filename != NULL)
382      {
383         if (!strcmp(hook->filename, filename))
384         {
385            usehook = hook;
386            break;
387         }
388         hook = &rtems_ftpd_configuration.hooks[i++];
389      }
390   }
391
392   if (usehook != NULL)
393   {
394      char                *bigBufr;
395
396      /***********************************************************************
397       * Allocate space for our "file".
398       **********************************************************************/
399      bigBufr = (char *)malloc(
400                  rtems_ftpd_configuration.max_hook_filesize * sizeof(char));
401      if (bigBufr == NULL)
402      {
403         rtems_ftpd_send_reply(440, "Server error - malloc fail.");
404         free(bufr);
405         return(1);
406      }
407
408      /***********************************************************************
409       * Retrieve the file into our buffer space.
410       **********************************************************************/
411      size = 0;
412      while ((n = read(s, bufr, BUFSIZ)) > 0)
413      {
414         if (size + n >
415               rtems_ftpd_configuration.max_hook_filesize * sizeof(char))
416         {
417            rtems_ftpd_send_reply(440, "Server error - Buffer size exceeded.");
418            free(bufr);
419            free(bigBufr);
420            close(s);
421            return(1);
422         }
423         memcpy(&bigBufr[size], bufr, n);
424         size += n;
425      }
426      close(s);
427
428      /***********************************************************************
429       * Call our hook.
430       **********************************************************************/
431      if ((usehook->hook_function)(bigBufr, size) == 0)
432      {
433         rtems_ftpd_send_reply(210, "File transferred successfully.");
434      }
435      else
436      {
437         rtems_ftpd_send_reply(440, "File transfer failed.");
438      }
439      free(bigBufr);
440   }
441   else
442   {
443      int    fd;
444      size_t written;
445
446      fd = creat(filename, S_IRUSR | S_IWUSR |
447                           S_IRGRP | S_IWGRP |
448                           S_IROTH | S_IWOTH);
449      if (fd == -1)
450      {
451         rtems_ftpd_send_reply(450, "Could not open file.");
452         close(s);
453         free(bufr);
454         return(1);
455      }
456      while ((n = read(s, bufr, BUFSIZ)) > 0)
457      {
458         written = write(fd, bufr, n);
459         if (written == -1)
460         {
461            rtems_ftpd_send_reply(450, "Error during write.");
462            close(fd);
463            close(s);
464            free(bufr);
465            return(1);
466         }
467      }
468      close(fd);
469      close(s);
470      rtems_ftpd_send_reply(226, "Transfer complete.");
471   }
472
473   free(bufr);
474   return(0);
475}
476
477
478/**************************************************************************
479 * Function: rtems_ftpd_command_list                                      *
480 **************************************************************************
481 * Description:                                                           *
482 *                                                                        *
483 *    Sends a file list through a data connection.  The data              *
484 *    connection must have already been opened with the "PORT" command.   *
485 *                                                                        *
486 *                                                                        *
487 * Inputs:                                                                *
488 *                                                                        *
489 *    char *fname  - File (or directory) to list.                         *
490 *                                                                        *
491 * Output:                                                                *
492 *                                                                        *
493 *    none                                                                *
494 *                                                                        *
495 **************************************************************************
496 * Change History:                                                        *
497 *  12/01/97 - Creation (JWJ)                                             *
498 *************************************************************************/
499static void
500rtems_ftpd_command_list(char *fname)
501{
502   int                 s;
503   rtems_status_code   sc;
504   FTPD_SessionInfo_t  *info = NULL;
505   DIR                 *dirp;
506   struct dirent       *dp;
507   char                dirline[255];
508   struct stat         stat_buf;
509
510
511   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
512                            (rtems_unsigned32 *)&info);
513
514   rtems_ftpd_send_reply(150, "ASCII data connection for LIST.");
515
516   s = socket(AF_INET, SOCK_STREAM, 0);
517   if (connect(s, (struct sockaddr *)&info->data_addr,
518               sizeof(struct sockaddr)) < 0)
519   {
520      syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
521      return;
522   }
523
524   if ((dirp = opendir(fname)) == NULL)
525   {
526      sprintf(dirline, "%s: No such file or directory.%s\n",
527              fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
528      send(s, dirline, strlen(dirline), 0);
529      close(s);
530      rtems_ftpd_send_reply(226, "Transfer complete.");
531      return;
532   }
533   while ((dp = readdir(dirp)) != NULL)
534   {
535      if (stat(dp->d_name, &stat_buf) == 0)
536      {
537         sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
538                 (S_ISLNK(stat_buf.st_mode)?('l'):
539                    (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
540                 (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
541                 (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
542                 (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
543                 (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
544                 (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
545                 (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
546                 (stat_buf.st_mode & S_IROTH)?('r'):('-'),
547                 (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
548                 (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
549                 (int)stat_buf.st_uid,
550                 (int)stat_buf.st_gid,
551                 (int)stat_buf.st_size,
552                 dp->d_name,
553                 (info->xfer_mode==TYPE_A)?("\r"):(""));
554         send(s, dirline, strlen(dirline), 0);
555      }
556   }
557   closedir(dirp);
558
559   close(s);
560   rtems_ftpd_send_reply(226, "Transfer complete.");
561}
562
563
564/*
565 * Cheesy way to change directories
566 */
567static void
568rtems_ftpd_CWD(char *dir)
569{
570   rtems_status_code   sc;
571   FTPD_SessionInfo_t  *info = NULL;
572
573
574   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
575                            (rtems_unsigned32 *)&info);
576
577   if (chdir(dir) == 0)
578   {
579      rtems_ftpd_send_reply(250, "CWD command successful.");
580   }
581   else
582   {
583      rtems_ftpd_send_reply(550, "CWD command failed.");
584   }
585}
586
587
588/**************************************************************************
589 * Function: rtems_ftpd_command_port                                      *
590 **************************************************************************
591 * Description:                                                           *
592 *                                                                        *
593 *    This procedure opens up a data port given the IP address of the     *
594 *    remote machine and the port on the remote machine.  This connection *
595 *    will then be used to transfer data between the hosts.               *
596 *                                                                        *
597 *                                                                        *
598 * Inputs:                                                                *
599 *                                                                        *
600 *    char *bufr - Arguments to the "PORT" command.                       *
601 *                                                                        *
602 *                                                                        *
603 * Output:                                                                *
604 *                                                                        *
605 *    none                                                                *
606 *                                                                        *
607 **************************************************************************
608 * Change History:                                                        *
609 *  12/01/97 - Creation (JWJ)                                             *
610 *************************************************************************/
611static void
612rtems_ftpd_command_port(char *bufr)
613{
614   char                *ip;
615   char                *port;
616   int                 ip0, ip1, ip2, ip3, port0, port1;
617   rtems_status_code   sc;
618   FTPD_SessionInfo_t  *info = NULL;
619
620
621   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
622                            (rtems_unsigned32 *)&info);
623
624   sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
625   ip = (char *)&(info->data_addr.sin_addr);
626   ip[0] = ip0 & 0xff;
627   ip[1] = ip1 & 0xff;
628   ip[2] = ip2 & 0xff;
629   ip[3] = ip3 & 0xff;
630   port = (char *)&(info->data_addr.sin_port);
631   port[0] = port0 & 0xff;
632   port[1] = port1 & 0xff;
633   info->data_addr.sin_family = AF_INET;
634}
635
636
637/**************************************************************************
638 * Function: rtems_ftpd_parse_command                                     *
639 **************************************************************************
640 * Description:                                                           *
641 *                                                                        *
642 *    Here, we parse the commands that have come through the control      *
643 *    connection.                                                         *
644 *                                                                        *
645 * FIXME: This section is somewhat of a hack.  We should have a better    *
646 *        way to parse commands.                                          *
647 *                                                                        *
648 * Inputs:                                                                *
649 *                                                                        *
650 *    char *bufr     - Pointer to the buffer which contains the command   *
651 *                     text.                                              *
652 *                                                                        *
653 * Output:                                                                *
654 *                                                                        *
655 *    none                                                                *
656 *                                                                        *
657 **************************************************************************
658 * Change History:                                                        *
659 *  12/01/97 - Creation (JWJ)                                             *
660 *************************************************************************/
661static void
662rtems_ftpd_parse_command(char *bufr)
663{
664   char fname[255];
665   rtems_status_code   sc;
666   FTPD_SessionInfo_t  *info = NULL;
667
668
669   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
670                            (rtems_unsigned32 *)&info);
671
672   if (!strncmp("PORT", bufr, 4))
673   {
674      rtems_ftpd_send_reply(200, "PORT command successful.");
675      rtems_ftpd_command_port(&bufr[5]);
676   }
677   else if (!strncmp("RETR", bufr, 4))
678   {
679      sscanf(&bufr[5], "%254s", fname);
680      rtems_ftpd_command_retrieve(fname);
681   }
682   else if (!strncmp("STOR", bufr, 4))
683   {
684      sscanf(&bufr[5], "%254s", fname);
685      rtems_ftpd_command_store(fname);
686   }
687   else if (!strncmp("LIST", bufr, 4))
688   {
689      if (bufr[5] == '\n')
690      {
691         rtems_ftpd_command_list(".");
692      }
693      else
694      {
695         sscanf(&bufr[5], "%254s", fname);
696         rtems_ftpd_command_list(fname);
697      }
698   }
699   else if (!strncmp("USER", bufr, 4))
700   {
701      rtems_ftpd_send_reply(230, "User logged in.");
702   }
703   else if (!strncmp("SYST", bufr, 4))
704   {
705      rtems_ftpd_send_reply(240, "RTEMS");
706   }
707   else if (!strncmp("TYPE", bufr, 4))
708   {
709      if (bufr[5] == 'I')
710      {
711         info->xfer_mode = TYPE_I;
712         rtems_ftpd_send_reply(200, "Type set to I.");
713      }
714      else if (bufr[5] == 'A')
715      {
716         info->xfer_mode = TYPE_A;
717         rtems_ftpd_send_reply(200, "Type set to A.");
718      }
719      else
720      {
721         info->xfer_mode = TYPE_I;
722         rtems_ftpd_send_reply(504, "Type not implemented.  Set to I.");
723      }
724   }
725   else if (!strncmp("PASS", bufr, 4))
726   {
727      rtems_ftpd_send_reply(230, "User logged in.");
728   }
729   else if (!strncmp("DELE", bufr, 4))
730   {
731      sscanf(&bufr[4], "%254s", fname);
732      if (unlink(fname) == 0)
733      {
734         rtems_ftpd_send_reply(257, "DELE successful.");
735      }
736      else
737      {
738         rtems_ftpd_send_reply(550, "DELE failed.");
739      }
740   }
741   else if (!strncmp("SITE CHMOD", bufr, 10))
742   {
743      int mask;
744
745      sscanf(&bufr[11], "%o %254s", &mask, fname);
746      if (chmod(fname, (mode_t)mask) == 0)
747      {
748         rtems_ftpd_send_reply(257, "CHMOD successful.");
749      }
750      else
751      {
752         rtems_ftpd_send_reply(550, "CHMOD failed.");
753      }
754   }
755   else if (!strncmp("RMD", bufr, 3))
756   {
757      sscanf(&bufr[4], "%254s", fname);
758      if (rmdir(fname) == 0)
759      {
760         rtems_ftpd_send_reply(257, "RMD successful.");
761      }
762      else
763      {
764         rtems_ftpd_send_reply(550, "RMD failed.");
765      }
766   }
767   else if (!strncmp("MKD", bufr, 3))
768   {
769      sscanf(&bufr[4], "%254s", fname);
770      if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
771      {
772         rtems_ftpd_send_reply(257, "MKD successful.");
773      }
774      else
775      {
776         rtems_ftpd_send_reply(550, "MKD failed.");
777      }
778   }
779   else if (!strncmp("CWD", bufr, 3))
780   {
781      sscanf(&bufr[4], "%254s", fname);
782      rtems_ftpd_CWD(fname);
783   }
784   else if (!strncmp("PWD", bufr, 3))
785   {
786      char *cwd = getcwd(0, 0);
787      sprintf(bufr, "\"%s\" is the current directory.", cwd);
788      rtems_ftpd_send_reply(250, bufr);
789      free(cwd);
790   }
791   else
792   {
793      rtems_ftpd_send_reply(500, "Unrecognized/unsupported command.");
794   }
795}
796
797
798/**************************************************************************
799 * Function: rtems_ftpd_session                                           *
800 **************************************************************************
801 * Description:                                                           *
802 *                                                                        *
803 *    This task is started when the FTP daemon gets a service request     *
804 *    from a remote machine.  Here, we watch for commands that will       *
805 *    come through the "control" connection.  These commands are then     *
806 *    parsed and executed until the connection is closed, either          *
807 *    unintentionally or intentionally with the "QUIT" command.           *
808 *                                                                        *
809 *                                                                        *
810 * Inputs:                                                                *
811 *                                                                        *
812 *    rtems_task_argument arg - The daemon task passes the socket         *
813 *                              which serves as the control connection.   *
814 *                                                                        *
815 * Output:                                                                *
816 *                                                                        *
817 *    none                                                                *
818 *                                                                        *
819 **************************************************************************
820 * Change History:                                                        *
821 *  12/01/97 - Creation (JWJ)                                             *
822 *************************************************************************/
823static void
824rtems_ftpd_session(rtems_task_argument arg)
825{
826   char                cmd[256];
827   rtems_status_code   sc;
828   FTPD_SessionInfo_t  *info = NULL;
829
830
831   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
832                            (rtems_unsigned32 *)&info);
833
834   rtems_ftpd_send_reply(220, FTPD_SERVER_MESSAGE);
835
836   /***********************************************************************
837    * Set initial directory to "/".
838    **********************************************************************/
839   strcpy(info->cwd, "/");
840   info->xfer_mode = TYPE_A;
841   while (1)
842   {
843      if (fgets(cmd, 256, info->ctrl_fp) == NULL)
844      {
845         syslog(LOG_INFO, "ftpd: Connection aborted.");
846         break;
847      }
848
849      if (!strncmp("QUIT", cmd, 4))
850      {
851         rtems_ftpd_send_reply(221, "Goodbye.");
852         break;
853      }
854      else
855      {
856         rtems_ftpd_parse_command(cmd);
857      }
858   }
859
860   if (fclose(info->ctrl_fp) != 0)
861   {
862      syslog(LOG_ERR, "ftpd: Could not close session.");
863   }
864
865
866   /* Least we can do is put the CWD back to /. */
867   chdir("/");
868
869   /***********************************************************************
870    * Free up the allocated SessionInfo struct and exit.
871    **********************************************************************/
872   free(info);
873   sc = rtems_task_delete(RTEMS_SELF);
874   syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
875          rtems_status_text(sc));
876}
877
878
879/**************************************************************************
880 * Function: rtems_ftpd_daemon                                            *
881 **************************************************************************
882 * Description:                                                           *
883 *                                                                        *
884 *    This task runs in the background forever.  It waits for service     *
885 *    requests on the FTP port (port 21).  When a request is received,    *
886 *    it opens a new session to handle those requests until the           *
887 *    connection is closed.                                               *
888 *                                                                        *
889 *                                                                        *
890 * Inputs:                                                                *
891 *                                                                        *
892 *    none                                                                *
893 *                                                                        *
894 * Output:                                                                *
895 *                                                                        *
896 *    none                                                                *
897 *                                                                        *
898 **************************************************************************
899 * Change History:                                                        *
900 *  12/01/97 - Creation (JWJ)                                             *
901 *************************************************************************/
902
903/* this is not prototyped in strict ansi mode */
904
905FILE *fdopen (int fildes, const char *mode);
906
907static void
908rtems_ftpd_daemon()
909{
910   int                 s;
911   int                 s1;
912   int                 addrLen;
913   struct sockaddr_in  remoteAddr;
914   struct sockaddr_in  localAddr;
915   char                sessionID;
916   rtems_task_priority priority;
917   rtems_status_code   sc;
918   rtems_id            tid;
919   FTPD_SessionInfo_t  *info = NULL;
920
921
922   sessionID = 'a';
923
924   s = socket(AF_INET, SOCK_STREAM, 0);
925   if (s < 0)
926   {
927      perror("Creating socket");
928   }
929
930   localAddr.sin_family      = AF_INET;
931   localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
932   localAddr.sin_addr.s_addr = INADDR_ANY;
933   memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
934   if (bind(s, (struct sockaddr *)&localAddr,
935            sizeof(localAddr)) < 0)
936   {
937      perror("Binding control socket");
938   }
939
940   if (listen(s, 2) < 0)
941   {
942      perror("Listening on control socket");
943   }
944
945   while (1)
946   {
947      /********************************************************************
948       * Allocate a SessionInfo structure for the session task.
949       *******************************************************************/
950      info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
951      if (info == NULL)
952      {
953         syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
954         rtems_panic("Malloc fail.");
955      }
956
957      /********************************************************************
958       * Accept on the socket and start the session task.
959       *******************************************************************/
960      addrLen = sizeof(remoteAddr);
961      s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
962      if (s1 < 0)
963      {
964         perror("Accepting control connection");
965      }
966
967      rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
968      sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
969                             priority, 8*1024,
970                             RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
971                             RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
972                             RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
973                             &tid);
974      if (sc != RTEMS_SUCCESSFUL)
975      {
976         syslog(LOG_ERR, "ftpd: Could not create FTPD session: %s",
977                rtems_status_text(sc));
978      }
979
980      if (sessionID == 'z')
981      {
982         sessionID = 'a';
983      }
984      else
985      {
986         sessionID++;
987      }
988
989      /********************************************************************
990       * Send the socket on to the new session.
991       *******************************************************************/
992      if ((info->ctrl_fp = fdopen(s1, "r+")) == NULL)
993      {
994         syslog(LOG_ERR, "ftpd: fdopen() on socket failed.");
995         close(s1);
996      }
997      else
998      {
999         sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
1000                                  (rtems_unsigned32)info);
1001         sc = rtems_task_start(tid, rtems_ftpd_session, 0);
1002         if (sc != RTEMS_SUCCESSFUL)
1003         {
1004            syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
1005                   rtems_status_text(sc));
1006         }
1007      }
1008   }
1009}
1010
1011
1012/**************************************************************************
1013 * Function: rtems_ftpd_start                                             *
1014 **************************************************************************
1015 * Description:                                                           *
1016 *                                                                        *
1017 *    Here, we start the FTPD task which waits for FTP requests and       *
1018 *    services them.  This procedure returns to its caller once the       *
1019 *    task is started.                                                    *
1020 *                                                                        *
1021 *                                                                        *
1022 * Inputs:                                                                *
1023 *                                                                        *
1024 *    rtems_task_priority priority - Priority to assign to this task.     *
1025 *                                                                        *
1026 * Output:                                                                *
1027 *                                                                        *
1028 *    int - RTEMS_SUCCESSFUL on successful start of the daemon.           *
1029 *                                                                        *
1030 **************************************************************************
1031 * Change History:                                                        *
1032 *  12/01/97 - Creation (JWJ)                                             *
1033 *************************************************************************/
1034int
1035rtems_initialize_ftpd()
1036{
1037   rtems_status_code   sc;
1038   rtems_id            tid;
1039
1040
1041   if (rtems_ftpd_configuration.port == 0)
1042   {
1043      rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
1044   }
1045
1046   /***********************************************************************
1047    * Default FTPD priority.
1048    **********************************************************************/
1049   if (rtems_ftpd_configuration.priority == 0)
1050   {
1051      rtems_ftpd_configuration.priority = 40;
1052   }
1053   sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
1054                          rtems_ftpd_configuration.priority, 8*1024,
1055                          RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
1056                          RTEMS_INTERRUPT_LEVEL(0),
1057                          RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
1058                          &tid);
1059   if (sc != RTEMS_SUCCESSFUL)
1060   {
1061      syslog(LOG_ERR, "ftpd: Could not create FTP daemon: %s",
1062             rtems_status_text(sc));
1063      return(RTEMS_UNSATISFIED);
1064   }
1065
1066   sc = rtems_task_start(tid, rtems_ftpd_daemon, 0);
1067   if (sc != RTEMS_SUCCESSFUL)
1068   {
1069      syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
1070             rtems_status_text(sc));
1071      return(RTEMS_UNSATISFIED);
1072   }   
1073
1074   syslog(LOG_INFO, "ftpd: FTP daemon started.");
1075   return(RTEMS_SUCCESSFUL);
1076}
1077
Note: See TracBrowser for help on using the repository browser.