source: rtems/c/src/lib/libnetworking/lib/tftpDriver.c @ 48abdc31

4.104.114.84.95
Last change on this file since 48abdc31 was 48abdc31, checked in by Joel Sherrill <joel.sherrill@…>, on 08/23/99 at 14:58:16

Patch from Eric Norum <eric@…> to readd the behavior where
the minor number indicated the port number to try.

  • Property mode set to 100644
File size: 16.0 KB
Line 
1/*
2 * Trivial File Transfer Protocol (RFC 1350)
3 *
4 * Transfer file to/from remote host
5 *
6 * W. Eric Norum
7 * Saskatchewan Accelerator Laboratory
8 * University of Saskatchewan
9 * Saskatoon, Saskatchewan, CANADA
10 * eric@skatter.usask.ca
11 *
12 *  $Id$
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <malloc.h>
19#include <string.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <rtems.h>
23#include <rtems/libio.h>
24#include <rtems/rtems_bsdnet.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#ifndef set_errno_and_return_minus_one
31#define set_errno_and_return_minus_one( _error ) \
32  do { errno = (_error); return -1; } while(0)
33#endif
34
35
36/*
37 * Range of UDP ports to try
38 */
39#define UDP_PORT_BASE   3180
40
41/*
42 * Pathname prefix
43 */
44#define TFTP_PATHNAME_PREFIX    "/TFTP/"
45
46/*
47 * Default limits
48 */
49#define PACKET_REPLY_MILLISECONDS       6000
50#define OPEN_RETRY_LIMIT        10
51#define IO_RETRY_LIMIT          10
52
53/*
54 * TFTP opcodes
55 */
56#define TFTP_OPCODE_RRQ         1
57#define TFTP_OPCODE_WRQ         2
58#define TFTP_OPCODE_DATA        3
59#define TFTP_OPCODE_ACK         4
60#define TFTP_OPCODE_ERROR       5
61
62/*
63 * Largest data transfer
64 */
65#define TFTP_BUFSIZE    512
66
67/*
68 * Packets transferred between machines
69 */
70union tftpPacket {
71        /*
72         * RRQ/WRQ packet
73         */
74        struct tftpRWRQ {
75                rtems_unsigned16        opcode;
76                char                    filename_mode[TFTP_BUFSIZE];
77        } tftpRWRQ;
78
79        /*
80         * DATA packet
81         */
82        struct tftpDATA {
83                rtems_unsigned16        opcode;
84                rtems_unsigned16        blocknum;
85                rtems_unsigned8         data[TFTP_BUFSIZE];
86        } tftpDATA;
87
88        /*
89         * ACK packet
90         */
91        struct tftpACK {
92                rtems_unsigned16        opcode;
93                rtems_unsigned16        blocknum;
94        } tftpACK;
95
96        /*
97         * ERROR packet
98         */
99        struct tftpERROR {
100                rtems_unsigned16        opcode;
101                rtems_unsigned16        errorCode;
102                char                    errorMessage[TFTP_BUFSIZE];
103        } tftpERROR;
104};
105
106/*
107 * State of each TFTP stream
108 */
109struct tftpStream {
110        /*
111         * Buffer for storing most recently-received packet
112         */
113        union tftpPacket        pkbuf;
114
115        /*
116         * Last block number received
117         */
118        rtems_unsigned16        blocknum;
119
120        /*
121         * Data transfer socket
122         */
123        int                     socket;
124        struct sockaddr_in      myAddress;
125        struct sockaddr_in      farAddress;
126
127        /*
128         * Indices into buffer
129         */
130        int     nleft;
131        int     nused;
132
133        /*
134         * Flags
135         */
136        int     firstReply;
137        int     eof;
138};
139
140/*
141 * Number of streams open at the same time
142 */
143
144static rtems_id tftp_mutex;
145static int nStreams;
146static struct tftpStream ** volatile tftpStreams;
147
148typedef const char *tftp_node;
149extern rtems_filesystem_operations_table  rtems_tftp_ops;
150extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
151
152/*
153 *  Direct copy from the IMFS.  Look at this.
154 */
155
156rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
157   5,   /* link_max */
158   6,   /* max_canon */
159   7,   /* max_input */
160   255, /* name_max */
161   255, /* path_max */
162   2,   /* pipe_buf */
163   1,   /* posix_async_io */
164   2,   /* posix_chown_restrictions */
165   3,   /* posix_no_trunc */
166   4,   /* posix_prio_io */
167   5,   /* posix_sync_io */
168   6    /* posix_vdisable */
169};
170
171int rtems_tftp_mount_me(
172  rtems_filesystem_mount_table_entry_t *temp_mt_entry
173)
174{
175  rtems_status_code  sc;
176
177  temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
178  temp_mt_entry->mt_fs_root.ops      = &rtems_tftp_ops;
179
180  /*
181   *   We have no tftp filesystem specific data to maintain.  This
182   *   filesystem may only be mounted ONCE.
183   *
184   *   And we maintain no real filesystem nodes, so there is no real root.
185   */
186
187  temp_mt_entry->fs_info                = NULL;
188  temp_mt_entry->mt_fs_root.node_access = NULL;
189
190  /*
191   *  These need to be looked at for full POSIX semantics.
192   */
193
194  temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options;
195
196
197  /*
198   *  Now allocate a semaphore for mutual exclusion.
199   *
200   *  NOTE:  This could be in an fsinfo for this filesystem type.
201   */
202 
203  sc = rtems_semaphore_create (
204    rtems_build_name('T', 'F', 'T', 'P'),
205    1,
206    RTEMS_FIFO |
207    RTEMS_BINARY_SEMAPHORE |
208    RTEMS_NO_INHERIT_PRIORITY |
209    RTEMS_NO_PRIORITY_CEILING |
210    RTEMS_LOCAL,
211    0,
212    &tftp_mutex
213  );
214
215  if (sc != RTEMS_SUCCESSFUL)
216    set_errno_and_return_minus_one( ENOMEM );
217
218  return 0;
219}
220
221/*
222 * Initialize the TFTP driver
223 */
224
225int rtems_bsdnet_initialize_tftp_filesystem ()
226{
227 int                                   status;
228 rtems_filesystem_mount_table_entry_t *entry;
229
230 status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
231 if ( status == -1 )
232   return status;
233
234  status = mount(
235     &entry,
236     &rtems_tftp_ops,
237     RTEMS_FILESYSTEM_READ_ONLY,
238     NULL,
239     TFTP_PATHNAME_PREFIX
240  );
241
242  if ( status )
243    perror( "TFTP mount failed" );
244
245  return status;
246}
247
248/*
249 * Set error message
250 * This RTEMS/UNIX error mapping needs to be fixed!
251 */
252static void
253tftpSetErrno (struct tftpStream *tp)
254{
255        unsigned int tftpError;
256        static const int errorMap[] = {
257                0,
258                ENOENT,
259                EPERM,
260                ENOSPC,
261                EINVAL,
262                ENXIO,
263                EEXIST,
264                ESRCH,
265                0,
266        };
267
268        tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
269        if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
270                errno = errorMap[tftpError];
271        else
272                errno = 1000 + tftpError;
273}
274
275/*
276 * Send a message to make the other end shut up
277 */
278static void
279sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
280{
281        int len;
282
283        /*
284         * Create the error packet (Unknown transfer ID).
285         */
286        tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
287        tp->pkbuf.tftpERROR.errorCode = htons (5);
288        len = sizeof tp->pkbuf.tftpERROR.opcode +
289                                sizeof tp->pkbuf.tftpERROR.errorCode + 1;
290        len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
291
292        /*
293         * Send it
294         */
295        sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
296                                        (struct sockaddr *)to, sizeof *to);
297}
298
299/*
300 * Wait for a data packet
301 */
302static int
303getPacket (struct tftpStream *tp)
304{
305        int len;
306        struct timeval tv;
307
308        tv.tv_sec = 6;
309        tv.tv_usec = 0;
310        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
311        for (;;) {
312                union {
313                        struct sockaddr s;
314                        struct sockaddr_in i;
315                } from;
316                int fromlen = sizeof from;
317                len = recvfrom (tp->socket, (char *)&tp->pkbuf,
318                                                        sizeof tp->pkbuf, 0,
319                                                        &from.s, &fromlen);
320                if (len < 0)
321                        break;
322                if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
323                        if (tp->firstReply) {
324                                tp->firstReply = 0;
325                                tp->farAddress.sin_port = from.i.sin_port;
326                        }
327                        if (tp->farAddress.sin_port == from.i.sin_port)
328                                break;
329                }
330
331                /*
332                 * Packet is from someone with whom we are
333                 * not interested.  Tell them to go away.
334                 */
335                sendStifle (tp, &from.i);
336        }
337        tv.tv_sec = 0;
338        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
339        return len;
340}
341
342/*
343 * Send an acknowledgement
344 */
345static int
346sendAck (struct tftpStream *tp)
347{
348        /*
349         * Create the acknowledgement
350         */
351        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
352        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
353
354        /*
355         * Send it
356         */
357        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
358                                        (struct sockaddr *)&tp->farAddress,
359                                        sizeof tp->farAddress) < 0)
360                return errno;
361        return 0;
362}
363
364/*
365 * Release a stream and clear the pointer to it
366 */
367static void
368releaseStream (int s)
369{
370        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
371        free (tftpStreams[s]);
372        tftpStreams[s] = NULL;
373        rtems_semaphore_release (tftp_mutex);
374}
375
376int rtems_tftp_evaluate_for_make(
377   const char                         *path,       /* IN     */
378   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
379   const char                        **name        /* OUT    */
380)
381
382  set_errno_and_return_minus_one( EIO );   
383}
384
385/*
386 * XXX - Fix return values.
387 */
388
389int rtems_tftp_eval_path( 
390  const char                        *pathname,     /* IN     */
391  int                                flags,        /* IN     */
392  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
393)
394{
395
396  /*
397   * Read-only for now
398   */
399   
400  if ( (flags & O_WRONLY) == O_WRONLY )
401    set_errno_and_return_minus_one( ENOENT );
402
403  /*
404   * The File system is mounted at TFTP_PATHNAME_PREFIX
405   * the caller of this routine has striped off this part of the
406   * name. Save the remainder of the name for use by the open routine.
407   */
408
409  pathloc->node_access = (void * ) pathname;
410  pathloc->handlers    = &rtems_tftp_handlers;
411
412  return 0;
413}
414
415
416int rtems_tftp_open(
417  rtems_libio_t *iop,
418  const char    *new_name,
419  unsigned32     flag,
420  unsigned32     mode
421)
422{
423  struct tftpStream  *tp;
424  int                 retryCount;
425  rtems_unsigned32    farAddress;
426  int                 s;
427  int                 len;
428  char               *cp1;
429  char               *cp2;
430  char               *remoteFilename;
431  rtems_interval      now;
432  rtems_status_code   sc;
433  char               *hostname;
434
435  /*
436   * This came from the evaluate path.
437   */
438
439  cp2 = iop->file_info; 
440
441  cp1 = cp2;
442  while (*cp2 != '/') {
443    if (*cp2 == '\0')
444      return ENOENT;
445    cp2++;
446  }
447
448  len = cp2 - cp1;
449  hostname = malloc (len + 1);
450  if (hostname == NULL)
451    return ENOMEM;
452
453  strncpy (hostname, cp1, len);
454  hostname[len] = '\0';
455  farAddress = inet_addr (hostname);
456  free (hostname);
457
458  if ((farAddress == 0) || (farAddress == ~0))
459    return ENOENT;
460
461  if (*++cp2 == '\0')
462    return ENOENT;
463
464  remoteFilename = cp2;
465  if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
466    return ENOENT;   
467
468  /*
469   * Find a free stream
470   */
471
472  sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
473  if (sc != RTEMS_SUCCESSFUL)
474    return EBUSY;
475
476  for (s = 0 ; s < nStreams ; s++) {
477    if (tftpStreams[s] == NULL)
478    break;
479  }
480
481  if (s == nStreams) {
482    /*
483     * Reallocate stream pointers
484     * Guard against the case where realloc() returns NULL.
485     */
486    struct tftpStream **np;
487
488    np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
489    if (np == NULL) {
490      rtems_semaphore_release (tftp_mutex);
491      return ENOMEM;
492    }
493    tftpStreams = np;
494  }
495
496  tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
497  rtems_semaphore_release (tftp_mutex);
498  if (tp == NULL)
499    return ENOMEM;
500  iop->data0 = s;
501  iop->data1 = tp;
502
503  /*
504   * Create the socket
505   */
506
507  if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
508    releaseStream (s);
509    return ENOMEM;
510  }
511
512  /*
513   * Bind the socket to a local address
514   */
515
516  retryCount = 0;
517  rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
518  for (;;) {
519    int try = (now + retryCount) % 10;
520
521    tp->myAddress.sin_family = AF_INET;
522    tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
523    tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
524    if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
525      break;
526    if (++retryCount == 10) {
527      close (tp->socket);
528      releaseStream (s);
529      return EBUSY;
530    }
531  }
532
533  /*
534   * Set the UDP destination to the TFTP server
535   * port on the remote machine.
536   */
537  tp->farAddress.sin_family = AF_INET;
538  tp->farAddress.sin_addr.s_addr = farAddress;
539  tp->farAddress.sin_port = htons (69);
540
541  /*
542   * Start the transfer
543   */
544  tp->firstReply = 1;
545  for (;;) {
546    /*
547     * Create the request
548     */
549    tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
550    cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
551    cp2 = (char *) remoteFilename;
552    while ((*cp1++ = *cp2++) != '\0')
553      continue;
554    cp2 = "octet";
555    while ((*cp1++ = *cp2++) != '\0')
556      continue;
557    len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
558
559    /*
560     * Send the request
561     */
562    if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
563          (struct sockaddr *)&tp->farAddress,
564          sizeof tp->farAddress) < 0) {
565      close (tp->socket);
566      releaseStream (s);
567      return EIO;
568    }
569
570    /*
571     * Get reply
572     */
573    len = getPacket (tp);
574    if (len >= (int) sizeof tp->pkbuf.tftpACK) {
575      int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
576      if ((opcode == TFTP_OPCODE_DATA)
577       && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
578        tp->nused = 0;
579        tp->blocknum = 1;
580        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
581        tp->eof = (tp->nleft < TFTP_BUFSIZE);
582        if (sendAck (tp) != 0) {
583          close (tp->socket);
584          releaseStream (s);
585          return EIO;
586        }
587        break;
588      }
589      if (opcode == TFTP_OPCODE_ERROR) {
590        tftpSetErrno (tp);
591        close (tp->socket);
592        releaseStream (s);
593        return EIO;
594      }
595    }
596
597    /*
598     * Keep trying
599     */
600    if (++retryCount >= OPEN_RETRY_LIMIT) {
601      close (tp->socket);
602      releaseStream (s);
603      return EIO;
604    }
605  }
606
607  return 0;
608}
609
610/*
611 * Read from a TFTP stream
612 */
613
614int rtems_tftp_read(
615  rtems_libio_t *iop,
616  void          *buffer,
617  unsigned32     count
618)
619{
620  char              *bp;
621  struct tftpStream *tp;
622  int                retryCount;
623  int                nwant;
624
625  tp = iop->data1;
626
627  /*
628   * Read till user request is satisfied or EOF is reached
629   */
630
631  bp = buffer;
632  nwant = count;
633  while (nwant) {
634    if (tp->nleft) {
635      int count;
636      if (nwant < tp->nleft)
637        count = nwant;
638      else
639        count = tp->nleft;
640      memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
641      tp->nused += count;
642      tp->nleft -= count;
643      bp += count;
644      nwant -= count;
645      if (nwant == 0)
646        break;
647    }
648    if (tp->eof)
649      break;
650
651    /*
652     * Wait for the next packet
653     */
654    retryCount = 0;
655    for (;;) {
656      int len = getPacket (tp);
657      if (len >= (int)sizeof tp->pkbuf.tftpACK) {
658        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
659        rtems_unsigned16 nextBlock = tp->blocknum + 1;
660        if ((opcode == TFTP_OPCODE_DATA)
661         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
662          tp->nused = 0;
663          tp->nleft = len - 2 * sizeof (rtems_unsigned16);
664          tp->eof = (tp->nleft < TFTP_BUFSIZE);
665          tp->blocknum++;
666          if (sendAck (tp) != 0)
667            set_errno_and_return_minus_one( EIO );
668          break;
669        }
670        if (opcode == TFTP_OPCODE_ERROR) {
671          tftpSetErrno (tp);
672          return RTEMS_INTERNAL_ERROR;
673        }
674      }
675
676      /*
677       * Keep trying?
678       */
679      if (++retryCount == IO_RETRY_LIMIT)
680        set_errno_and_return_minus_one( EIO );
681      if (sendAck (tp) != 0)
682        set_errno_and_return_minus_one( EIO );
683    }
684  }
685
686 /*
687  * XXX - Eric is this right?
688  *
689  */
690  return count - nwant;
691}
692
693/*
694 * Close a TFTP stream
695 */
696int rtems_tftp_close(
697  rtems_libio_t *iop
698)
699{
700  struct tftpStream *tp = iop->data1;;
701
702  if (!tp->eof && !tp->firstReply) {
703    /*
704     * Tell the other end to stop
705     */
706    rtems_interval ticksPerSecond;
707    sendStifle (tp, &tp->farAddress);
708    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
709    rtems_task_wake_after (1 + ticksPerSecond / 10);
710  }
711  close (tp->socket);
712  releaseStream (iop->data0);
713  return RTEMS_SUCCESSFUL;
714}
715
716int rtems_tftp_write(
717  rtems_libio_t *iop,
718  const void    *buffer,
719  unsigned32     count
720)
721{
722  return RTEMS_NOT_CONFIGURED;
723}
724
725rtems_device_driver rtems_tftp_control(
726  rtems_device_major_number major,
727  rtems_device_minor_number minor,
728  void *pargp
729)
730{
731  return RTEMS_NOT_CONFIGURED;
732}
733
734rtems_filesystem_node_types_t rtems_tftp_node_type(
735   rtems_filesystem_location_info_t    *pathloc         /* IN */
736)
737{
738  return RTEMS_FILESYSTEM_MEMORY_FILE;
739}
740
741
742rtems_filesystem_operations_table  rtems_tftp_ops = {
743  rtems_tftp_eval_path,            /* eval_path */
744  rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
745  NULL,                            /* link */
746  NULL,                            /* unlink */
747  rtems_tftp_node_type,            /* node_type */
748  NULL,                            /* mknod */
749  NULL,                            /* rmnod */
750  NULL,                            /* chown */
751  NULL,                            /* freenodinfo */
752  NULL,                            /* mount */
753  rtems_tftp_mount_me,             /* initialize */
754  NULL,                            /* unmount */
755  NULL,                            /* fsunmount */
756  NULL,                            /* utime, */
757  NULL,                            /* evaluate_link */
758  NULL,                            /* symlink */
759  NULL,                            /* readlin */
760};
761
762rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
763  rtems_tftp_open,
764  rtems_tftp_close,
765  rtems_tftp_read,
766  rtems_tftp_write,
767  NULL,
768  NULL,
769  NULL,
770  NULL,
771  NULL,
772  NULL,
773  NULL,
774  NULL,
775};
Note: See TracBrowser for help on using the repository browser.