source: rtems/c/src/exec/libnetworking/lib/tftpDriver.c @ a7c4314d

4.104.114.84.95
Last change on this file since a7c4314d was a7c4314d, checked in by Joel Sherrill <joel.sherrill@…>, on 01/19/99 at 20:23:15

Added comments for changing to a filesystem

  • Property mode set to 100644
File size: 12.3 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 */
142/* XXX change name to rtems_bsdnet_initialize_tftp_filesystem ("mountpt") */
143/* XXX this won't be a driver when we are finished */
144rtems_device_driver rtems_tftp_initialize(
145  rtems_device_major_number major,
146  rtems_device_minor_number minor,
147  void *pargp
148)
149{
150        rtems_status_code sc;
151
152        sc = rtems_semaphore_create (rtems_build_name('T', 'F', 'T', 'P'),
153                                        1,
154                                        RTEMS_FIFO |
155                                                RTEMS_BINARY_SEMAPHORE |
156                                                RTEMS_NO_INHERIT_PRIORITY |
157                                                RTEMS_NO_PRIORITY_CEILING |
158                                                RTEMS_LOCAL,
159                                        0,
160                                        &tftp_mutex);
161        if (sc != RTEMS_SUCCESSFUL)
162                return sc;
163/* XXX change to a mount */
164        rtems_io_register_name (TFTP_PATHNAME_PREFIX, major, minor);
165        return RTEMS_SUCCESSFUL;
166}
167
168/*
169 * Set error message
170 * This RTEMS/UNIX error mapping needs to be fixed!
171 */
172static void
173tftpSetErrno (struct tftpStream *tp)
174{
175        unsigned int tftpError;
176        static const int errorMap[] = {
177                0,
178                ENOENT,
179                EPERM,
180                ENOSPC,
181                EINVAL,
182                ENXIO,
183                EEXIST,
184                ESRCH,
185                0,
186        };
187
188        tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
189        if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
190                errno = errorMap[tftpError];
191        else
192                errno = 1000 + tftpError;
193}
194
195/*
196 * Send a message to make the other end shut up
197 */
198static void
199sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
200{
201        int len;
202
203        /*
204         * Create the error packet (Unknown transfer ID).
205         */
206        tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
207        tp->pkbuf.tftpERROR.errorCode = htons (5);
208        len = sizeof tp->pkbuf.tftpERROR.opcode +
209                                sizeof tp->pkbuf.tftpERROR.errorCode + 1;
210        len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
211
212        /*
213         * Send it
214         */
215        sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
216                                        (struct sockaddr *)to, sizeof *to);
217}
218
219/*
220 * Wait for a data packet
221 */
222static int
223getPacket (struct tftpStream *tp)
224{
225        int len;
226        struct timeval tv;
227
228        tv.tv_sec = 6;
229        tv.tv_usec = 0;
230        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
231        for (;;) {
232                union {
233                        struct sockaddr s;
234                        struct sockaddr_in i;
235                } from;
236                int fromlen = sizeof from;
237                len = recvfrom (tp->socket, (char *)&tp->pkbuf,
238                                                        sizeof tp->pkbuf, 0,
239                                                        &from.s, &fromlen);
240                if (len < 0)
241                        break;
242                if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
243                        if (tp->firstReply) {
244                                tp->firstReply = 0;
245                                tp->farAddress.sin_port = from.i.sin_port;
246                        }
247                        if (tp->farAddress.sin_port == from.i.sin_port)
248                                break;
249                }
250
251                /*
252                 * Packet is from someone with whom we are
253                 * not interested.  Tell them to go away.
254                 */
255                sendStifle (tp, &from.i);
256        }
257        tv.tv_sec = 0;
258        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
259        return len;
260}
261
262/*
263 * Send an acknowledgement
264 */
265static int
266sendAck (struct tftpStream *tp)
267{
268        /*
269         * Create the acknowledgement
270         */
271        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
272        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
273
274        /*
275         * Send it
276         */
277        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
278                                        (struct sockaddr *)&tp->farAddress,
279                                        sizeof tp->farAddress) < 0)
280                return errno;
281        return 0;
282}
283
284/*
285 * Release a stream and clear the pointer to it
286 */
287static void
288releaseStream (int s)
289{
290        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
291        free (tftpStreams[s]);
292        tftpStreams[s] = NULL;
293        rtems_semaphore_release (tftp_mutex);
294}
295
296/*
297 * Open a TFTP stream
298 */
299rtems_device_driver rtems_tftp_open(
300  rtems_device_major_number major,
301  rtems_device_minor_number minor,
302  void *pargp
303)
304{
305        rtems_libio_open_close_args_t *ap = pargp;
306        struct tftpStream *tp;
307        int retryCount;
308        rtems_unsigned32 farAddress;
309        int s;
310        int len;
311        char *cp1, *cp2;
312        char *remoteFilename;
313        rtems_interval now;
314        rtems_status_code sc;
315
316/* XXX change to eval_path/open */
317        /*
318         * Read-only for now
319         */
320        if (ap->flags & LIBIO_FLAGS_WRITE)
321                return RTEMS_NOT_IMPLEMENTED;
322
323        /*
324         * Pick apart the name into a host:pathname pair
325         */
326        if (strlen (ap->iop->pathname) <= strlen (TFTP_PATHNAME_PREFIX))
327                return RTEMS_INVALID_NAME;
328        cp2 = ap->iop->pathname + strlen (TFTP_PATHNAME_PREFIX);
329        if (*cp2 == '/') {
330                farAddress = rtems_bsdnet_bootp_server_address.s_addr;
331        }
332        else {
333                char *hostname;
334
335                cp1 = cp2;
336                while (*cp2 != '/') {
337                        if (*cp2 == '\0')
338                                return RTEMS_INVALID_NAME;
339                        cp2++;
340                }
341                len = cp2 - cp1;
342                hostname = malloc (len + 1);
343                if (hostname == NULL)
344                        return RTEMS_NO_MEMORY;
345                strncpy (hostname, cp1, len);
346                hostname[len] = '\0';
347                farAddress = inet_addr (hostname);
348                free (hostname);
349        }
350        if ((farAddress == 0) || (farAddress == ~0))
351                return RTEMS_INVALID_NAME;
352        if (*++cp2 == '\0')
353                return RTEMS_INVALID_NAME;
354        remoteFilename = cp2;
355        if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
356                return RTEMS_INVALID_NAME;
357
358        /*
359         * Find a free stream
360         */
361        sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
362        if (sc != RTEMS_SUCCESSFUL)
363                return sc;
364        for (s = 0 ; s < nStreams ; s++) {
365                if (tftpStreams[s] == NULL)
366                        break;
367        }
368        if (s == nStreams) {
369                /*
370                 * Reallocate stream pointers
371                 * Guard against the case where realloc() returns NULL.
372                 */
373                struct tftpStream **np;
374
375                np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
376                if (np == NULL) {
377                        rtems_semaphore_release (tftp_mutex);
378                        return RTEMS_NO_MEMORY;
379                }
380                tftpStreams = np;
381        }
382        tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
383        rtems_semaphore_release (tftp_mutex);
384        if (tp == NULL)
385                return RTEMS_NO_MEMORY;
386        ap->iop->data0 = s;
387        ap->iop->data1 = tp;
388
389        /*
390         * Create the socket
391         */
392        if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
393                releaseStream (s);
394                return RTEMS_TOO_MANY;
395        }
396
397        /*
398         * Bind the socket to a local address
399         */
400        retryCount = 0;
401        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
402        for (;;) {
403                int try = (now + retryCount) % 10;
404
405                tp->myAddress.sin_family = AF_INET;
406                tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor);
407                tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
408                if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
409                        break;
410                if (++retryCount == 10) {
411                        close (tp->socket);
412                        releaseStream (minor);
413                        return RTEMS_RESOURCE_IN_USE;
414                }
415        }
416
417        /*
418         * Set the UDP destination to the TFTP server
419         * port on the remote machine.
420         */
421        tp->farAddress.sin_family = AF_INET;
422        tp->farAddress.sin_addr.s_addr = farAddress;
423        tp->farAddress.sin_port = htons (69);
424
425        /*
426         * Start the transfer
427         */
428        tp->firstReply = 1;
429        for (;;) {
430                /*
431                 * Create the request
432                 */
433                tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
434                cp1 = tp->pkbuf.tftpRWRQ.filename_mode;
435                cp2 = remoteFilename;
436                while ((*cp1++ = *cp2++) != '\0')
437                        continue;
438                cp2 = "octet";
439                while ((*cp1++ = *cp2++) != '\0')
440                        continue;
441                len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
442
443                /*
444                 * Send the request
445                 */
446                if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
447                                        (struct sockaddr *)&tp->farAddress,
448                                        sizeof tp->farAddress) < 0) {
449                        close (tp->socket);
450                        releaseStream (minor);
451                        return RTEMS_UNSATISFIED;
452                }
453
454                /*
455                 * Get reply
456                 */
457                len = getPacket (tp);
458                if (len >= (int) sizeof tp->pkbuf.tftpACK) {
459                        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
460                        if ((opcode == TFTP_OPCODE_DATA)
461                         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
462                                tp->nused = 0;
463                                tp->blocknum = 1;
464                                tp->nleft = len - 2 * sizeof (rtems_unsigned16);
465                                tp->eof = (tp->nleft < TFTP_BUFSIZE);
466                                if (sendAck (tp) != 0) {
467                                        close (tp->socket);
468                                        releaseStream (minor);
469                                        return RTEMS_UNSATISFIED;
470                                }
471                                break;
472                        }
473                        if (opcode == TFTP_OPCODE_ERROR) {
474                                tftpSetErrno (tp);
475                                close (tp->socket);
476                                releaseStream (ap->iop->data0);
477                                return RTEMS_INTERNAL_ERROR;
478                        }
479                }
480
481                /*
482                 * Keep trying
483                 */
484                if (++retryCount >= OPEN_RETRY_LIMIT) {
485                        close (tp->socket);
486                        releaseStream (minor);
487                        return RTEMS_UNSATISFIED;
488                }
489        }
490        return RTEMS_SUCCESSFUL;
491}
492
493/*
494 * Read from a TFTP stream
495 */
496rtems_device_driver rtems_tftp_read(
497  rtems_device_major_number major,
498  rtems_device_minor_number minor,
499  void *pargp
500)
501{
502        rtems_libio_rw_args_t *ap = pargp;
503        char *bp;
504        struct tftpStream *tp;
505        int retryCount;
506        int nwant;
507
508        tp = ap->iop->data1;
509
510        /*
511         * Read till user request is satisfied or EOF is reached
512         */
513        bp = ap->buffer;
514        nwant = ap->count;
515        while (nwant) {
516                if (tp->nleft) {
517                        int count;
518                        if (nwant < tp->nleft)
519                                count = nwant;
520                        else
521                                count = tp->nleft;
522                        memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
523                        tp->nused += count;
524                        tp->nleft -= count;
525                        bp += count;
526                        nwant -= count;
527                        if (nwant == 0)
528                                break;
529                }
530                if (tp->eof)
531                        break;
532
533                /*
534                 * Wait for the next packet
535                 */
536                retryCount = 0;
537                for (;;) {
538                        int len = getPacket (tp);
539                        if (len >= (int)sizeof tp->pkbuf.tftpACK) {
540                                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
541                                rtems_unsigned16 nextBlock = tp->blocknum + 1;
542                                if ((opcode == TFTP_OPCODE_DATA)
543                                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
544                                        tp->nused = 0;
545                                        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
546                                        tp->eof = (tp->nleft < TFTP_BUFSIZE);
547                                        tp->blocknum++;
548                                        if (sendAck (tp) != 0)
549                                                return RTEMS_IO_ERROR;
550                                        break;
551                                }
552                                if (opcode == TFTP_OPCODE_ERROR) {
553                                        tftpSetErrno (tp);
554                                        return RTEMS_INTERNAL_ERROR;
555                                }
556                        }
557
558                        /*
559                         * Keep trying?
560                         */
561                        if (++retryCount == IO_RETRY_LIMIT)
562                                return RTEMS_IO_ERROR;
563                        if (sendAck (tp) != 0)
564                                return RTEMS_IO_ERROR;
565                }
566        }
567        ap->bytes_moved = ap->count - nwant;
568        return RTEMS_SUCCESSFUL;
569}
570
571/*
572 * Close a TFTP stream
573 */
574rtems_device_driver rtems_tftp_close(
575  rtems_device_major_number major,
576  rtems_device_minor_number minor,
577  void *pargp
578)
579{
580        rtems_libio_open_close_args_t *ap = pargp;
581        struct tftpStream *tp = ap->iop->data1;;
582
583        if (!tp->eof && !tp->firstReply) {
584                /*
585                 * Tell the other end to stop
586                 */
587                rtems_interval ticksPerSecond;
588                sendStifle (tp, &tp->farAddress);
589                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
590                rtems_task_wake_after (1 + ticksPerSecond / 10);
591        }
592        close (tp->socket);
593        releaseStream (ap->iop->data0);
594        return RTEMS_SUCCESSFUL;
595}
596
597rtems_device_driver rtems_tftp_write(
598  rtems_device_major_number major,
599  rtems_device_minor_number minor,
600  void *pargp
601)
602{
603        return RTEMS_NOT_CONFIGURED;
604}
605
606rtems_device_driver rtems_tftp_control(
607  rtems_device_major_number major,
608  rtems_device_minor_number minor,
609  void *pargp
610)
611{
612        return RTEMS_NOT_CONFIGURED;
613}
Note: See TracBrowser for help on using the repository browser.