]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/net/tftp_client.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / net / tftp_client.c
1 //==========================================================================
2 //
3 //      net/tftp_client.c
4 //
5 //      Stand-alone TFTP support for RedBoot
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2002, 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         2000-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 // TFTP client support
57
58 #include <redboot.h>     // have_net
59 #include <net/net.h>
60 #include <net/tftp.h>
61 #include <net/tftp_support.h>
62
63 // So we remember which ports have been used
64 static int get_port = 7700;
65
66 static struct {
67     bool open;
68     int  total_timeouts, packets_received;
69     unsigned long last_good_block;
70     int  avail, actual_len;
71     struct sockaddr_in local_addr, from_addr;
72     char data[SEGSIZE+sizeof(struct tftphdr)];
73     char *bufp;
74 } tftp_stream;
75
76 int
77 tftp_stream_open(connection_info_t *info,
78                  int *err)
79 {
80     struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
81     char *cp, *fp;
82     char test_buf;
83
84     if (!have_net || tftp_stream.open) {
85         *err = TFTP_INVALID;  // Already open
86         return -1;
87     }
88
89     // Create initial request
90     hdr->th_opcode = htons(RRQ);  // Read file
91     cp = (char *)&hdr->th_stuff;
92     fp = info->filename;
93     while (*fp) *cp++ = *fp++;
94     *cp++ = '\0';
95     // Since this is used for downloading data, OCTET (binary) is the
96     // only mode that makes sense.
97     fp = "OCTET";
98     while (*fp) *cp++ = *fp++;
99     *cp++ = '\0';
100
101     memset(&tftp_stream.local_addr, 0, sizeof(tftp_stream.local_addr));
102     tftp_stream.local_addr.sin_family = AF_INET;
103     tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
104     tftp_stream.local_addr.sin_port = htons(get_port++);
105
106     if (info->server->sin_port == 0) {
107         info->server->sin_port = htons(TFTP_PORT);
108     } else {
109         info->server->sin_port = htons(info->server->sin_port);
110     }
111
112     // Send request - note: RFC 1350 (TFTP rev 2) indicates that this should be
113     // only as long as required to hold the request, with the nul terminator.
114     // Some servers silently go to lunch if the request is not the correct size.
115     if (__udp_sendto(tftp_stream.data, cp-(char *)hdr, 
116                      info->server, &tftp_stream.local_addr) < 0) {
117         // Problem sending request
118         *err = TFTP_NETERR;
119         return -1;
120     }
121
122     tftp_stream.open = true;
123     tftp_stream.avail = 0;
124     tftp_stream.actual_len = -1;
125     tftp_stream.last_good_block = 0;
126     tftp_stream.total_timeouts = 0;
127     tftp_stream.from_addr.sin_port = 0;
128     tftp_stream.packets_received = 0;
129
130     // Try and read the first byte [block] since no errors are
131     // reported until then.
132     if (tftp_stream_read(&test_buf, 1, err) == 1) {
133         // Back up [rewind] over this datum
134         tftp_stream.bufp--;
135         tftp_stream.avail++;
136         return 0;  // Open and first read successful
137     } else {
138         tftp_stream.open = false;
139         return -1; // Couldn't read
140     }
141 }
142
143 static int
144 tftp_ack(int *err)
145 {
146     struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
147     // ACK last packet so server can shut down
148     if (tftp_stream.packets_received > 0) {
149         hdr->th_opcode = htons(ACK);
150         hdr->th_block = htons((cyg_uint16)tftp_stream.last_good_block & 0xFFFF);
151         if (__udp_sendto(tftp_stream.data, 4 /* FIXME */, 
152                          &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
153             // Problem sending ACK
154             *err = TFTP_NETERR;
155             return -1;
156         }
157     }
158     return 0;
159 }
160
161 static int
162 tftp_error_ack(int *err, short code, char *msg)
163 {
164     struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
165
166     if (strlen(msg) > (SEGSIZE-1)) {
167       *(msg + SEGSIZE) = 0;
168     }
169
170     if (tftp_stream.packets_received > 0) {
171         hdr->th_opcode = htons(ERROR);
172         hdr->th_code = code;
173         strcpy((char *)&hdr->th_data, msg);
174         if (__udp_sendto(tftp_stream.data, (5 + strlen(msg)), 
175                          &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
176             // Problem sending ACK
177             *err = TFTP_NETERR;
178             return -1;
179         }
180     }
181     return 0;
182 }
183
184 void
185 tftp_stream_close(int *err)
186 {
187     if (tftp_stream.open == true) {
188         tftp_ack(err);
189         tftp_stream.open = false;
190     }
191 }
192
193 void
194 tftp_stream_terminate(bool abort,
195                       int (*getc)(void))
196 {
197     int err;
198
199     if (abort)
200         tftp_error_ack(&err, EUNDEF, "redboot tftp_stream_terminate");
201     else
202         tftp_ack(&err);
203
204     tftp_stream.open = false;
205 }
206
207 int
208 tftp_stream_read(void *buf,
209                  int len,
210                  int *err)
211 {
212     int total_bytes = 0;
213     int size, recv_len, data_len;
214     struct timeval timeout;
215     struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
216
217     while (total_bytes < len) {
218         // Move any bytes which we've already read/buffered
219         if (tftp_stream.avail > 0) {
220             size = tftp_stream.avail;
221             if (size > (len - total_bytes)) size = len - total_bytes;
222             memcpy(buf, tftp_stream.bufp, size);
223             buf = (char *)buf + size;
224             tftp_stream.bufp += size;
225             tftp_stream.avail -= size;
226             total_bytes += size;
227         } else {
228             if (tftp_ack(err) < 0) {
229                 return -1;
230             }
231             if ((tftp_stream.actual_len >= 0) && (tftp_stream.actual_len < SEGSIZE)) {
232                 // Out of data
233                 break;
234             }
235             timeout.tv_sec = (tftp_stream.last_good_block == 0) ? 10*TFTP_TIMEOUT_PERIOD : TFTP_TIMEOUT_PERIOD;
236             timeout.tv_usec = 0;
237             recv_len = sizeof(tftp_stream.data);
238             if ((data_len = __udp_recvfrom(&tftp_stream.data[0], recv_len, &tftp_stream.from_addr, 
239                                            &tftp_stream.local_addr,  &timeout)) < 0) {
240                 // No data, try again
241                 diag_printf("TFTP timed out %d/%d\n", tftp_stream.total_timeouts+1, TFTP_TIMEOUT_MAX);
242                 if ((++tftp_stream.total_timeouts > TFTP_TIMEOUT_MAX) || 
243                     (tftp_stream.last_good_block == 0)) {
244                     // Timeout - no data received
245                     *err = TFTP_TIMEOUT;
246                     return -1;
247                 }
248                 // Send out the ACK for the last block - maybe server will retry
249                 if (tftp_ack(err) < 0) {
250                     return -1;
251                 }
252             } else {
253                 tftp_stream.packets_received++;
254                 if (ntohs(hdr->th_opcode) == DATA) {
255                     if (ntohs(hdr->th_block) == (cyg_uint16)((tftp_stream.last_good_block+1) & 0xFFFF)) {
256                         // Consume this data
257                         data_len -= 4;  /* Sizeof TFTP header */
258                         tftp_stream.avail = tftp_stream.actual_len = data_len;
259                         tftp_stream.bufp = hdr->th_data;
260                         tftp_stream.last_good_block++;
261                     }
262                 } else {
263                     if (ntohs(hdr->th_opcode) == ERROR) {
264                         *err = ntohs(hdr->th_code);
265                         return -1;
266                     } else {
267                         // What kind of packet is this?
268                         *err = TFTP_PROTOCOL;
269                         return -1;
270                     }
271                 }
272             }
273         }
274     }
275     return total_bytes;
276 }
277
278 char *
279 tftp_error(int err)
280 {
281     char *errmsg = "Unknown error";
282
283     switch (err) {
284     case TFTP_ENOTFOUND:
285         return "file not found";
286     case TFTP_EACCESS:
287         return "access violation";
288     case TFTP_ENOSPACE:
289         return "disk full or allocation exceeded";
290     case TFTP_EBADOP:
291         return "illegal TFTP operation";
292     case TFTP_EBADID:
293         return "unknown transfer ID";
294     case TFTP_EEXISTS:
295         return "file already exists";
296     case TFTP_ENOUSER:
297         return "no such user";
298     case TFTP_TIMEOUT:
299         return "operation timed out";
300     case TFTP_NETERR:
301         return "some sort of network error";
302     case TFTP_INVALID:
303         return "invalid parameter";
304     case TFTP_PROTOCOL:
305         return "protocol violation";
306     case TFTP_TOOLARGE:
307         return "file is larger than buffer";
308     }
309     return errmsg;
310 }
311
312 //
313 // RedBoot interface
314 //
315 GETC_IO_FUNCS(tftp_io, tftp_stream_open, tftp_stream_close,
316               tftp_stream_terminate, tftp_stream_read, tftp_error);
317 RedBoot_load(tftp, tftp_io, true, true, 0);
318