source: rtems/c/src/libnetworking/lib/tftpDriver.c @ 7c7fd4d

4.104.114.84.95
Last change on this file since 7c7fd4d was 94b3ee13, checked in by Joel Sherrill <joel.sherrill@…>, on 01/21/99 at 22:25:43

Made to compile after hacking tftp driver into beginnings of a mini-filesystem.

  • Property mode set to 100644
File size: 13.9 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 <rtems.h>
22#include <rtems/libio.h>
23#include <rtems/rtems_bsdnet.h>
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
28
29#ifndef set_errno_and_return_minus_one
30#define set_errno_and_return_minus_one( _error ) \
31  do { errno = (_error); return -1; } while(0)
32#endif
33
34
35/*
36 * Range of UDP ports to try
37 */
38#define UDP_PORT_BASE   3180
39
40/*
41 * Pathname prefix
42 */
43#define TFTP_PATHNAME_PREFIX    "/TFTP/"
44
45/*
46 * Default limits
47 */
48#define PACKET_REPLY_MILLISECONDS       6000
49#define OPEN_RETRY_LIMIT        10
50#define IO_RETRY_LIMIT          10
51
52/*
53 * TFTP opcodes
54 */
55#define TFTP_OPCODE_RRQ         1
56#define TFTP_OPCODE_WRQ         2
57#define TFTP_OPCODE_DATA        3
58#define TFTP_OPCODE_ACK         4
59#define TFTP_OPCODE_ERROR       5
60
61/*
62 * Largest data transfer
63 */
64#define TFTP_BUFSIZE    512
65
66/*
67 * Packets transferred between machines
68 */
69union tftpPacket {
70        /*
71         * RRQ/WRQ packet
72         */
73        struct tftpRWRQ {
74                rtems_unsigned16        opcode;
75                char                    filename_mode[TFTP_BUFSIZE];
76        } tftpRWRQ;
77
78        /*
79         * DATA packet
80         */
81        struct tftpDATA {
82                rtems_unsigned16        opcode;
83                rtems_unsigned16        blocknum;
84                rtems_unsigned8         data[TFTP_BUFSIZE];
85        } tftpDATA;
86
87        /*
88         * ACK packet
89         */
90        struct tftpACK {
91                rtems_unsigned16        opcode;
92                rtems_unsigned16        blocknum;
93        } tftpACK;
94
95        /*
96         * ERROR packet
97         */
98        struct tftpERROR {
99                rtems_unsigned16        opcode;
100                rtems_unsigned16        errorCode;
101                char                    errorMessage[TFTP_BUFSIZE];
102        } tftpERROR;
103};
104
105/*
106 * State of each TFTP stream
107 */
108struct tftpStream {
109        /*
110         * Buffer for storing most recently-received packet
111         */
112        union tftpPacket        pkbuf;
113
114        /*
115         * Last block number received
116         */
117        rtems_unsigned16        blocknum;
118
119        /*
120         * Data transfer socket
121         */
122        int                     socket;
123        struct sockaddr_in      myAddress;
124        struct sockaddr_in      farAddress;
125
126        /*
127         * Indices into buffer
128         */
129        int     nleft;
130        int     nused;
131
132        /*
133         * Flags
134         */
135        int     firstReply;
136        int     eof;
137};
138
139/*
140 * Number of streams open at the same time
141 */
142static rtems_id tftp_mutex;
143static int nStreams;
144static struct tftpStream ** volatile tftpStreams;
145
146typedef struct {
147   rtems_id                             tftp_mutex;
148   int                                  nStreams;
149   struct tftpStream ** volatile        tftpStreams;
150  rtems_filesystem_mount_table_entry_t *mt_entry; 
151}  tftp_fs_info;
152
153int rtems_tftp_mount_me(
154  rtems_filesystem_mount_table_entry_t *temp_mt_entry
155)
156{
157  tftp_fs_info      *fs_info;
158  rtems_status_code  sc;
159
160  /*
161   * Allocate stuff for this file system.
162   */
163
164  fs_info = calloc( 1, sizeof( tftp_fs_info ));
165  if ( !fs_info )
166    set_errno_and_return_minus_one( ENOMEM );
167
168  temp_mt_entry->fs_info                = fs_info;
169  temp_mt_entry->mt_fs_root.node_access = fs_info;
170
171 
172  sc = rtems_semaphore_create (
173    rtems_build_name('T', 'F', 'T', 'P'),
174    1,
175    RTEMS_FIFO |
176    RTEMS_BINARY_SEMAPHORE |
177    RTEMS_NO_INHERIT_PRIORITY |
178    RTEMS_NO_PRIORITY_CEILING |
179    RTEMS_LOCAL,
180    0,
181    &fs_info->tftp_mutex
182  );
183
184  if (sc != RTEMS_SUCCESSFUL)
185    set_errno_and_return_minus_one( ENOMEM ); /* ??? */
186
187  return 0;
188}
189
190/*
191 * Initialize the TFTP driver
192 */
193/* XXX change name to rtems_bsdnet_initialize_tftp_filesystem ("mountpt") */
194/* XXX this won't be a driver when we are finished */
195rtems_device_driver rtems_tftp_initialize(
196  rtems_device_major_number major,
197  rtems_device_minor_number minor,
198  void *pargp
199)
200{
201
202/* XXX change to a mount */
203        rtems_io_register_name (TFTP_PATHNAME_PREFIX, major, minor);
204        return RTEMS_SUCCESSFUL;
205}
206
207/*
208 * Set error message
209 * This RTEMS/UNIX error mapping needs to be fixed!
210 */
211static void
212tftpSetErrno (struct tftpStream *tp)
213{
214        unsigned int tftpError;
215        static const int errorMap[] = {
216                0,
217                ENOENT,
218                EPERM,
219                ENOSPC,
220                EINVAL,
221                ENXIO,
222                EEXIST,
223                ESRCH,
224                0,
225        };
226
227        tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
228        if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
229                errno = errorMap[tftpError];
230        else
231                errno = 1000 + tftpError;
232}
233
234/*
235 * Send a message to make the other end shut up
236 */
237static void
238sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
239{
240        int len;
241
242        /*
243         * Create the error packet (Unknown transfer ID).
244         */
245        tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
246        tp->pkbuf.tftpERROR.errorCode = htons (5);
247        len = sizeof tp->pkbuf.tftpERROR.opcode +
248                                sizeof tp->pkbuf.tftpERROR.errorCode + 1;
249        len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
250
251        /*
252         * Send it
253         */
254        sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
255                                        (struct sockaddr *)to, sizeof *to);
256}
257
258/*
259 * Wait for a data packet
260 */
261static int
262getPacket (struct tftpStream *tp)
263{
264        int len;
265        struct timeval tv;
266
267        tv.tv_sec = 6;
268        tv.tv_usec = 0;
269        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
270        for (;;) {
271                union {
272                        struct sockaddr s;
273                        struct sockaddr_in i;
274                } from;
275                int fromlen = sizeof from;
276                len = recvfrom (tp->socket, (char *)&tp->pkbuf,
277                                                        sizeof tp->pkbuf, 0,
278                                                        &from.s, &fromlen);
279                if (len < 0)
280                        break;
281                if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
282                        if (tp->firstReply) {
283                                tp->firstReply = 0;
284                                tp->farAddress.sin_port = from.i.sin_port;
285                        }
286                        if (tp->farAddress.sin_port == from.i.sin_port)
287                                break;
288                }
289
290                /*
291                 * Packet is from someone with whom we are
292                 * not interested.  Tell them to go away.
293                 */
294                sendStifle (tp, &from.i);
295        }
296        tv.tv_sec = 0;
297        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
298        return len;
299}
300
301/*
302 * Send an acknowledgement
303 */
304static int
305sendAck (struct tftpStream *tp)
306{
307        /*
308         * Create the acknowledgement
309         */
310        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
311        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
312
313        /*
314         * Send it
315         */
316        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
317                                        (struct sockaddr *)&tp->farAddress,
318                                        sizeof tp->farAddress) < 0)
319                return errno;
320        return 0;
321}
322
323/*
324 * Release a stream and clear the pointer to it
325 */
326static void
327releaseStream (int s)
328{
329        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
330        free (tftpStreams[s]);
331        tftpStreams[s] = NULL;
332        rtems_semaphore_release (tftp_mutex);
333}
334
335int rtems_tftp_evaluate_for_make(
336   const char                         *path,       /* IN     */
337   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
338   const char                        **name        /* OUT    */
339)
340
341    set_errno_and_return_minus_one( EIO );   
342}
343/*
344 * XXX - Fix return values.
345 */
346
347int TFTP_eval_path( 
348  const char                        *pathname,     /* IN     */
349  int                                flags,        /* IN     */
350  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
351)
352{
353        rtems_unsigned32 farAddress;
354        int len;
355        const char *remoteFilename;
356
357  tftp_fs_info      *fs_info;
358  const char *cp1, *cp2;
359
360  fs_info = pathloc->node_access;
361
362        /*
363         * Pick apart the name into a host:pathname pair
364         */
365        /*
366         * XXX - I think this is handled by the caller of
367         *       the evaluate routine. ? Am I starting at the right
368         *       place?
369         */
370
371        cp2 = pathname+1;
372
373        if (*cp2 == '/') {
374                farAddress = rtems_bsdnet_bootp_server_address.s_addr;
375        }
376        else {
377                char *hostname;
378
379                cp1 = cp2;
380                while (*cp2 != '/') {
381                        if (*cp2 == '\0')
382                                return RTEMS_INVALID_NAME;
383                        cp2++;
384                }
385                len = cp2 - cp1;
386                hostname = malloc (len + 1);
387                if (hostname == NULL)
388                        return RTEMS_NO_MEMORY;
389                strncpy (hostname, cp1, len);
390                hostname[len] = '\0';
391                farAddress = inet_addr (hostname);
392                free (hostname);
393        }
394        if ((farAddress == 0) || (farAddress == ~0))
395                return RTEMS_INVALID_NAME;
396        if (*++cp2 == '\0')
397                return RTEMS_INVALID_NAME;
398        remoteFilename = cp2;
399        if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
400                return RTEMS_INVALID_NAME;
401
402  return 0;
403}
404
405/*
406 * Open a TFTP stream
407 */
408rtems_device_driver rtems_tftp_open(
409  rtems_device_major_number major,
410  rtems_device_minor_number minor,
411  void *pargp
412)
413{
414        rtems_libio_open_close_args_t *ap = pargp;
415        struct tftpStream *tp;
416        int retryCount;
417        rtems_unsigned32 farAddress = 0; /* XXX - node parameter */
418        int s;
419        int len;
420        char *cp1, *cp2;
421        char *remoteFilename = NULL; /* XXX - node parameter */
422        rtems_interval now;
423        rtems_status_code sc;
424
425        /*
426         * Read-only for now
427         */
428        /* XXX - Move to the open routine */
429        if (ap->flags & LIBIO_FLAGS_WRITE)
430                return RTEMS_NOT_IMPLEMENTED;
431
432
433        /*
434         * Find a free stream
435         */
436        sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
437        if (sc != RTEMS_SUCCESSFUL)
438                return sc;
439        for (s = 0 ; s < nStreams ; s++) {
440                if (tftpStreams[s] == NULL)
441                        break;
442        }
443        if (s == nStreams) {
444                /*
445                 * Reallocate stream pointers
446                 * Guard against the case where realloc() returns NULL.
447                 */
448                struct tftpStream **np;
449
450                np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
451                if (np == NULL) {
452                        rtems_semaphore_release (tftp_mutex);
453                        return RTEMS_NO_MEMORY;
454                }
455                tftpStreams = np;
456        }
457        tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
458        rtems_semaphore_release (tftp_mutex);
459        if (tp == NULL)
460                return RTEMS_NO_MEMORY;
461        ap->iop->data0 = s;
462        ap->iop->data1 = tp;
463
464        /*
465         * Create the socket
466         */
467        if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
468                releaseStream (s);
469                return RTEMS_TOO_MANY;
470        }
471
472        /*
473         * Bind the socket to a local address
474         */
475        retryCount = 0;
476        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
477        for (;;) {
478                int try = (now + retryCount) % 10;
479
480                tp->myAddress.sin_family = AF_INET;
481                tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor);
482                tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
483                if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
484                        break;
485                if (++retryCount == 10) {
486                        close (tp->socket);
487                        releaseStream (minor);
488                        return RTEMS_RESOURCE_IN_USE;
489                }
490        }
491
492        /*
493         * Set the UDP destination to the TFTP server
494         * port on the remote machine.
495         */
496        tp->farAddress.sin_family = AF_INET;
497        tp->farAddress.sin_addr.s_addr = farAddress;
498        tp->farAddress.sin_port = htons (69);
499
500        /*
501         * Start the transfer
502         */
503        tp->firstReply = 1;
504        for (;;) {
505                /*
506                 * Create the request
507                 */
508                tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
509                cp1 = tp->pkbuf.tftpRWRQ.filename_mode;
510                cp2 = remoteFilename;
511                while ((*cp1++ = *cp2++) != '\0')
512                        continue;
513                cp2 = "octet";
514                while ((*cp1++ = *cp2++) != '\0')
515                        continue;
516                len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
517
518                /*
519                 * Send the request
520                 */
521                if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
522                                        (struct sockaddr *)&tp->farAddress,
523                                        sizeof tp->farAddress) < 0) {
524                        close (tp->socket);
525                        releaseStream (minor);
526                        return RTEMS_UNSATISFIED;
527                }
528
529                /*
530                 * Get reply
531                 */
532                len = getPacket (tp);
533                if (len >= (int) sizeof tp->pkbuf.tftpACK) {
534                        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
535                        if ((opcode == TFTP_OPCODE_DATA)
536                         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
537                                tp->nused = 0;
538                                tp->blocknum = 1;
539                                tp->nleft = len - 2 * sizeof (rtems_unsigned16);
540                                tp->eof = (tp->nleft < TFTP_BUFSIZE);
541                                if (sendAck (tp) != 0) {
542                                        close (tp->socket);
543                                        releaseStream (minor);
544                                        return RTEMS_UNSATISFIED;
545                                }
546                                break;
547                        }
548                        if (opcode == TFTP_OPCODE_ERROR) {
549                                tftpSetErrno (tp);
550                                close (tp->socket);
551                                releaseStream (ap->iop->data0);
552                                return RTEMS_INTERNAL_ERROR;
553                        }
554                }
555
556                /*
557                 * Keep trying
558                 */
559                if (++retryCount >= OPEN_RETRY_LIMIT) {
560                        close (tp->socket);
561                        releaseStream (minor);
562                        return RTEMS_UNSATISFIED;
563                }
564        }
565        return RTEMS_SUCCESSFUL;
566}
567
568/*
569 * Read from a TFTP stream
570 */
571rtems_device_driver rtems_tftp_read(
572  rtems_device_major_number major,
573  rtems_device_minor_number minor,
574  void *pargp
575)
576{
577        rtems_libio_rw_args_t *ap = pargp;
578        char *bp;
579        struct tftpStream *tp;
580        int retryCount;
581        int nwant;
582
583        tp = ap->iop->data1;
584
585        /*
586         * Read till user request is satisfied or EOF is reached
587         */
588        bp = ap->buffer;
589        nwant = ap->count;
590        while (nwant) {
591                if (tp->nleft) {
592                        int count;
593                        if (nwant < tp->nleft)
594                                count = nwant;
595                        else
596                                count = tp->nleft;
597                        memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
598                        tp->nused += count;
599                        tp->nleft -= count;
600                        bp += count;
601                        nwant -= count;
602                        if (nwant == 0)
603                                break;
604                }
605                if (tp->eof)
606                        break;
607
608                /*
609                 * Wait for the next packet
610                 */
611                retryCount = 0;
612                for (;;) {
613                        int len = getPacket (tp);
614                        if (len >= (int)sizeof tp->pkbuf.tftpACK) {
615                                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
616                                rtems_unsigned16 nextBlock = tp->blocknum + 1;
617                                if ((opcode == TFTP_OPCODE_DATA)
618                                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
619                                        tp->nused = 0;
620                                        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
621                                        tp->eof = (tp->nleft < TFTP_BUFSIZE);
622                                        tp->blocknum++;
623                                        if (sendAck (tp) != 0)
624                                                return RTEMS_IO_ERROR;
625                                        break;
626                                }
627                                if (opcode == TFTP_OPCODE_ERROR) {
628                                        tftpSetErrno (tp);
629                                        return RTEMS_INTERNAL_ERROR;
630                                }
631                        }
632
633                        /*
634                         * Keep trying?
635                         */
636                        if (++retryCount == IO_RETRY_LIMIT)
637                                return RTEMS_IO_ERROR;
638                        if (sendAck (tp) != 0)
639                                return RTEMS_IO_ERROR;
640                }
641        }
642        ap->bytes_moved = ap->count - nwant;
643        return RTEMS_SUCCESSFUL;
644}
645
646/*
647 * Close a TFTP stream
648 */
649rtems_device_driver rtems_tftp_close(
650  rtems_device_major_number major,
651  rtems_device_minor_number minor,
652  void *pargp
653)
654{
655        rtems_libio_open_close_args_t *ap = pargp;
656        struct tftpStream *tp = ap->iop->data1;;
657
658        if (!tp->eof && !tp->firstReply) {
659                /*
660                 * Tell the other end to stop
661                 */
662                rtems_interval ticksPerSecond;
663                sendStifle (tp, &tp->farAddress);
664                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
665                rtems_task_wake_after (1 + ticksPerSecond / 10);
666        }
667        close (tp->socket);
668        releaseStream (ap->iop->data0);
669        return RTEMS_SUCCESSFUL;
670}
671
672rtems_device_driver rtems_tftp_write(
673  rtems_device_major_number major,
674  rtems_device_minor_number minor,
675  void *pargp
676)
677{
678        return RTEMS_NOT_CONFIGURED;
679}
680
681rtems_device_driver rtems_tftp_control(
682  rtems_device_major_number major,
683  rtems_device_minor_number minor,
684  void *pargp
685)
686{
687        return RTEMS_NOT_CONFIGURED;
688}
Note: See TracBrowser for help on using the repository browser.