source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 00db336a

4.115
Last change on this file since 00db336a was 00db336a, checked in by Sebastian Huber <sebastian.huber@…>, on 02/21/11 at 10:58:15

2011-02-21 Sebastian Huber <sebastian.huber@…>

  • libnetworking/lib/ftpfs.c: Workaround for some firewalls.
  • Property mode set to 100644
File size: 29.4 KB
Line 
1/**
2 * @file
3 *
4 * File Transfer Protocol file system (FTP client).
5 */
6
7/*
8 * Copyright (c) 2009, 2010, 2011
9 * embedded brains GmbH
10 * Obere Lagerstr. 30
11 * D-82178 Puchheim
12 * Germany
13 * <rtems@embedded-brains.de>
14 *
15 * (c) Copyright 2002
16 * Thomas Doerfler
17 * IMD Ingenieurbuero fuer Microcomputertechnik
18 * Herbststr. 8
19 * 82178 Puchheim, Germany
20 * <Thomas.Doerfler@imd-systems.de>
21 *
22 * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>.
23 *
24 * This code has been created after closly inspecting "tftpdriver.c" from Eric
25 * Norum.
26 *
27 * The license and distribution terms for this file may be
28 * found in the file LICENSE in this distribution or at
29 * http://www.rtems.com/license/LICENSE.
30 *
31 * $Id$
32 */
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include <ctype.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <malloc.h>
42#include <netdb.h>
43#include <stdarg.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <arpa/inet.h>
49#include <netinet/in.h>
50#include <sys/select.h>
51#include <sys/socket.h>
52#include <sys/types.h>
53
54#include <rtems.h>
55#include <rtems/ftpfs.h>
56#include <rtems/imfs.h>
57#include <rtems/libio.h>
58#include <rtems/rtems_bsdnet.h>
59#include <rtems/seterr.h>
60
61#ifdef DEBUG
62  #define DEBUG_PRINTF(...) printf(__VA_ARGS__)
63#else
64  #define DEBUG_PRINTF(...)
65#endif
66
67/**
68 * Connection entry for each open file stream.
69 */
70typedef struct {
71  /**
72   * Control connection socket.
73   */
74  int ctrl_socket;
75
76  /**
77   * Data transfer socket.
78   */
79  int data_socket;
80
81  /**
82   * End of file flag.
83   */
84  bool eof;
85} rtems_ftpfs_entry;
86
87/**
88 * Mount entry for each file system instance.
89 */
90typedef struct {
91  /**
92   * Verbose mode enabled or disabled.
93   */
94  bool verbose;
95
96  /**
97   * Timeout value
98   */
99  struct timeval timeout;
100} rtems_ftpfs_mount_entry;
101
102static const rtems_filesystem_operations_table rtems_ftpfs_ops;
103
104static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
105
106static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers;
107
108static bool rtems_ftpfs_use_timeout(const struct timeval *to)
109{
110  return to->tv_sec != 0 || to->tv_usec != 0;
111}
112
113static int rtems_ftpfs_set_connection_timeout(
114  int socket,
115  const struct timeval *to
116)
117{
118  if (rtems_ftpfs_use_timeout(to)) {
119    int rv = 0;
120   
121    rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to));
122    if (rv != 0) {
123      return EIO;
124    }
125
126    rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to));
127    if (rv != 0) {
128      return EIO;
129    }
130  }
131
132  return 0;
133}
134
135static rtems_status_code rtems_ftpfs_do_ioctl(
136  const char *mount_point,
137  ioctl_command_t req,
138  ...
139)
140{
141  rtems_status_code sc = RTEMS_SUCCESSFUL;
142  int rv = 0;
143  int fd = 0;
144  va_list ap;
145
146  if (mount_point == NULL) {
147    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
148  }
149
150  fd = open(mount_point, O_RDWR);
151  if (fd < 0) {
152    return RTEMS_INVALID_NAME;
153  }
154 
155  va_start(ap, req);
156  rv = ioctl(fd, req, va_arg(ap, void *));
157  va_end(ap);
158  if (rv != 0) {
159    sc = RTEMS_INVALID_NUMBER;
160  }
161
162  rv = close(fd);
163  if (rv != 0 && sc == RTEMS_SUCCESSFUL) {
164    sc = RTEMS_IO_ERROR;
165  }
166 
167  return sc;
168}
169
170rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose)
171{
172  return rtems_ftpfs_do_ioctl(
173    mount_point,
174    RTEMS_FTPFS_IOCTL_GET_VERBOSE,
175    verbose
176  );
177}
178
179rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose)
180{
181  return rtems_ftpfs_do_ioctl(
182    mount_point,
183    RTEMS_FTPFS_IOCTL_SET_VERBOSE,
184    &verbose
185  );
186}
187
188rtems_status_code rtems_ftpfs_get_timeout(
189  const char *mount_point,
190  struct timeval *timeout
191)
192{
193  return rtems_ftpfs_do_ioctl(
194    mount_point,
195    RTEMS_FTPFS_IOCTL_GET_TIMEOUT,
196    timeout
197  );
198}
199
200rtems_status_code rtems_ftpfs_set_timeout(
201  const char *mount_point,
202  const struct timeval *timeout
203)
204{
205  return rtems_ftpfs_do_ioctl(
206    mount_point,
207    RTEMS_FTPFS_IOCTL_SET_TIMEOUT,
208    timeout
209  );
210}
211
212typedef void (*rtems_ftpfs_reply_parser)(
213  const char * /* reply fragment */,
214  size_t /* reply fragment length */,
215  void * /* parser argument */
216);
217
218typedef enum {
219  RTEMS_FTPFS_REPLY_START,
220  RTEMS_FTPFS_REPLY_SINGLE_LINE,
221  RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE,
222  RTEMS_FTPFS_REPLY_MULTI_LINE,
223  RTEMS_FTPFS_REPLY_NEW_LINE,
224  RTEMS_FTPFS_REPLY_NEW_LINE_START
225} rtems_ftpfs_reply_state;
226
227typedef enum {
228  RTEMS_FTPFS_REPLY_ERROR = 0,
229  RTEMS_FTPFS_REPLY_1 = '1',
230  RTEMS_FTPFS_REPLY_2 = '2',
231  RTEMS_FTPFS_REPLY_3 = '3',
232  RTEMS_FTPFS_REPLY_4 = '4',
233  RTEMS_FTPFS_REPLY_5 = '5'
234} rtems_ftpfs_reply;
235
236#define RTEMS_FTPFS_REPLY_SIZE 3
237
238static rtems_ftpfs_reply rtems_ftpfs_get_reply(
239  int socket,
240  rtems_ftpfs_reply_parser parser,
241  void *parser_arg,
242  bool verbose
243)
244{
245  rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
246  unsigned char reply_first [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' };
247  unsigned char reply_last [RTEMS_FTPFS_REPLY_SIZE] = { 'b', 'b', 'b' };
248  size_t reply_first_index = 0;
249  size_t reply_last_index = 0;
250  char buf [128];
251
252  while (true) {
253    /* Receive reply fragment from socket */
254    ssize_t i = 0;
255    ssize_t rv = recv(socket, buf, sizeof(buf), 0);
256
257    if (rv <= 0) {
258      return RTEMS_FTPFS_REPLY_ERROR;
259    }
260
261    /* Be verbose if necessary */
262    if (verbose) {
263      write(STDERR_FILENO, buf, (size_t) rv);
264    }
265
266    /* Invoke parser if necessary */
267    if (parser != NULL) {
268      parser(buf, (size_t) rv, parser_arg);
269    }
270
271    /* Parse reply fragment */
272    for (i = 0; i < rv; ++i) {
273      char c = buf [i];
274
275      switch (state) {
276        case RTEMS_FTPFS_REPLY_START:
277          if (reply_first_index < RTEMS_FTPFS_REPLY_SIZE) {
278            reply_first [reply_first_index] = c;
279            ++reply_first_index;
280          } else if (c == '-') {
281            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
282          } else {
283            state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
284          }
285          break;
286        case RTEMS_FTPFS_REPLY_SINGLE_LINE:
287          if (c == '\n') {
288            state = RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE;
289          }
290          break;
291        case RTEMS_FTPFS_REPLY_MULTI_LINE:
292          if (c == '\n') {
293            state = RTEMS_FTPFS_REPLY_NEW_LINE_START;
294            reply_last_index = 0;
295          }
296          break;
297        case RTEMS_FTPFS_REPLY_NEW_LINE:
298        case RTEMS_FTPFS_REPLY_NEW_LINE_START:
299          if (reply_last_index < RTEMS_FTPFS_REPLY_SIZE) {
300            state = RTEMS_FTPFS_REPLY_NEW_LINE;
301            reply_last [reply_last_index] = c;
302            ++reply_last_index;
303          } else {
304            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
305          }
306          break;
307        default:
308          return RTEMS_FTPFS_REPLY_ERROR;
309      }
310    }
311
312    /* Check reply */
313    if (state == RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE) {
314      if (
315        isdigit(reply_first [0])
316          && isdigit(reply_first [1])
317          && isdigit(reply_first [2])
318      ) {
319        break;
320      } else {
321        return RTEMS_FTPFS_REPLY_ERROR;
322      }
323    } else if (state == RTEMS_FTPFS_REPLY_NEW_LINE_START) {
324      bool ok = true;
325
326      for (i = 0; i < RTEMS_FTPFS_REPLY_SIZE; ++i) {
327        ok = ok
328          && reply_first [i] == reply_last [i]
329          && isdigit(reply_first [i]);
330      }
331
332      if (ok) {
333        break;
334      }
335    }
336  }
337
338  return reply_first [0];
339}
340
341static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
342  int socket,
343  const char *cmd,
344  const char *arg,
345  rtems_ftpfs_reply_parser parser,
346  void *parser_arg,
347  bool verbose
348)
349{
350  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
351  size_t cmd_len = strlen(cmd);
352  size_t arg_len = arg != NULL ? strlen(arg) : 0;
353  size_t len = cmd_len + arg_len + 2;
354  char *buf = malloc(len);
355
356  if (buf != NULL) {
357    ssize_t n = 0;
358    char *buf_arg = buf + cmd_len;
359    char *buf_eol = buf_arg + arg_len;
360
361    memcpy(buf, cmd, cmd_len);
362    memcpy(buf_arg, arg, arg_len);
363    buf_eol [0] = '\r';
364    buf_eol [1] = '\n';
365
366    /* Send */
367    n = send(socket, buf, len, 0);
368    if (n == (ssize_t) len) {
369      if (verbose) {
370        write(STDERR_FILENO, buf, len);
371      }
372
373      /* Reply */
374      reply = rtems_ftpfs_get_reply(socket, parser, parser_arg, verbose);
375    }
376
377    free(buf);
378  }
379
380  return reply;
381}
382
383static rtems_ftpfs_reply rtems_ftpfs_send_command(
384  int socket,
385  const char *cmd,
386  const char *arg,
387  bool verbose
388)
389{
390  return rtems_ftpfs_send_command_with_parser(
391    socket,
392    cmd,
393    arg,
394    NULL,
395    NULL,
396    verbose
397  );
398}
399
400typedef enum {
401  STATE_USER_NAME,
402  STATE_START_PASSWORD,
403  STATE_START_HOST_NAME,
404  STATE_START_HOST_NAME_OR_PATH,
405  STATE_START_PATH,
406  STATE_PASSWORD,
407  STATE_HOST_NAME,
408  STATE_DONE,
409  STATE_INVALID
410} split_state;
411
412static bool rtems_ftpfs_split_names (
413  char *s,
414  const char **user,
415  const char **password,
416  const char **hostname,
417  const char **path
418)
419{
420  split_state state = STATE_USER_NAME;
421  size_t len = strlen(s);
422  size_t i = 0;
423
424  *user = s;
425  *password = NULL;
426  *hostname = NULL;
427  *path = NULL;
428
429  for (i = 0; i < len; ++i) {
430    char c = s [i];
431
432    switch (state) {
433      case STATE_USER_NAME:
434        if (c == ':') {
435          state = STATE_START_PASSWORD;
436          s [i] = '\0';
437        } else if (c == '@') {
438          state = STATE_START_HOST_NAME;
439          s [i] = '\0';
440        } else if (c == '/') {
441          state = STATE_START_HOST_NAME_OR_PATH;
442          s [i] = '\0';
443        }
444        break;
445      case STATE_START_PASSWORD:
446        state = STATE_PASSWORD;
447        *password = &s [i];
448        --i;
449        break;
450      case STATE_START_HOST_NAME:
451        state = STATE_HOST_NAME;
452        *hostname = &s [i];
453        --i;
454        break;
455      case STATE_START_HOST_NAME_OR_PATH:
456        if (c == '@') {
457          state = STATE_START_HOST_NAME;
458        } else {
459          state = STATE_DONE;
460          *path = &s [i];
461          goto done;
462        }
463        break;
464      case STATE_START_PATH:
465        state = STATE_DONE;
466        *path = &s [i];
467        goto done;
468      case STATE_PASSWORD:
469        if (c == '@') {
470          state = STATE_START_HOST_NAME;
471          s [i] = '\0';
472        } else if (c == '/') {
473          state = STATE_START_HOST_NAME_OR_PATH;
474          s [i] = '\0';
475        }
476        break;
477      case STATE_HOST_NAME:
478        if (c == '/') {
479          state = STATE_START_PATH;
480          s [i] = '\0';
481        }
482        break;
483      default:
484        state = STATE_INVALID;
485        goto done;
486    }
487  }
488
489done:
490
491  /* This is a special case with no username and password */
492  if (*hostname == NULL) {
493    *hostname = &s [0];
494    *user = "anonymous";
495    *password = *user;
496  }
497
498  /* If we have no password use the user name */
499  if (*password == NULL) {
500    *password = *user;
501  }
502
503  return state == STATE_DONE;
504}
505
506static socklen_t rtems_ftpfs_create_address(
507  struct sockaddr_in *sa,
508  unsigned long address,
509  unsigned short port
510)
511{
512  memset(sa, sizeof(*sa), 0);
513
514  sa->sin_family = AF_INET;
515  sa->sin_addr.s_addr = address;
516  sa->sin_port = port;
517  sa->sin_len = sizeof(*sa);
518
519  return sizeof(*sa);
520}
521
522static int rtems_ftpfs_terminate(rtems_libio_t *iop, bool error)
523{
524  int eno = 0;
525  int rv = 0;
526  rtems_ftpfs_entry *e = iop->data1;
527  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
528  bool verbose = me->verbose;
529  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
530
531  if (e != NULL) {
532    /* Close data connection if necessary */
533    if (e->data_socket >= 0) {
534      rv = close(e->data_socket);
535      if (rv != 0) {
536        eno = EIO;
537      }
538
539      /* For write connections we have to obtain the transfer reply  */
540      if (
541        e->ctrl_socket >= 0
542          && (iop->flags & LIBIO_FLAGS_WRITE) != 0
543          && !error
544      ) {
545        reply = rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose);
546        if (reply != RTEMS_FTPFS_REPLY_2) {
547          eno = EIO;
548        }
549      }
550    }
551
552    /* Close control connection if necessary */
553    if (e->ctrl_socket >= 0) {
554      reply = rtems_ftpfs_send_command(
555        e->ctrl_socket,
556        "QUIT",
557        NULL,
558        verbose
559      );
560      if (reply != RTEMS_FTPFS_REPLY_2) {
561        eno = EIO;
562      }
563
564      rv = close(e->ctrl_socket);
565      if (rv != 0) {
566        eno = EIO;
567      }
568    }
569
570    /* Free connection entry */
571    free(e);
572  }
573
574  /* Invalidate IO entry */
575  iop->data1 = NULL;
576
577  return eno;
578}
579
580static int rtems_ftpfs_open_ctrl_connection(
581  rtems_ftpfs_entry *e,
582  const char *user,
583  const char *password,
584  const char *hostname,
585  uint32_t *client_address,
586  bool verbose,
587  const struct timeval *timeout
588)
589{
590  int rv = 0;
591  int eno = 0;
592  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
593  struct in_addr address = { .s_addr = 0 };
594  struct sockaddr_in sa;
595  socklen_t size = 0;
596
597  /* Create the socket for the control connection */
598  e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0);
599  if (e->ctrl_socket < 0) {
600    return ENOMEM;
601  }
602
603  /* Set up the server address from the hostname */
604  if (inet_aton(hostname, &address) == 0) {
605    /* Try to get the address by name */
606    struct hostent *he = gethostbyname(hostname);
607
608    if (he != NULL) {
609      memcpy(&address, he->h_addr, sizeof(address));
610    } else {
611      return ENOENT;
612    }
613  }
614  rtems_ftpfs_create_address(&sa, address.s_addr, htons(RTEMS_FTPFS_CTRL_PORT));
615  DEBUG_PRINTF("server = %s\n", inet_ntoa(sa.sin_addr));
616
617  /* Open control connection */
618  rv = connect(
619    e->ctrl_socket,
620    (struct sockaddr *) &sa,
621    sizeof(sa)
622  );
623  if (rv != 0) {
624    return ENOENT;
625  }
626
627  /* Set control connection timeout */
628  eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout);
629  if (eno != 0) {
630    return eno;
631  }
632
633  /* Get client address */
634  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
635  rv = getsockname(
636    e->ctrl_socket,
637    (struct sockaddr *) &sa,
638    &size
639  );
640  if (rv != 0) {
641    return ENOMEM;
642  }
643  *client_address = ntohl(sa.sin_addr.s_addr);
644  DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr));
645
646  /* Now we should get a welcome message from the server */
647  reply = rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose);
648  if (reply != RTEMS_FTPFS_REPLY_2) {
649    return ENOENT;
650  }
651
652  /* Send USER command */
653  reply = rtems_ftpfs_send_command(e->ctrl_socket, "USER ", user, verbose);
654  if (reply == RTEMS_FTPFS_REPLY_3) {
655    /* Send PASS command */
656    reply = rtems_ftpfs_send_command(
657      e->ctrl_socket,
658      "PASS ",
659      password,
660      verbose
661    );
662    if (reply != RTEMS_FTPFS_REPLY_2) {
663      return EACCES;
664    }
665
666    /* TODO: Some server may require an account */
667  } else if (reply != RTEMS_FTPFS_REPLY_2) {
668    return EACCES;
669  }
670
671  /* Send TYPE command to set binary mode for all data transfers */
672  reply = rtems_ftpfs_send_command(e->ctrl_socket, "TYPE I", NULL, verbose);
673  if (reply != RTEMS_FTPFS_REPLY_2) {
674    return EIO;
675  }
676
677  return 0;
678}
679
680static int rtems_ftpfs_open_data_connection_active(
681  rtems_ftpfs_entry *e,
682  uint32_t client_address,
683  const char *file_command,
684  const char *filename,
685  bool verbose,
686  const struct timeval *timeout
687)
688{
689  int rv = 0;
690  int eno = 0;
691  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
692  struct sockaddr_in sa;
693  socklen_t size = 0;
694  int port_socket = -1;
695  char port_command [] = "PORT 000,000,000,000,000,000";
696  uint16_t data_port = 0;
697
698  /* Create port socket to establish a data data connection */
699  port_socket = socket(AF_INET, SOCK_STREAM, 0);
700  if (port_socket < 0) {
701    eno = ENOMEM;
702    goto cleanup;
703  }
704
705  /* Bind port socket */
706  rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
707  rv = bind(
708    port_socket,
709    (struct sockaddr *) &sa,
710    sizeof(sa)
711  );
712  if (rv != 0) {
713    eno = EBUSY;
714    goto cleanup;
715  }
716
717  /* Get port number for data socket */
718  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
719  rv = getsockname(
720    port_socket,
721    (struct sockaddr *) &sa,
722    &size
723  );
724  if (rv != 0) {
725    eno = ENOMEM;
726    goto cleanup;
727  }
728  data_port = ntohs(sa.sin_port);
729
730  /* Send PORT command to set data connection port for server */
731  snprintf(
732    port_command,
733    sizeof(port_command),
734    "PORT %lu,%lu,%lu,%lu,%lu,%lu",
735    (client_address >> 24) & 0xffUL,
736    (client_address >> 16) & 0xffUL,
737    (client_address >> 8) & 0xffUL,
738    (client_address >> 0) & 0xffUL,
739    (data_port >> 8) & 0xffUL,
740    (data_port >> 0) & 0xffUL
741  );
742  reply = rtems_ftpfs_send_command(
743    e->ctrl_socket,
744    port_command,
745    NULL,
746    verbose
747  );
748  if (reply != RTEMS_FTPFS_REPLY_2) {
749    eno = ENOTSUP;
750    goto cleanup;
751  }
752
753  /* Listen on port socket for incoming data connections */
754  rv = listen(port_socket, 1);
755  if (rv != 0) {
756    eno = EBUSY;
757    goto cleanup;
758  }
759
760  /* Send RETR or STOR command with filename */
761  reply = rtems_ftpfs_send_command(
762    e->ctrl_socket,
763    file_command,
764    filename,
765    verbose
766  );
767  if (reply != RTEMS_FTPFS_REPLY_1) {
768    eno = EIO;
769    goto cleanup;
770  }
771
772  /* Wait for connect on data connection if necessary */
773  if (rtems_ftpfs_use_timeout(timeout)) {
774    struct timeval to = *timeout;
775    fd_set fds;
776
777    FD_ZERO(&fds);
778    FD_SET(port_socket, &fds);
779
780    rv = select(port_socket + 1, &fds, NULL, NULL, &to);
781    if (rv <= 0) {
782      eno = EIO;
783      goto cleanup;
784    }
785  }
786
787  /* Accept data connection  */
788  size = sizeof(sa);
789  e->data_socket = accept(
790    port_socket,
791    (struct sockaddr *) &sa,
792    &size
793  );
794  if (e->data_socket < 0) {
795    eno = EIO;
796    goto cleanup;
797  }
798
799cleanup:
800
801  /* Close port socket if necessary */
802  if (port_socket >= 0) {
803    rv = close(port_socket);
804    if (rv != 0) {
805      eno = EIO;
806    }
807  }
808
809  return eno;
810}
811
812typedef enum {
813  RTEMS_FTPFS_PASV_START = 0,
814  RTEMS_FTPFS_PASV_JUNK,
815  RTEMS_FTPFS_PASV_DATA,
816  RTEMS_FTPFS_PASV_DONE
817} rtems_ftpfs_pasv_state;
818
819typedef struct {
820  rtems_ftpfs_pasv_state state;
821  uint8_t data [6];
822  size_t index;
823} rtems_ftpfs_pasv_entry;
824
825static void rtems_ftpfs_pasv_parser(
826  const char* buf,
827  size_t len,
828  void *arg
829)
830{
831  rtems_ftpfs_pasv_entry *e = arg;
832  size_t i = 0;
833
834  for (i = 0; i < len; ++i) {
835    int c = buf [i];
836
837    switch (e->state) {
838      case RTEMS_FTPFS_PASV_START:
839        if (!isdigit(c)) {
840          e->state = RTEMS_FTPFS_PASV_JUNK;
841          e->index = 0;
842        }
843        break;
844      case RTEMS_FTPFS_PASV_JUNK:
845        if (isdigit(c)) {
846          e->state = RTEMS_FTPFS_PASV_DATA;
847          e->data [e->index] = (uint8_t) (c - '0');
848        }
849        break;
850      case RTEMS_FTPFS_PASV_DATA:
851        if (isdigit(c)) {
852          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
853        } else if (c == ',') {
854          ++e->index;
855          if (e->index < sizeof(e->data)) {
856            e->data [e->index] = 0;
857          } else {
858            e->state = RTEMS_FTPFS_PASV_DONE;
859          }
860        } else {
861          e->state = RTEMS_FTPFS_PASV_DONE;
862        }
863        break;
864      default:
865        return;
866    }
867  }
868}
869
870static int rtems_ftpfs_open_data_connection_passive(
871  rtems_ftpfs_entry *e,
872  uint32_t client_address,
873  const char *file_command,
874  const char *filename,
875  bool verbose,
876  const struct timeval *timeout
877)
878{
879  int rv = 0;
880  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
881  struct sockaddr_in sa;
882  uint32_t data_address = 0;
883  uint16_t data_port = 0;
884
885  rtems_ftpfs_pasv_entry pe = {
886    .state = RTEMS_FTPFS_PASV_START
887  };
888
889  /* Send PASV command */
890  reply = rtems_ftpfs_send_command_with_parser(
891    e->ctrl_socket,
892    "PASV",
893    NULL,
894    rtems_ftpfs_pasv_parser,
895    &pe,
896    verbose
897  );
898  if (reply != RTEMS_FTPFS_REPLY_2) {
899    return ENOTSUP;
900  }
901  data_address = ((uint32_t)(pe.data [0]) << 24) + ((uint32_t)(pe.data [1]) << 16)
902    + ((uint32_t)(pe.data [2]) << 8) + ((uint32_t)(pe.data [3]));
903  data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
904  rtems_ftpfs_create_address(&sa, htonl(data_address), htons(data_port));
905  DEBUG_PRINTF(
906    "server data = %s:%u\n",
907    inet_ntoa(sa.sin_addr),
908    (unsigned) ntohs(sa.sin_port)
909  );
910
911  /* Create data socket */
912  e->data_socket = socket(AF_INET, SOCK_STREAM, 0);
913  if (e->data_socket < 0) {
914    return ENOMEM;
915  }
916
917  /* Open data connection */
918  rv = connect(
919    e->data_socket,
920    (struct sockaddr *) &sa,
921    sizeof(sa)
922  );
923  if (rv != 0) {
924    return EIO;
925  }
926
927  /* Send RETR or STOR command with filename */
928  reply = rtems_ftpfs_send_command(
929    e->ctrl_socket,
930    file_command,
931    filename,
932    verbose
933  );
934  if (reply != RTEMS_FTPFS_REPLY_1) {
935    return EIO;
936  }
937
938  return 0;
939}
940
941static int rtems_ftpfs_open(
942  rtems_libio_t *iop,
943  const char *path,
944  uint32_t flags,
945  uint32_t mode
946)
947{
948  int eno = 0;
949  bool ok = false;
950  rtems_ftpfs_entry *e = NULL;
951  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
952  bool verbose = me->verbose;
953  const struct timeval *timeout = &me->timeout;
954  const char *user = NULL;
955  const char *password = NULL;
956  const char *hostname = NULL;
957  const char *filename = NULL;
958  const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
959    ? "STOR "
960    : "RETR ";
961  uint32_t client_address = 0;
962  char *location = iop->pathinfo.node_access;
963
964  /* Invalidate data handle */
965  iop->data1 = NULL;
966
967  /* Check location, it was allocated during path evaluation */
968  if (location == NULL) {
969    rtems_set_errno_and_return_minus_one(ENOMEM);
970  }
971
972  /* Split location into parts */
973  ok = rtems_ftpfs_split_names(
974      location,
975      &user,
976      &password,
977      &hostname,
978      &filename
979  );
980  if (!ok) {
981    if (strlen(location) == 0) {
982      /*
983       * This is an access to the root node that will be used for file system
984       * option settings.
985       */
986      iop->pathinfo.handlers = &rtems_ftpfs_root_handlers;
987
988      return 0;
989    } else {
990      rtems_set_errno_and_return_minus_one(ENOENT);
991    }
992  }
993  DEBUG_PRINTF(
994    "user = '%s', password = '%s', filename = '%s'\n",
995    user,
996    password,
997    filename
998  );
999
1000  /* Check for either read-only or write-only flags */
1001  if (
1002    (iop->flags & LIBIO_FLAGS_WRITE) != 0
1003      && (iop->flags & LIBIO_FLAGS_READ) != 0
1004  ) {
1005    rtems_set_errno_and_return_minus_one(ENOTSUP);
1006  }
1007
1008  /* Allocate connection entry */
1009  e = malloc(sizeof(*e));
1010  if (e == NULL) {
1011    rtems_set_errno_and_return_minus_one(ENOMEM);
1012  }
1013
1014  /* Initialize connection entry */
1015  e->ctrl_socket = -1;
1016  e->data_socket = -1;
1017  e->eof = false;
1018
1019  /* Save connection state */
1020  iop->data1 = e;
1021
1022  /* Open control connection */
1023  eno = rtems_ftpfs_open_ctrl_connection(
1024    e,
1025    user,
1026    password,
1027    hostname,
1028    &client_address,
1029    verbose,
1030    timeout
1031  );
1032  if (eno != 0) {
1033    goto cleanup;
1034  }
1035
1036  /* Open passive data connection */
1037  eno = rtems_ftpfs_open_data_connection_passive(
1038    e,
1039    client_address,
1040    file_command,
1041    filename,
1042    verbose,
1043    timeout
1044  );
1045  if (eno == ENOTSUP) {
1046    /* Open active data connection */
1047    eno = rtems_ftpfs_open_data_connection_active(
1048      e,
1049      client_address,
1050      file_command,
1051      filename,
1052      verbose,
1053      timeout
1054    );
1055  }
1056  if (eno != 0) {
1057    goto cleanup;
1058  }
1059
1060  /* Set data connection timeout */
1061  eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout);
1062
1063cleanup:
1064
1065  if (eno == 0) {
1066    return 0;
1067  } else {
1068    /* Free all resources if an error occured */
1069    rtems_ftpfs_terminate(iop, true);
1070
1071    rtems_set_errno_and_return_minus_one(eno);
1072  }
1073}
1074
1075static ssize_t rtems_ftpfs_read(
1076  rtems_libio_t *iop,
1077  void *buffer,
1078  size_t count
1079)
1080{
1081  rtems_ftpfs_entry *e = iop->data1;
1082  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1083  bool verbose = me->verbose;
1084  char *in = buffer;
1085  size_t todo = count;
1086
1087  if (e->eof) {
1088    return 0;
1089  }
1090
1091  while (todo > 0) {
1092    ssize_t rv = recv(e->data_socket, in, todo, 0);
1093
1094    if (rv <= 0) {
1095      if (rv == 0) {
1096        rtems_ftpfs_reply reply =
1097          rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose);
1098
1099        if (reply == RTEMS_FTPFS_REPLY_2) {
1100          e->eof = true;
1101          break;
1102        }
1103      }
1104
1105      rtems_set_errno_and_return_minus_one(EIO);
1106    }
1107
1108    in += rv;
1109    todo -= (size_t) rv;
1110  }
1111
1112  return (ssize_t) (count - todo);
1113}
1114
1115static ssize_t rtems_ftpfs_write(
1116  rtems_libio_t *iop,
1117  const void *buffer,
1118  size_t count
1119)
1120{
1121  rtems_ftpfs_entry *e = iop->data1;
1122  const char *out = buffer;
1123  size_t todo = count;
1124
1125  while (todo > 0) {
1126    ssize_t rv = send(e->data_socket, out, todo, 0);
1127
1128    if (rv <= 0) {
1129      if (rv == 0) {
1130        break;
1131      } else {
1132        rtems_set_errno_and_return_minus_one(EIO);
1133      }
1134    }
1135
1136    out += rv;
1137    todo -= (size_t) rv;
1138  }
1139
1140  return (ssize_t) (count - todo);
1141}
1142
1143static int rtems_ftpfs_close(rtems_libio_t *iop)
1144{
1145  int eno = rtems_ftpfs_terminate(iop, false);
1146
1147  if (eno == 0) {
1148    return 0;
1149  } else {
1150    rtems_set_errno_and_return_minus_one(eno);
1151  }
1152}
1153
1154/* Dummy version to let fopen(*,"w") work properly */
1155static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, rtems_off64_t count)
1156{
1157  return 0;
1158}
1159
1160static int rtems_ftpfs_eval_path(
1161  const char *pathname,
1162  size_t pathnamelen,
1163  int flags,
1164  rtems_filesystem_location_info_t *pathloc
1165)
1166{
1167  /*
1168   * The caller of this routine has striped off the mount prefix from the path.
1169   * We need to store this path here or otherwise we would have to do this job
1170   * again.  The path is used in rtems_ftpfs_open() via iop->pathinfo.node_access.
1171   */
1172  char *pathname_dup = malloc(pathnamelen + 1);
1173
1174  if (pathname_dup != NULL) {
1175    memcpy(pathname_dup, pathname, pathnamelen);
1176    pathname_dup [pathnamelen] = '\0';
1177  }
1178
1179  pathloc->node_access = pathname_dup;
1180
1181  return 0;
1182}
1183
1184static int rtems_ftpfs_free_node(rtems_filesystem_location_info_t *pathloc)
1185{
1186  free(pathloc->node_access);
1187
1188  return 0;
1189}
1190
1191static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1192  rtems_filesystem_location_info_t *pathloc
1193)
1194{
1195  return RTEMS_FILESYSTEM_MEMORY_FILE;
1196}
1197
1198int rtems_ftpfs_initialize(
1199  rtems_filesystem_mount_table_entry_t *e,
1200  const void                           *d
1201)
1202{
1203  rtems_ftpfs_mount_entry *me = malloc(sizeof(rtems_ftpfs_mount_entry));
1204
1205  /* Mount entry for FTP file system instance */
1206  e->fs_info = me;
1207  if (e->fs_info == NULL) {
1208    rtems_set_errno_and_return_minus_one(ENOMEM);
1209  }
1210  me->verbose = false;
1211  me->timeout.tv_sec = 0;
1212  me->timeout.tv_usec = 0;
1213
1214  /* Set handler and oparations table */
1215  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1216  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1217
1218  /* We maintain no real file system nodes, so there is no real root */
1219  e->mt_fs_root.node_access = NULL;
1220
1221  /* Just use the limits from IMFS */
1222  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1223
1224  return 0;
1225}
1226
1227static int rtems_ftpfs_unmount_me(
1228  rtems_filesystem_mount_table_entry_t *e
1229)
1230{
1231  free(e->fs_info);
1232
1233  return 0;
1234}
1235
1236static int rtems_ftpfs_ioctl(
1237  rtems_libio_t *iop,
1238  uint32_t command,
1239  void *arg
1240)
1241{
1242  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1243  bool *verbose = arg;
1244  struct timeval *timeout = arg;
1245
1246  if (arg == NULL) {
1247    rtems_set_errno_and_return_minus_one(EINVAL);
1248  }
1249
1250  switch (command) {
1251    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
1252      *verbose = me->verbose;
1253      break;
1254    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
1255      me->verbose = *verbose;
1256      break;
1257    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
1258      *timeout = me->timeout;
1259      break;
1260    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
1261      me->timeout = *timeout;
1262      break;
1263    default:
1264      rtems_set_errno_and_return_minus_one(EINVAL);
1265  }
1266
1267  return 0;
1268}
1269
1270/*
1271 * The stat() support is intended only for the cp shell command.  Each request
1272 * will return that we have a regular file with read, write and execute
1273 * permissions for every one.  The node index uses a global counter to support
1274 * a remote to remote copy.  This is not a very sophisticated method.
1275 */
1276static int rtems_ftpfs_fstat(
1277  rtems_filesystem_location_info_t *loc,
1278  struct stat *st
1279)
1280{
1281  static unsigned ino = 0;
1282
1283  memset(st, 0, sizeof(*st));
1284
1285  /* FIXME */
1286  st->st_ino = ++ino;
1287  st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU);
1288
1289  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1290
1291  return 0;
1292}
1293
1294static const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1295  .evalpath_h = rtems_ftpfs_eval_path,
1296  .evalformake_h = rtems_filesystem_default_evalformake,
1297  .link_h = rtems_filesystem_default_link,
1298  .unlink_h = rtems_filesystem_default_unlink,
1299  .node_type_h = rtems_ftpfs_node_type,
1300  .mknod_h = rtems_filesystem_default_mknod,
1301  .chown_h = rtems_filesystem_default_chown,
1302  .freenod_h = rtems_ftpfs_free_node,
1303  .mount_h = rtems_filesystem_default_mount,
1304  .fsmount_me_h = rtems_ftpfs_initialize,
1305  .unmount_h = rtems_filesystem_default_unmount,
1306  .fsunmount_me_h = rtems_ftpfs_unmount_me,
1307  .utime_h = rtems_filesystem_default_utime,
1308  .eval_link_h = rtems_filesystem_default_evaluate_link,
1309  .symlink_h = rtems_filesystem_default_symlink,
1310  .readlink_h = rtems_filesystem_default_readlink
1311};
1312
1313static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1314  .open_h = rtems_ftpfs_open,
1315  .close_h = rtems_ftpfs_close,
1316  .read_h = rtems_ftpfs_read,
1317  .write_h = rtems_ftpfs_write,
1318  .ioctl_h = rtems_filesystem_default_ioctl,
1319  .lseek_h = rtems_filesystem_default_lseek,
1320  .fstat_h = rtems_ftpfs_fstat,
1321  .fchmod_h = rtems_filesystem_default_fchmod,
1322  .ftruncate_h = rtems_ftpfs_ftruncate,
1323  .fpathconf_h = rtems_filesystem_default_fpathconf,
1324  .fsync_h = rtems_filesystem_default_fsync,
1325  .fdatasync_h = rtems_filesystem_default_fdatasync,
1326  .fcntl_h = rtems_filesystem_default_fcntl,
1327  .rmnod_h = rtems_filesystem_default_rmnod
1328};
1329
1330static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
1331  .open_h = rtems_filesystem_default_open,
1332  .close_h = rtems_filesystem_default_close,
1333  .read_h = rtems_filesystem_default_read,
1334  .write_h = rtems_filesystem_default_write,
1335  .ioctl_h = rtems_ftpfs_ioctl,
1336  .lseek_h = rtems_filesystem_default_lseek,
1337  .fstat_h = rtems_filesystem_default_fstat,
1338  .fchmod_h = rtems_filesystem_default_fchmod,
1339  .ftruncate_h = rtems_filesystem_default_ftruncate,
1340  .fpathconf_h = rtems_filesystem_default_fpathconf,
1341  .fsync_h = rtems_filesystem_default_fsync,
1342  .fdatasync_h = rtems_filesystem_default_fdatasync,
1343  .fcntl_h = rtems_filesystem_default_fcntl,
1344  .rmnod_h = rtems_filesystem_default_rmnod
1345};
Note: See TracBrowser for help on using the repository browser.