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