source: rtems/c/src/lib/libnetworking/lib/tftpDriver.c @ 39e6e65a

4.104.114.84.95
Last change on this file since 39e6e65a was 39e6e65a, checked in by Joel Sherrill <joel.sherrill@…>, on 08/19/98 at 21:32:28

Base files

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