comparison packages/redboot/current/src/net/tftp_client.c @ 115:6ed91473a1cd ecos-sw-2000-08-21

Merge from eCos master repository on 2000-08-21-22:40:54-BST
author jlarmour
date Fri, 25 Aug 2000 17:32:38 +0000
parents
children 6b5f480c6929
comparison
equal deleted inserted replaced
114:5ad2b71d525e 115:6ed91473a1cd
1 //==========================================================================
2 //
3 // net/tftp_client.c
4 //
5 // Stand-alone TFTP support for RedBoot
6 //
7 //==========================================================================
8 //####COPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 // The contents of this file are subject to the Red Hat eCos Public License
12 // Version 1.1 (the "License"); you may not use this file except in
13 // compliance with the License. You may obtain a copy of the License at
14 // http://www.redhat.com/
15 //
16 // Software distributed under the License is distributed on an "AS IS"
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
18 // License for the specific language governing rights and limitations under
19 // the License.
20 //
21 // The Original Code is eCos - Embedded Configurable Operating System,
22 // released September 30, 1998.
23 //
24 // The Initial Developer of the Original Code is Red Hat.
25 // Portions created by Red Hat are
26 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
27 // All Rights Reserved.
28 // -------------------------------------------
29 //
30 //####COPYRIGHTEND####
31 //==========================================================================
32 //#####DESCRIPTIONBEGIN####
33 //
34 // Author(s): gthomas
35 // Contributors: gthomas
36 // Date: 2000-07-14
37 // Purpose:
38 // Description:
39 //
40 // This code is part of RedBoot (tm).
41 //
42 //####DESCRIPTIONEND####
43 //
44 //==========================================================================
45
46 // TFTP client support
47
48 #include <net/net.h>
49 #include <net/tftp.h>
50 #include <net/tftp_support.h>
51
52 //
53 // Read a file from a host into a local buffer. Returns the
54 // number of bytes actually read, or (-1) if an error occurs.
55 // On error, *err will hold the reason.
56 //
57 int
58 tftp_get(char *filename,
59 struct sockaddr_in *server,
60 char *buf,
61 int len,
62 int mode,
63 int *err)
64 {
65 int res = 0;
66 int actual_len, data_len, recv_len;
67 static int get_port = 7700;
68 struct sockaddr_in local_addr, from_addr;
69 char data[SEGSIZE+sizeof(struct tftphdr)];
70 struct tftphdr *hdr = (struct tftphdr *)data;
71 char *cp, *fp;
72 struct timeval timeout;
73 int last_good_block = 0;
74 int total_timeouts = 0;
75
76 *err = 0; // Just in case
77
78 // Create initial request
79 hdr->th_opcode = htons(RRQ); // Read file
80 cp = (char *)&hdr->th_stuff;
81 fp = filename;
82 while (*fp) *cp++ = *fp++;
83 *cp++ = '\0';
84 if (mode == TFTP_NETASCII) {
85 fp = "NETASCII";
86 } else if (mode == TFTP_OCTET) {
87 fp = "OCTET";
88 } else {
89 *err = TFTP_INVALID;
90 return -1;
91 }
92 while (*fp) *cp++ = *fp++;
93 *cp++ = '\0';
94
95 memset((char *)&local_addr, 0, sizeof(local_addr));
96 local_addr.sin_family = AF_INET;
97 local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
98 local_addr.sin_port = htons(get_port++);
99
100 if (server->sin_port == 0) {
101 server->sin_port = htons(TFTP_PORT);
102 }
103
104 // Send request
105 if (__udp_sendto(data, sizeof(data), server, &local_addr) < 0) {
106 // Problem sending request
107 *err = TFTP_NETERR;
108 return -1;
109 }
110
111 // Read data
112 fp = buf;
113 while (true) {
114 timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
115 timeout.tv_usec = 0;
116 recv_len = sizeof(data);
117 if ((data_len = __udp_recvfrom(&data[0], recv_len, &from_addr, &local_addr, &timeout)) < 0) {
118 // No data, try again
119 if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
120 // Timeout - no data received
121 *err = TFTP_TIMEOUT;
122 return -1;
123 }
124 // Try resending last ACK
125 hdr->th_opcode = htons(ACK);
126 hdr->th_block = htons(last_good_block);
127 if (__udp_sendto(data, 4 /* FIXME */, &from_addr, &local_addr) < 0) {
128 // Problem sending request
129 *err = TFTP_NETERR;
130 return -1;
131 }
132 } else {
133 if (ntohs(hdr->th_opcode) == DATA) {
134 actual_len = 0;
135 if (ntohs(hdr->th_block) == (last_good_block+1)) {
136 // Consume this data
137 cp = hdr->th_data;
138 data_len -= 4; /* Sizeof TFTP header */
139 actual_len = data_len;
140 res += actual_len;
141 while (data_len-- > 0) {
142 if (len-- > 0) {
143 *fp++ = *cp++;
144 } else {
145 // Buffer overflow
146 *err = TFTP_TOOLARGE;
147 return -1;
148 }
149 }
150 last_good_block++;
151 }
152 // Send out the ACK
153 hdr->th_opcode = htons(ACK);
154 hdr->th_block = htons(last_good_block);
155 if (__udp_sendto(data, 4 /* FIXME */, &from_addr, &local_addr) < 0) {
156 // Problem sending ACK
157 *err = TFTP_NETERR;
158 return -1;
159 }
160 if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
161 // End of data
162 return res;
163 }
164 } else
165 if (ntohs(hdr->th_opcode) == ERROR) {
166 *err = ntohs(hdr->th_code);
167 return -1;
168 } else {
169 // What kind of packet is this?
170 *err = TFTP_PROTOCOL;
171 return -1;
172 }
173 }
174 }
175 }
176
177 static struct {
178 bool open;
179 int total_timeouts;
180 int last_good_block;
181 int avail, actual_len;
182 struct sockaddr_in local_addr, from_addr;
183 char data[SEGSIZE+sizeof(struct tftphdr)];
184 char *bufp;
185 } tftp_stream;
186
187 int
188 tftp_stream_open(char *filename,
189 struct sockaddr_in *server,
190 int mode,
191 int *err)
192 {
193 struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
194 char *cp, *fp;
195 static int get_port = 7700;
196
197 if (tftp_stream.open) {
198 *err = TFTP_INVALID; // Already open
199 return -1;
200 }
201
202 // Create initial request
203 hdr->th_opcode = htons(RRQ); // Read file
204 cp = (char *)&hdr->th_stuff;
205 fp = filename;
206 while (*fp) *cp++ = *fp++;
207 *cp++ = '\0';
208 if (mode == TFTP_NETASCII) {
209 fp = "NETASCII";
210 } else if (mode == TFTP_OCTET) {
211 fp = "OCTET";
212 } else {
213 *err = TFTP_INVALID;
214 return -1;
215 }
216 while (*fp) *cp++ = *fp++;
217 *cp++ = '\0';
218
219 memset((char *)&tftp_stream.local_addr, 0, sizeof(tftp_stream.local_addr));
220 tftp_stream.local_addr.sin_family = AF_INET;
221 tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
222 tftp_stream.local_addr.sin_port = htons(get_port++);
223
224 if (server->sin_port == 0) {
225 server->sin_port = htons(TFTP_PORT);
226 }
227
228 // Send request
229 if (__udp_sendto(tftp_stream.data, sizeof(tftp_stream.data),
230 server, &tftp_stream.local_addr) < 0) {
231 // Problem sending request
232 *err = TFTP_NETERR;
233 return -1;
234 }
235
236 tftp_stream.open = true;
237 tftp_stream.avail = 0;
238 tftp_stream.actual_len = -1;
239 tftp_stream.last_good_block = 0;
240 tftp_stream.total_timeouts = 0;
241
242 return 0;
243 }
244
245 int
246 tftp_stream_close(int *err)
247 {
248 tftp_stream.open = false;
249 return 0;
250 }
251
252 int
253 tftp_stream_read(char *buf,
254 int len,
255 int *err)
256 {
257 int total_bytes = 0;
258 int size, recv_len, data_len;
259 struct timeval timeout;
260 struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
261
262 while (total_bytes < len) {
263 // Move any bytes which we've already read/buffered
264 if (tftp_stream.avail > 0) {
265 size = tftp_stream.avail;
266 if (size > (len - total_bytes)) size = len - total_bytes;
267 memcpy(buf, tftp_stream.bufp, size);
268 buf += size;
269 tftp_stream.bufp += size;
270 tftp_stream.avail -= size;
271 total_bytes += size;
272 } else {
273 if ((tftp_stream.actual_len >= 0) && (tftp_stream.actual_len < SEGSIZE)) {
274 // Out of data
275 break;
276 }
277 timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
278 timeout.tv_usec = 0;
279 recv_len = sizeof(tftp_stream.data);
280 if ((data_len = __udp_recvfrom(&tftp_stream.data[0], recv_len, &tftp_stream.from_addr,
281 &tftp_stream.local_addr, &timeout)) < 0) {
282 // No data, try again
283 if ((++tftp_stream.total_timeouts > TFTP_TIMEOUT_MAX) ||
284 (tftp_stream.last_good_block == 0)) {
285 // Timeout - no data received
286 *err = TFTP_TIMEOUT;
287 return -1;
288 }
289 // Try resending last ACK
290 hdr->th_opcode = htons(ACK);
291 hdr->th_block = htons(tftp_stream.last_good_block);
292 if (__udp_sendto(tftp_stream.data, 4 /* FIXME */,
293 &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
294 // Problem sending request
295 *err = TFTP_NETERR;
296 return -1;
297 }
298 } else {
299 if (ntohs(hdr->th_opcode) == DATA) {
300 if (ntohs(hdr->th_block) == (tftp_stream.last_good_block+1)) {
301 // Consume this data
302 data_len -= 4; /* Sizeof TFTP header */
303 tftp_stream.avail = tftp_stream.actual_len = data_len;
304 tftp_stream.bufp = hdr->th_data;
305 tftp_stream.last_good_block++;
306 }
307 // Send out the ACK
308 hdr->th_opcode = htons(ACK);
309 hdr->th_block = htons(tftp_stream.last_good_block);
310 if (__udp_sendto(tftp_stream.data, 4 /* FIXME */,
311 &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
312 // Problem sending ACK
313 *err = TFTP_NETERR;
314 return -1;
315 }
316 } else {
317 if (ntohs(hdr->th_opcode) == ERROR) {
318 *err = ntohs(hdr->th_code);
319 return -1;
320 } else {
321 // What kind of packet is this?
322 *err = TFTP_PROTOCOL;
323 return -1;
324 }
325 }
326 }
327 }
328 }
329 return total_bytes;
330 }
331
332 char *
333 tftp_error(int err)
334 {
335 char *errmsg = "Unknown error";
336
337 switch (err) {
338 case TFTP_ENOTFOUND:
339 return "file not found";
340 case TFTP_EACCESS:
341 return "access violation";
342 case TFTP_ENOSPACE:
343 return "disk full or allocation exceeded";
344 case TFTP_EBADOP:
345 return "illegal TFTP operation";
346 case TFTP_EBADID:
347 return "unknown transfer ID";
348 case TFTP_EEXISTS:
349 return "file already exists";
350 case TFTP_ENOUSER:
351 return "no such user";
352 case TFTP_TIMEOUT:
353 return "operation timed out";
354 case TFTP_NETERR:
355 return "some sort of network error";
356 case TFTP_INVALID:
357 return "invalid parameter";
358 case TFTP_PROTOCOL:
359 return "protocol violation";
360 case TFTP_TOOLARGE:
361 return "file is larger than buffer";
362 }
363 return errmsg;
364 }