source: rtems/cpukit/ftpd/ftpd.c @ 6d0e13c

4.104.114.84.9
Last change on this file since 6d0e13c was 6d0e13c, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 16, 1999 at 1:51:53 AM

Added ftpd server from Jake Janovetz <janovetz@…>.

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