• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* tftp.c - TFTP client.
2  *
3  * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
4  * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
5  *
6  * No Standard.
7 
8 USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
9 
10 config TFTP
11   bool "tftp"
12   default n
13   help
14     usage: tftp [OPTIONS] HOST [PORT]
15 
16     Transfer file from/to tftp server.
17 
18     -l FILE Local FILE
19     -r FILE Remote FILE
20     -g    Get file
21     -p    Put file
22     -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
23 */
24 #define FOR_tftp
25 #include "toys.h"
26 
GLOBALS(char * local_file;char * remote_file;long block_size;struct sockaddr_storage inaddr;int af;)27 GLOBALS(
28   char *local_file;
29   char *remote_file;
30   long block_size;
31 
32   struct sockaddr_storage inaddr;
33   int af;
34 )
35 
36 #define TFTP_BLKSIZE    512
37 #define TFTP_RETRIES    3
38 #define TFTP_DATAHEADERSIZE 4
39 #define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
40 #define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
41 #define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
42 #define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
43 
44 #define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
45 #define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
46 #define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
47 #define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
48 #define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
49 #define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
50 
51 #define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
52 #define TFTP_ER_UNKID    5  /* Unknown transfer ID */
53 
54 #define TFTP_ES_NOSUCHFILE  "File not found"
55 #define TFTP_ES_ACCESS    "Access violation"
56 #define TFTP_ES_FULL    "Disk full or allocation exceeded"
57 #define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
58 #define TFTP_ES_UNKID    "Unknown transfer ID"
59 #define TFTP_ES_EXISTS    "File already exists"
60 #define TFTP_ES_UNKUSER    "No such user"
61 #define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
62 
63 // Initializes SERVER with ADDR and returns socket.
64 static int init_tftp(struct sockaddr_storage *server)
65 {
66   struct timeval to = { .tv_sec = 10, //Time out
67                         .tv_usec = 0 };
68   const int set = 1;
69   int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
70 
71   xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
72   xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
73 
74   if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
75   memset(server, 0, sizeof(struct sockaddr_storage));
76   if (TT.af == AF_INET6) {
77       ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
78       ((struct sockaddr_in6 *)server)->sin6_addr =
79         ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
80       ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
81   }
82   else {
83       ((struct sockaddr_in *)server)->sin_family = AF_INET;
84       ((struct sockaddr_in *)server)->sin_addr.s_addr =
85         ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
86       ((struct sockaddr_in *)server)->sin_port = htons(port);
87   }
88   return sd;
89 }
90 
91 /*
92  * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
93  * and returns length of packet.
94  */
mkpkt_request(uint8_t * buffer,int opcode,char * path,int mode)95 static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
96 {
97   buffer[0] = opcode >> 8;
98   buffer[1] = opcode & 0xff;
99   if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
100   return sprintf((char*) &buffer[2], "%s%c%s", path, 0,
101     (mode ? "octet" : "netascii")) + 3;
102 }
103 
104 /*
105  * Makes an acknowledgement packet in BUFFER of BLOCNO
106  * and returns packet length.
107  */
mkpkt_ack(uint8_t * buffer,uint16_t blockno)108 static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
109 {
110   buffer[0] = TFTP_OP_ACK >> 8;
111   buffer[1] = TFTP_OP_ACK & 0xff;
112   buffer[2] = blockno >> 8;
113   buffer[3] = blockno & 0xff;
114   return 4;
115 }
116 
117 /*
118  * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
119  * and returns packet length.
120  */
mkpkt_err(uint8_t * buffer,uint16_t errorcode,char * errormsg)121 static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
122 {
123   buffer[0] = TFTP_OP_ERR >> 8;
124   buffer[1] = TFTP_OP_ERR & 0xff;
125   buffer[2] = errorcode >> 8;
126   buffer[3] = errorcode & 0xff;
127   strcpy((char*) &buffer[4], errormsg);
128   return strlen(errormsg) + 5;
129 }
130 
131 /*
132  * Recieves data from server in BUFF with socket SD and updates FROM
133  * and returns read length.
134  */
read_server(int sd,void * buf,size_t len,struct sockaddr_storage * from)135 static ssize_t read_server(int sd, void *buf, size_t len,
136   struct sockaddr_storage *from)
137 {
138   socklen_t alen;
139   ssize_t nb;
140 
141   for (;;) {
142     memset(buf, 0, len);
143     alen = sizeof(struct sockaddr_storage);
144     nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
145     if (nb < 0) {
146       if (errno == EAGAIN) {
147         perror_msg("server read timed out");
148         return nb;
149       }else if (errno != EINTR) {
150         perror_msg("server read failed");
151         return nb;
152       }
153     }else return nb;
154   }
155   return nb;
156 }
157 
158 /*
159  * sends data to server TO from BUFF of length LEN through socket SD
160  * and returns successfully send bytes number.
161  */
write_server(int sd,void * buf,size_t len,struct sockaddr_storage * to)162 static ssize_t write_server(int sd, void *buf, size_t len,
163   struct sockaddr_storage *to)
164 {
165   ssize_t nb;
166 
167   for (;;) {
168     nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
169             sizeof(struct sockaddr_storage));
170     if (nb < 0) {
171       if (errno != EINTR) {
172         perror_msg("server write failed");
173         return nb;
174       }
175     } else return nb;
176   }
177   return nb;
178 }
179 
180 // checks packet for data and updates block no
check_data(uint8_t * packet,uint16_t * opcode,uint16_t * blockno)181 static inline int check_data( uint8_t *packet, uint16_t *opcode,
182   uint16_t *blockno)
183 {
184   *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
185   if (*opcode == TFTP_OP_DATA) {
186     *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
187     return 0;
188   }
189   return -1;
190 }
191 
192 // Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
mkpkt_data(int fd,off_t offset,uint8_t * packet,uint16_t blockno)193 static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
194 {
195   off_t tmp;
196   int nbytesread;
197 
198   packet[0] = TFTP_OP_DATA >> 8;
199   packet[1] = TFTP_OP_DATA & 0xff;
200   packet[2] = blockno >> 8;
201   packet[3] = blockno & 0xff;
202   tmp = lseek(fd, offset, SEEK_SET);
203   if (tmp == (off_t) -1) {
204     perror_msg("lseek failed");
205     return -1;
206   }
207   nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
208   if (nbytesread < 0) return -1;
209   return nbytesread + TFTP_DATAHEADERSIZE;
210 }
211 
212 // Receives ACK responses from server and updates blockno
read_ack(int sd,uint8_t * packet,struct sockaddr_storage * server,uint16_t * port,uint16_t * blockno)213 static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
214   uint16_t *port, uint16_t *blockno)
215 {
216   struct sockaddr_storage from;
217   ssize_t nbytes;
218   uint16_t opcode, rblockno;
219   int packetlen, retry;
220 
221   for (retry = 0; retry < TFTP_RETRIES; retry++) {
222     for (;;) {
223       nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
224       if (nbytes < 4) { // Ack headersize = 4
225         if (nbytes == 0) error_msg("Connection lost.");
226         else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
227         else error_msg("Server read ACK failure.");
228         break;
229       } else {
230         if (!*port) {
231           *port = ((struct sockaddr_in *)&from)->sin_port;
232           ((struct sockaddr_in *)server)->sin_port =
233                   ((struct sockaddr_in *)&from)->sin_port;
234         }
235         if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
236                 ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
237           error_msg("Invalid address in DATA.");
238           continue;
239         }
240         if (*port != ((struct sockaddr_in *)server)->sin_port) {
241           error_msg("Invalid port in DATA.");
242           packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
243           (void) write_server(sd, packet, packetlen, server);
244           continue;
245         }
246         opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
247         rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
248 
249         if (opcode != TFTP_OP_ACK) {
250           error_msg("Bad opcode.");
251           if (opcode > 5) {
252             packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
253             (void) write_server(sd, packet, packetlen, server);
254           }
255           break;
256         }
257         if (blockno) *blockno = rblockno;
258         return 0;
259       }
260     }
261   }
262   error_msg("Timeout, Waiting for ACK.");
263   return -1;
264 }
265 
266 // receives file from server.
file_get(void)267 static int file_get(void)
268 {
269   struct sockaddr_storage server, from;
270   uint8_t *packet;
271   uint16_t blockno = 0, opcode, rblockno = 0;
272   int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
273 
274   sd = init_tftp(&server);
275 
276   packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
277   fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
278 
279   len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
280   ret = write_server(sd, packet, len, &server);
281   if (ret != len){
282     unlink(TT.local_file);
283     goto errout_with_sd;
284   }
285   if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
286   else ((struct sockaddr_in *)&server)->sin_port = 0;
287 
288   do {
289     blockno++;
290     for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
291       nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
292       if (nbytesrecvd > 0) {
293         if ( ((TT.af == AF_INET) &&
294                 memcmp(&((struct sockaddr_in *)&server)->sin_addr,
295                 &((struct sockaddr_in *)&from)->sin_addr,
296                 sizeof(struct in_addr))) ||
297              ((TT.af == AF_INET6) &&
298                 memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
299                 &((struct sockaddr_in6 *)&from)->sin6_addr,
300                 sizeof(struct in6_addr)))) {
301           error_msg("Invalid address in DATA.");
302           retry--;
303           continue;
304         }
305         if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
306                 && (((struct sockaddr_in *)&server)->sin_port !=
307                 ((struct sockaddr_in *)&from)->sin_port)) ||
308              ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
309                 && (((struct sockaddr_in6 *)&server)->sin6_port !=
310                 ((struct sockaddr_in6 *)&from)->sin6_port))) {
311           error_msg("Invalid port in DATA.");
312           len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
313           ret = write_server(sd, packet, len, &from);
314           retry--;
315           continue;
316         }
317         if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
318           error_msg("Tiny data packet ignored.");
319           continue;
320         }
321         if (check_data(packet, &opcode, &rblockno) != 0
322             || blockno != rblockno) {
323 
324         if (opcode == TFTP_OP_ERR) {
325           char *message = "DATA Check failure.";
326             char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
327               TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
328               TFTP_ES_UNKID, TFTP_ES_EXISTS,
329               TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
330             if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
331             error_msg(message);
332         }
333         if (opcode > 5) {
334           len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
335           ret = write_server(sd, packet, len, &from);
336         }
337         continue;
338         }
339         if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
340           ((struct sockaddr_in6 *)&server)->sin6_port =
341             ((struct sockaddr_in6 *)&from)->sin6_port;
342         else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
343           ((struct sockaddr_in *)&server)->sin_port =
344             ((struct sockaddr_in *)&from)->sin_port;
345         break;
346       }
347     }
348     if (retry == TFTP_RETRIES) {
349       error_msg("Retry limit exceeded.");
350       unlink(TT.local_file);
351       goto errout_with_sd;
352     }
353     ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
354     if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
355       unlink(TT.local_file);
356       goto errout_with_sd;
357     }
358     len = mkpkt_ack(packet, blockno);
359     ret = write_server(sd, packet, len, &server);
360     if (ret != len){
361       unlink(TT.local_file);
362       goto errout_with_sd;
363     }
364   } while (ndatabytes >= TFTP_DATASIZE);
365 
366   result = 0;
367 
368 errout_with_sd: xclose(sd);
369   free(packet);
370   return result;
371 }
372 
373 // Sends file to server.
file_put(void)374 int file_put(void)
375 {
376   struct sockaddr_storage server;
377   uint8_t *packet;
378   off_t offset = 0;
379   uint16_t blockno = 1, rblockno, port = 0;
380   int packetlen, sd, fd, retry = 0, ret, result = -1;
381 
382   sd = init_tftp(&server);
383   packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
384   fd = xopenro(TT.local_file);
385 
386   for (;;) {  //first loop for request send and confirmation from server.
387     packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
388     ret = write_server(sd, packet, packetlen, &server);
389     if (ret != packetlen) goto errout_with_sd;
390     if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
391     if (++retry > TFTP_RETRIES) {
392       error_msg("Retry count exceeded.");
393       goto errout_with_sd;
394     }
395   }
396   for (;;) {  // loop for data sending and receving ack from server.
397     packetlen = mkpkt_data(fd, offset, packet, blockno);
398     if (packetlen < 0) goto errout_with_sd;
399 
400     ret = write_server(sd, packet, packetlen, &server);
401     if (ret != packetlen) goto errout_with_sd;
402 
403     if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
404       if (rblockno == blockno) {
405         if (packetlen < TFTP_PACKETSIZE) break;
406         blockno++;
407         offset += TFTP_DATASIZE;
408         retry = 0;
409         continue;
410       }
411     }
412     if (++retry > TFTP_RETRIES) {
413       error_msg("Retry count exceeded.");
414       goto errout_with_sd;
415     }
416   }
417   result = 0;
418 
419 errout_with_sd: close(sd);
420   free(packet);
421   return result;
422 }
423 
tftp_main(void)424 void tftp_main(void)
425 {
426   struct addrinfo *info, rp, *res=0;
427   int ret;
428 
429   if (toys.optflags & FLAG_r) {
430     if (!(toys.optflags & FLAG_l)) {
431       char *slash = strrchr(TT.remote_file, '/');
432       TT.local_file = (slash) ? slash + 1 : TT.remote_file;
433     }
434   } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
435   else error_exit("Please provide some files.");
436 
437   memset(&rp, 0, sizeof(rp));
438   rp.ai_family = AF_UNSPEC;
439   rp.ai_socktype = SOCK_STREAM;
440   ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
441   if (!ret) {
442     for (res = info; res; res = res->ai_next)
443     if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
444   }
445   if (!res)
446     error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
447   TT.af = info->ai_family;
448 
449   memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
450   freeaddrinfo(info);
451 
452   if (toys.optflags & FLAG_g) file_get();
453   if (toys.optflags & FLAG_p) file_put();
454 }
455