]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/net/common/v2_0/src/tftp_client.c
c11eb11ea20ca5bc92ebab0ffa41196f70fd4855
[karo-tx-redboot.git] / packages / net / common / v2_0 / src / tftp_client.c
1 //==========================================================================
2 //
3 //      lib/tftp_client.c
4 //
5 //      TFTP client support
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) 2003 Andrew.Lunn@ascom.ch
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, andrew.lunn@ascom.ch
46 // Date:         2000-04-06
47 // Purpose:      
48 // Description:  
49 //              
50 //
51 //####DESCRIPTIONEND####
52 //
53 //==========================================================================
54
55 // TFTP client support
56
57 #include <network.h>
58 #include <arpa/tftp.h>
59 #include <tftp_support.h>
60
61 #define min(x,y) (x<y ? x : y)
62
63 //
64 // Read a file from a host into a local buffer.  Returns the
65 // number of bytes actually read, or (-1) if an error occurs.
66 // On error, *err will hold the reason.
67 // This version uses the server name. This can be a name for DNS lookup
68 // or a dotty or colony number format for IPv4 or IPv6.
69 int tftp_client_get(char *filename,
70                     char *server,
71                     int port,
72                     char *buf,
73                     int len,
74                     int mode,
75                     int *err) {
76                     
77     int result = 0;
78     int s=-1;
79     int actual_len, data_len;
80     socklen_t from_len, recv_len;
81     static int get_port = 7700;
82     struct addrinfo * addrinfo;
83     struct addrinfo * res;
84     struct addrinfo hints;
85     int error;
86
87     struct sockaddr local_addr, from_addr;
88     char data[SEGSIZE+sizeof(struct tftphdr)];
89     struct tftphdr *hdr = (struct tftphdr *)data;
90     char *cp, *fp;
91     struct timeval timeout;
92     unsigned short last_good_block = 0;
93     fd_set fds;
94     int total_timeouts = 0;
95
96     *err = 0;  // Just in case
97
98     // Create initial request
99     hdr->th_opcode = htons(RRQ);  // Read file
100     cp = (char *)&hdr->th_stuff;
101     fp = filename;
102     while (*fp) *cp++ = *fp++;
103     *cp++ = '\0';
104     if (mode == TFTP_NETASCII) {
105         fp = "NETASCII";
106     } else if (mode == TFTP_OCTET) {
107         fp = "OCTET";
108     } else {
109         *err = TFTP_INVALID;
110         return -1;
111     }
112     while (*fp) *cp++ = *fp++;
113     *cp++ = '\0';
114
115     memset(&hints,0,sizeof(hints));
116     hints.ai_family = PF_UNSPEC;
117     error = getaddrinfo(server, "tftp", &hints, &res);
118     if (error) {
119       *err = TFTP_NETERR;
120       return -1;
121     }
122     
123     addrinfo = res;
124     while (addrinfo) {
125       s = socket(addrinfo->ai_family, addrinfo->ai_socktype, 
126                  addrinfo->ai_protocol);
127       if (s >= 0) {
128         memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
129         switch(addrinfo->ai_addr->sa_family) {
130         case AF_INET: {
131           struct sockaddr_in * saddr = 
132             (struct sockaddr_in *) addrinfo->ai_addr;
133           struct sockaddr_in * laddr = 
134             (struct sockaddr_in *) &local_addr;
135           if (port) {
136             saddr->sin_port = htons(port);
137           }
138           laddr->sin_port = htons(get_port++);
139           laddr->sin_addr.s_addr = INADDR_ANY;
140           break;
141         }
142 #ifdef CYGPKG_NET_INET6
143         case AF_INET6: {
144           struct sockaddr_in6 * saddr = 
145             (struct sockaddr_in6 *) addrinfo->ai_addr;
146           struct sockaddr_in6 * laddr = 
147             (struct sockaddr_in6 *) &local_addr;
148           if (port) {
149             saddr->sin6_port = htons(port);
150           }
151           laddr->sin6_port = htons(get_port++);
152           laddr->sin6_addr = in6addr_any;
153           break;
154         }
155 #endif
156         default:
157           *err = TFTP_NETERR;
158           goto out;
159         }
160
161         if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) {
162           *err = TFTP_NETERR;
163           goto out;
164         }
165         
166         // Send request
167         if (sendto(s, data, (int)(cp-data), 0, 
168                    addrinfo->ai_addr, 
169                    addrinfo->ai_addrlen) < 0) {
170           // Problem sending request
171           *err = TFTP_NETERR;
172           goto nextaddr;
173         }
174
175         // Read data
176         fp = buf;
177         while (true) {
178           timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
179           timeout.tv_usec = 0;
180           FD_ZERO(&fds);
181           FD_SET(s, &fds);
182           if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
183             total_timeouts++;
184             if ((last_good_block == 0) && (total_timeouts > TFTP_RETRIES_MAX)) {
185               // Timeout - no data received. Probably no server.
186               *err = TFTP_TIMEOUT;
187               goto nextaddr;
188             }
189             if (total_timeouts > TFTP_TIMEOUT_MAX) {
190               // Timeout - have received data. Network problem?
191               *err = TFTP_TIMEOUT;
192               goto out;
193             }
194             
195             if (last_good_block == 0 ) {
196               // Send request
197               if (sendto(s, data, (int)(cp-data), 0, 
198                          addrinfo->ai_addr, 
199                          addrinfo->ai_addrlen) < 0) {
200                 // Problem sending request
201                 *err = TFTP_NETERR;
202                 goto nextaddr;
203               }
204             } else {
205               // Try resending last ACK
206               hdr->th_opcode = htons(ACK);
207               hdr->th_block = htons(last_good_block);
208               if (sendto(s, data, 4 /* FIXME */, 0, 
209                          &from_addr, from_len) < 0) {
210                 // Problem sending request
211                 *err = TFTP_NETERR;
212                 goto out;
213               }
214             }
215           } else {
216             recv_len = sizeof(data);
217             from_len = sizeof(from_addr);
218             if ((data_len = recvfrom(s, &data, recv_len, 0, 
219                                      &from_addr, &from_len)) < 0) {
220               // What happened?
221               *err = TFTP_NETERR;
222               goto out;
223             }
224             if (ntohs(hdr->th_opcode) == DATA) {
225               actual_len = 0;
226               if (ntohs(hdr->th_block) == (last_good_block+1)) {
227                 // Consume this data
228                 cp = hdr->th_data;
229                 data_len -= 4;  /* Sizeof TFTP header */
230                 actual_len = data_len;
231                 result += actual_len;
232                 while (data_len-- > 0) {
233                   if (len-- > 0) {
234                     *fp++ = *cp++;
235                   } else {
236                     // Buffer overflow
237                     *err = TFTP_TOOLARGE;
238                     goto out;
239                   }
240                 }
241                 last_good_block++;
242               } else {
243                 // To prevent an out-of-sequence packet from
244                 // terminating transmission prematurely, set
245                 // actual_len to a full size packet.
246                 actual_len = SEGSIZE;
247               }
248               // Send out the ACK
249               hdr->th_opcode = htons(ACK);
250               hdr->th_block = htons(last_good_block);
251               if (sendto(s, data, 4 /* FIXME */, 0, 
252                          &from_addr, from_len) < 0) {
253                 // Problem sending request
254                 *err = TFTP_NETERR;
255                 goto out;
256               }
257               // A short packet marks the end of the file.
258               if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
259                 // End of data
260                 close(s);
261                 freeaddrinfo(res);
262                 return result;
263               }
264             } else 
265               if (ntohs(hdr->th_opcode) == ERROR) {
266                 *err = ntohs(hdr->th_code);
267                 goto out;
268               } else {
269                 // What kind of packet is this?
270                 *err = TFTP_PROTOCOL;
271                 goto out;
272               }
273           }
274         }
275       }
276       // If we got here, it means there was a problem connecting to the 
277       // server. Try the next address returned by getaddrinfo
278     nextaddr:
279       if (-1 != s) {
280         close(s);
281       }
282       addrinfo=addrinfo->ai_next;
283     }
284     // We ran into problems. Cleanup
285  out:
286     if (-1 != s) {
287       close (s);
288     }
289     freeaddrinfo(res);
290     return -1;
291 }
292 //
293 // Read a file from a host into a local buffer.  Returns the
294 // number of bytes actually read, or (-1) if an error occurs.
295 // On error, *err will hold the reason.
296 //
297 // Depreciated. Use tftp_client_get instead.
298 int
299 tftp_get(char *filename,
300          struct sockaddr_in *server,
301          char *buf,
302          int len,
303          int mode,
304          int *err)
305 {
306   char server_name[20];
307   char *ret;
308   int port;
309
310   ret = inet_ntop(AF_INET, (void *)&server->sin_addr, 
311                   server_name, sizeof(server_name));
312   if (NULL == ret) {
313       *err = TFTP_NETERR;
314       return -1;
315   }
316   port = server->sin_port;
317
318   return tftp_client_get(filename, server_name, port, buf, len, mode, err);
319 }
320
321 //
322 // Send data to a file on a server via TFTP.
323 //
324 int
325 tftp_put(char *filename,
326          struct sockaddr_in *server,
327          char *buf,
328          int len,
329          int mode,
330          int *err)
331 {
332   char server_name[20];
333   char *ret;
334   int port;
335
336   ret = inet_ntop(AF_INET, (void *)&server->sin_addr, 
337                   server_name, sizeof(server_name));
338   if (NULL == ret) {
339       *err = TFTP_NETERR;
340       return -1;
341   }
342   port = server->sin_port;
343
344   return tftp_client_put(filename, server_name, port, buf, len, mode, err);
345 }
346
347 //
348 // Put a file to a host from a local buffer.  Returns the
349 // number of bytes actually writen, or (-1) if an error occurs.
350 // On error, *err will hold the reason.
351 // This version uses the server name. This can be a name for DNS lookup
352 // or a dotty or colony number format for IPv4 or IPv6.
353 int tftp_client_put(char *filename,
354                     char *server,
355                     int port,
356                     char *buf,
357                     int len,
358                     int mode,
359                     int *err) {
360
361     int result = 0;
362     int s = -1, actual_len, data_len;
363     socklen_t recv_len, from_len;
364     static int put_port = 7800;
365     struct sockaddr local_addr, from_addr;
366     char data[SEGSIZE+sizeof(struct tftphdr)];
367     struct tftphdr *hdr = (struct tftphdr *)data;
368     char *cp, *fp, *sfp;
369     struct timeval timeout;
370     unsigned short last_good_block = 0;
371     fd_set fds;
372     int total_timeouts = 0;
373     struct addrinfo hints;
374     struct addrinfo * addrinfo;
375     struct addrinfo * res;
376     int error;
377
378     *err = 0;  // Just in case
379
380     memset(&hints,0,sizeof(hints));
381     hints.ai_family = PF_UNSPEC;
382     error = getaddrinfo(server, "tftp", &hints, &res);
383     if (error) {
384       *err = TFTP_NETERR;
385       return -1;
386     }
387     
388     addrinfo = res;
389     while (addrinfo) {
390       s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
391                  addrinfo->ai_protocol);
392       if (s >= 0) {
393         memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
394         switch(addrinfo->ai_addr->sa_family) {
395         case AF_INET: {
396           struct sockaddr_in * saddr = 
397             (struct sockaddr_in *) addrinfo->ai_addr;
398           struct sockaddr_in * laddr = 
399             (struct sockaddr_in *) &local_addr;
400           if (port) {
401             saddr->sin_port = htons(port);
402           }
403           laddr->sin_port = htons(put_port++);
404           laddr->sin_addr.s_addr = INADDR_ANY;
405           break;
406         }
407 #ifdef CYGPKG_NET_INET6
408         case AF_INET6: {
409           struct sockaddr_in6 * saddr = 
410             (struct sockaddr_in6 *) addrinfo->ai_addr;
411           struct sockaddr_in6 * laddr = 
412             (struct sockaddr_in6 *) &local_addr;
413           if (port) {
414             saddr->sin6_port = htons(port);
415           }
416           laddr->sin6_port = htons(put_port++);
417           laddr->sin6_addr = in6addr_any;
418           break;
419         }
420 #endif
421         default:
422           *err = TFTP_NETERR;
423           goto out;
424         }
425         if (bind(s, 
426                  (struct sockaddr *)&local_addr, 
427                  addrinfo->ai_addrlen) < 0) {
428           // Problem setting up my end
429           *err = TFTP_NETERR;
430           goto out;
431         }
432
433         while (1) {
434           // Create initial request
435           hdr->th_opcode = htons(WRQ);  // Create/write file
436           cp = (char *)&hdr->th_stuff;
437           fp = filename;
438           while (*fp) *cp++ = *fp++;
439           *cp++ = '\0';
440           if (mode == TFTP_NETASCII) {
441             fp = "NETASCII";
442           } else if (mode == TFTP_OCTET) {
443             fp = "OCTET";
444           } else {
445             *err = TFTP_INVALID;
446             goto out;
447           }
448           while (*fp) *cp++ = *fp++;
449           *cp++ = '\0';
450           // Send request
451           if (sendto(s, data, (int)(cp-data), 0, 
452                      addrinfo->ai_addr,
453                      addrinfo->ai_addrlen) < 0) {
454             // Problem sending request
455             *err = TFTP_NETERR;
456             goto nextaddr;
457           }
458           // Wait for ACK
459           timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
460           timeout.tv_usec = 0;
461           FD_ZERO(&fds);
462           FD_SET(s, &fds);
463           if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
464             if (++total_timeouts > TFTP_RETRIES_MAX) {
465               // Timeout - no ACK received
466               *err = TFTP_TIMEOUT;
467               goto nextaddr;
468             }
469           } else {
470             recv_len = sizeof(data);
471             from_len = sizeof(from_addr);
472             if ((data_len = recvfrom(s, &data, recv_len, 0, 
473                                      &from_addr, &from_len)) < 0) {
474               // What happened?
475               *err = TFTP_NETERR;
476               goto out;
477             }
478             if (ntohs(hdr->th_opcode) == ACK) {
479               // Write request accepted - start sending data
480               break;
481             } else 
482               if (ntohs(hdr->th_opcode) == ERROR) {
483                 *err = ntohs(hdr->th_code);
484                 goto out;
485               } else {
486                 // What kind of packet is this?
487                 goto out;
488               }
489           }
490         }
491         
492         // Send data
493         sfp = buf;
494         last_good_block = 1;
495         while (result < len) {
496           // Build packet of data to send
497           data_len = min(SEGSIZE, len-result);
498           hdr->th_opcode = htons(DATA);
499           hdr->th_block = htons(last_good_block);
500           cp = hdr->th_data;
501           fp = sfp;
502           actual_len = data_len + 4;
503           // FIXME - what about "netascii" data?
504           while (data_len-- > 0) *cp++ = *fp++;
505           // Send data packet
506           if (sendto(s, data, actual_len, 0, 
507                      &from_addr, from_len) < 0) {
508             // Problem sending request
509             *err = TFTP_NETERR;
510             goto out;
511           }
512           // Wait for ACK
513           timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
514           timeout.tv_usec = 0;
515           FD_ZERO(&fds);
516           FD_SET(s, &fds);
517           if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
518             if (++total_timeouts > TFTP_TIMEOUT_MAX) {
519               // Timeout - no data received
520               *err = TFTP_TIMEOUT;
521               goto out;
522             }
523           } else {
524             recv_len = sizeof(data);
525             from_len = sizeof(from_addr);
526             if ((data_len = recvfrom(s, &data, recv_len, 0, 
527                                      &from_addr, &from_len)) < 0) {
528               // What happened?
529               *err = TFTP_NETERR;
530               goto out;
531             }
532             if (ntohs(hdr->th_opcode) == ACK) {
533               if (ntohs(hdr->th_block) == last_good_block) {
534                 // Advance pointers, etc
535                 sfp = fp;
536                 result += (actual_len - 4);
537                 last_good_block++;
538               } else {
539                 diag_printf("Send block #%d, got ACK for #%d\n", 
540                             last_good_block, ntohs(hdr->th_block));
541               }
542             } else 
543               if (ntohs(hdr->th_opcode) == ERROR) {
544                 *err = ntohs(hdr->th_code);
545                 goto out;
546               } else {
547                 // What kind of packet is this?
548                 *err = TFTP_PROTOCOL;
549                 goto out;
550               }
551           }
552         }
553         close (s);
554         return result;
555       }
556
557       // If we got here, it means there was a problem connecting to the 
558       // server. Try the next address returned by getaddrinfo
559     nextaddr:
560       if (-1 != s) {
561         close(s);
562       }
563       addrinfo=addrinfo->ai_next;
564     }
565     // We ran into problems. Cleanup
566  out:
567     if (-1 != s) {
568       close (s);
569     }
570     freeaddrinfo(res);
571     return -1;
572 }
573
574 // EOF tftp_client.c