xref: /aosp_15_r20/external/toybox/toys/pending/tftpd.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* tftpd.c - TFTP server.
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2013 Ranjan Kumar <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2013 Kyungwan Han <[email protected]>
5*cf5a6c84SAndroid Build Coastguard Worker  *
6*cf5a6c84SAndroid Build Coastguard Worker  * No Standard.
7*cf5a6c84SAndroid Build Coastguard Worker 
8*cf5a6c84SAndroid Build Coastguard Worker USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
9*cf5a6c84SAndroid Build Coastguard Worker 
10*cf5a6c84SAndroid Build Coastguard Worker config TFTPD
11*cf5a6c84SAndroid Build Coastguard Worker   bool "tftpd"
12*cf5a6c84SAndroid Build Coastguard Worker   default n
13*cf5a6c84SAndroid Build Coastguard Worker   help
14*cf5a6c84SAndroid Build Coastguard Worker     usage: tftpd [-cr] [-u USER] [DIR]
15*cf5a6c84SAndroid Build Coastguard Worker 
16*cf5a6c84SAndroid Build Coastguard Worker     Transfer file from/to tftp server.
17*cf5a6c84SAndroid Build Coastguard Worker 
18*cf5a6c84SAndroid Build Coastguard Worker     -r	read only
19*cf5a6c84SAndroid Build Coastguard Worker     -c	Allow file creation via upload
20*cf5a6c84SAndroid Build Coastguard Worker     -u	run as USER
21*cf5a6c84SAndroid Build Coastguard Worker     -l	Log to syslog (inetd mode requires this)
22*cf5a6c84SAndroid Build Coastguard Worker */
23*cf5a6c84SAndroid Build Coastguard Worker 
24*cf5a6c84SAndroid Build Coastguard Worker #define FOR_tftpd
25*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
26*cf5a6c84SAndroid Build Coastguard Worker 
27*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
28*cf5a6c84SAndroid Build Coastguard Worker   char *user;
29*cf5a6c84SAndroid Build Coastguard Worker 
30*cf5a6c84SAndroid Build Coastguard Worker   long sfd;
31*cf5a6c84SAndroid Build Coastguard Worker   struct passwd *pw;
32*cf5a6c84SAndroid Build Coastguard Worker )
33*cf5a6c84SAndroid Build Coastguard Worker 
34*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_BLKSIZE 512  // as per RFC 1350.
35*cf5a6c84SAndroid Build Coastguard Worker 
36*cf5a6c84SAndroid Build Coastguard Worker // opcodes
37*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_RRQ  1  // Read Request          RFC 1350, RFC 2090
38*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_WRQ  2  // Write Request         RFC 1350
39*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_DATA 3  // Data chunk            RFC 1350
40*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_ACK  4  // Acknowledgement       RFC 1350
41*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_ERR  5  // Error Message         RFC 1350
42*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_OP_OACK 6  // Option acknowledgment RFC 2347
43*cf5a6c84SAndroid Build Coastguard Worker 
44*cf5a6c84SAndroid Build Coastguard Worker // Error Codes:
45*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_NOSUCHFILE  1 // File not found
46*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_ACCESS      2 // Access violation
47*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_FULL        3 // Disk full or allocation exceeded
48*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_ILLEGALOP   4 // Illegal TFTP operation
49*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_UNKID       5 // Unknown transfer ID
50*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_EXISTS      6 // File already exists
51*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_UNKUSER     7 // No such user
52*cf5a6c84SAndroid Build Coastguard Worker #define TFTPD_ER_NEGOTIATE   8 // Terminate transfer due to option negotiation
53*cf5a6c84SAndroid Build Coastguard Worker 
54*cf5a6c84SAndroid Build Coastguard Worker /* TFTP Packet Formats
55*cf5a6c84SAndroid Build Coastguard Worker  *  Type   Op #     Format without header
56*cf5a6c84SAndroid Build Coastguard Worker  *         2 bytes    string    1 byte    string    1 byte
57*cf5a6c84SAndroid Build Coastguard Worker  *         -----------------------------------------------
58*cf5a6c84SAndroid Build Coastguard Worker  *  RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
59*cf5a6c84SAndroid Build Coastguard Worker  *  WRQ    -----------------------------------------------
60*cf5a6c84SAndroid Build Coastguard Worker  *         2 bytes    2 bytes      n bytes
61*cf5a6c84SAndroid Build Coastguard Worker  *         ---------------------------------
62*cf5a6c84SAndroid Build Coastguard Worker  *  DATA  | 03    |   Block #  |    Data    |
63*cf5a6c84SAndroid Build Coastguard Worker  *         ---------------------------------
64*cf5a6c84SAndroid Build Coastguard Worker  *         2 bytes    2 bytes
65*cf5a6c84SAndroid Build Coastguard Worker  *         -------------------
66*cf5a6c84SAndroid Build Coastguard Worker  *  ACK   | 04    |   Block #  |
67*cf5a6c84SAndroid Build Coastguard Worker  *         --------------------
68*cf5a6c84SAndroid Build Coastguard Worker  *         2 bytes  2 bytes       string     1 byte
69*cf5a6c84SAndroid Build Coastguard Worker  *         ----------------------------------------
70*cf5a6c84SAndroid Build Coastguard Worker  *  ERROR | 05    |  ErrorCode |   ErrMsg   |   0  |
71*cf5a6c84SAndroid Build Coastguard Worker  *         ----------------------------------------
72*cf5a6c84SAndroid Build Coastguard Worker  */
73*cf5a6c84SAndroid Build Coastguard Worker 
74*cf5a6c84SAndroid Build Coastguard Worker static char *g_errpkt = toybuf + TFTPD_BLKSIZE;
75*cf5a6c84SAndroid Build Coastguard Worker 
76*cf5a6c84SAndroid Build Coastguard Worker // Create and send error packet.
send_errpkt(struct sockaddr * dstaddr,socklen_t socklen,char * errmsg)77*cf5a6c84SAndroid Build Coastguard Worker static void send_errpkt(struct sockaddr *dstaddr,
78*cf5a6c84SAndroid Build Coastguard Worker     socklen_t socklen, char *errmsg)
79*cf5a6c84SAndroid Build Coastguard Worker {
80*cf5a6c84SAndroid Build Coastguard Worker   error_msg_raw(errmsg);
81*cf5a6c84SAndroid Build Coastguard Worker   g_errpkt[1] = TFTPD_OP_ERR;
82*cf5a6c84SAndroid Build Coastguard Worker   strcpy(g_errpkt + 4, errmsg);
83*cf5a6c84SAndroid Build Coastguard Worker   if (sendto(TT.sfd, g_errpkt, strlen(errmsg)+5, 0, dstaddr, socklen) < 0)
84*cf5a6c84SAndroid Build Coastguard Worker     perror_exit("sendto failed");
85*cf5a6c84SAndroid Build Coastguard Worker }
86*cf5a6c84SAndroid Build Coastguard Worker 
87*cf5a6c84SAndroid Build Coastguard Worker // Advance to the next option or value. Returns NULL if there are no
88*cf5a6c84SAndroid Build Coastguard Worker // more options.
next_token(char * at,char * end)89*cf5a6c84SAndroid Build Coastguard Worker static char *next_token(char *at, char *end)
90*cf5a6c84SAndroid Build Coastguard Worker {
91*cf5a6c84SAndroid Build Coastguard Worker   if (at == NULL) return NULL;
92*cf5a6c84SAndroid Build Coastguard Worker 
93*cf5a6c84SAndroid Build Coastguard Worker   for (; at < end; at++) {
94*cf5a6c84SAndroid Build Coastguard Worker     if (*at == '\0') {
95*cf5a6c84SAndroid Build Coastguard Worker       at++;
96*cf5a6c84SAndroid Build Coastguard Worker       break;
97*cf5a6c84SAndroid Build Coastguard Worker     }
98*cf5a6c84SAndroid Build Coastguard Worker   }
99*cf5a6c84SAndroid Build Coastguard Worker   return (at < end) ? at : NULL;
100*cf5a6c84SAndroid Build Coastguard Worker }
101*cf5a6c84SAndroid Build Coastguard Worker 
102*cf5a6c84SAndroid Build Coastguard Worker // Used to send / receive packets.
do_action(struct sockaddr * srcaddr,struct sockaddr * dstaddr,socklen_t socklen,char * file,int opcode,int tsize,int blksize)103*cf5a6c84SAndroid Build Coastguard Worker static void do_action(struct sockaddr *srcaddr, struct sockaddr *dstaddr,
104*cf5a6c84SAndroid Build Coastguard Worker     socklen_t socklen, char *file, int opcode, int tsize, int blksize)
105*cf5a6c84SAndroid Build Coastguard Worker {
106*cf5a6c84SAndroid Build Coastguard Worker   int fd, done = 0, retry_count = 12, timeout = 100, len;
107*cf5a6c84SAndroid Build Coastguard Worker   uint16_t blockno = 1, pktopcode, rblockno;
108*cf5a6c84SAndroid Build Coastguard Worker   char *ptr, *spkt, *rpkt;
109*cf5a6c84SAndroid Build Coastguard Worker   struct pollfd pollfds[1];
110*cf5a6c84SAndroid Build Coastguard Worker 
111*cf5a6c84SAndroid Build Coastguard Worker   spkt = xzalloc(blksize + 4);
112*cf5a6c84SAndroid Build Coastguard Worker   rpkt = xzalloc(blksize + 4);
113*cf5a6c84SAndroid Build Coastguard Worker   ptr = spkt+2; //point after opcode.
114*cf5a6c84SAndroid Build Coastguard Worker 
115*cf5a6c84SAndroid Build Coastguard Worker   pollfds[0].fd = TT.sfd;
116*cf5a6c84SAndroid Build Coastguard Worker   // initialize groups, setgid and setuid
117*cf5a6c84SAndroid Build Coastguard Worker   if (TT.pw) xsetuser(TT.pw);
118*cf5a6c84SAndroid Build Coastguard Worker 
119*cf5a6c84SAndroid Build Coastguard Worker   if (opcode == TFTPD_OP_RRQ) fd = open(file, O_RDONLY, 0666);
120*cf5a6c84SAndroid Build Coastguard Worker   else fd = open(file,
121*cf5a6c84SAndroid Build Coastguard Worker     FLAG(c) ? (O_WRONLY|O_TRUNC|O_CREAT) : (O_WRONLY|O_TRUNC), 0666);
122*cf5a6c84SAndroid Build Coastguard Worker   if (fd < 0) {
123*cf5a6c84SAndroid Build Coastguard Worker     g_errpkt[3] = TFTPD_ER_NOSUCHFILE;
124*cf5a6c84SAndroid Build Coastguard Worker     send_errpkt(dstaddr, socklen, "can't open file");
125*cf5a6c84SAndroid Build Coastguard Worker     goto CLEAN_APP;
126*cf5a6c84SAndroid Build Coastguard Worker   }
127*cf5a6c84SAndroid Build Coastguard Worker   // For download -> blockno will be 1.
128*cf5a6c84SAndroid Build Coastguard Worker   // 1st ACK will be from dst,which will have blockno-=1
129*cf5a6c84SAndroid Build Coastguard Worker   // Create and send ACK packet.
130*cf5a6c84SAndroid Build Coastguard Worker   if (blksize != TFTPD_BLKSIZE || tsize) {
131*cf5a6c84SAndroid Build Coastguard Worker     pktopcode = TFTPD_OP_OACK;
132*cf5a6c84SAndroid Build Coastguard Worker     // add "blksize\000blksize_val\000" in send buffer.
133*cf5a6c84SAndroid Build Coastguard Worker     if (blksize != TFTPD_BLKSIZE) {
134*cf5a6c84SAndroid Build Coastguard Worker       strcpy(ptr, "blksize");
135*cf5a6c84SAndroid Build Coastguard Worker       ptr += strlen("blksize") + 1;
136*cf5a6c84SAndroid Build Coastguard Worker       ptr += snprintf(ptr, 6, "%d", blksize) + 1;
137*cf5a6c84SAndroid Build Coastguard Worker     }
138*cf5a6c84SAndroid Build Coastguard Worker     if (tsize) {// add "tsize\000tsize_val\000" in send buffer.
139*cf5a6c84SAndroid Build Coastguard Worker       struct stat sb;
140*cf5a6c84SAndroid Build Coastguard Worker 
141*cf5a6c84SAndroid Build Coastguard Worker       sb.st_size = 0;
142*cf5a6c84SAndroid Build Coastguard Worker       fstat(fd, &sb);
143*cf5a6c84SAndroid Build Coastguard Worker       strcpy(ptr, "tsize");
144*cf5a6c84SAndroid Build Coastguard Worker       ptr += strlen("tsize") + 1;
145*cf5a6c84SAndroid Build Coastguard Worker       ptr += sprintf(ptr, "%lu", (unsigned long)sb.st_size)+1;
146*cf5a6c84SAndroid Build Coastguard Worker     }
147*cf5a6c84SAndroid Build Coastguard Worker     goto SEND_PKT;
148*cf5a6c84SAndroid Build Coastguard Worker   }
149*cf5a6c84SAndroid Build Coastguard Worker   // upload ->  ACK 1st packet with filename, as it has blockno 0.
150*cf5a6c84SAndroid Build Coastguard Worker   if (opcode == TFTPD_OP_WRQ) blockno = 0;
151*cf5a6c84SAndroid Build Coastguard Worker 
152*cf5a6c84SAndroid Build Coastguard Worker   // Prepare DATA and/or ACK pkt and send it.
153*cf5a6c84SAndroid Build Coastguard Worker   for (;;) {
154*cf5a6c84SAndroid Build Coastguard Worker     int poll_ret;
155*cf5a6c84SAndroid Build Coastguard Worker 
156*cf5a6c84SAndroid Build Coastguard Worker     retry_count = 12, timeout = 100, pktopcode = TFTPD_OP_ACK;
157*cf5a6c84SAndroid Build Coastguard Worker     ptr = spkt+2;
158*cf5a6c84SAndroid Build Coastguard Worker     *((uint16_t*)ptr) = htons(blockno);
159*cf5a6c84SAndroid Build Coastguard Worker     blockno++;
160*cf5a6c84SAndroid Build Coastguard Worker     ptr += 2;
161*cf5a6c84SAndroid Build Coastguard Worker     if (opcode == TFTPD_OP_RRQ) {
162*cf5a6c84SAndroid Build Coastguard Worker       pktopcode = TFTPD_OP_DATA;
163*cf5a6c84SAndroid Build Coastguard Worker       len = readall(fd, ptr, blksize);
164*cf5a6c84SAndroid Build Coastguard Worker       if (len < 0) {
165*cf5a6c84SAndroid Build Coastguard Worker         send_errpkt(dstaddr, socklen, "read-error");
166*cf5a6c84SAndroid Build Coastguard Worker         break;
167*cf5a6c84SAndroid Build Coastguard Worker       }
168*cf5a6c84SAndroid Build Coastguard Worker       if (len != blksize) done = 1; //last pkt.
169*cf5a6c84SAndroid Build Coastguard Worker       ptr += len;
170*cf5a6c84SAndroid Build Coastguard Worker     }
171*cf5a6c84SAndroid Build Coastguard Worker SEND_PKT:
172*cf5a6c84SAndroid Build Coastguard Worker     // 1st ACK will be from dst, which will have blockno-=1
173*cf5a6c84SAndroid Build Coastguard Worker     *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode.
174*cf5a6c84SAndroid Build Coastguard Worker RETRY_SEND:
175*cf5a6c84SAndroid Build Coastguard Worker     if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0)
176*cf5a6c84SAndroid Build Coastguard Worker       perror_exit("sendto failed");
177*cf5a6c84SAndroid Build Coastguard Worker     // if "block size < 512", send ACK and exit.
178*cf5a6c84SAndroid Build Coastguard Worker     if ((pktopcode == TFTPD_OP_ACK) && done) break;
179*cf5a6c84SAndroid Build Coastguard Worker 
180*cf5a6c84SAndroid Build Coastguard Worker POLL_INPUT:
181*cf5a6c84SAndroid Build Coastguard Worker     pollfds[0].events = POLLIN;
182*cf5a6c84SAndroid Build Coastguard Worker     pollfds[0].fd = TT.sfd;
183*cf5a6c84SAndroid Build Coastguard Worker     poll_ret = poll(pollfds, 1, timeout);
184*cf5a6c84SAndroid Build Coastguard Worker     if (poll_ret < 0 && (errno == EINTR || errno == ENOMEM)) goto POLL_INPUT;
185*cf5a6c84SAndroid Build Coastguard Worker     if (!poll_ret) {
186*cf5a6c84SAndroid Build Coastguard Worker       if (!--retry_count) {
187*cf5a6c84SAndroid Build Coastguard Worker         error_msg("timeout");
188*cf5a6c84SAndroid Build Coastguard Worker         break;
189*cf5a6c84SAndroid Build Coastguard Worker       }
190*cf5a6c84SAndroid Build Coastguard Worker       timeout += 150;
191*cf5a6c84SAndroid Build Coastguard Worker       goto RETRY_SEND;
192*cf5a6c84SAndroid Build Coastguard Worker     } else if (poll_ret == 1) {
193*cf5a6c84SAndroid Build Coastguard Worker       len = read(pollfds[0].fd, rpkt, blksize + 4);
194*cf5a6c84SAndroid Build Coastguard Worker       if (len < 0) {
195*cf5a6c84SAndroid Build Coastguard Worker         send_errpkt(dstaddr, socklen, "read-error");
196*cf5a6c84SAndroid Build Coastguard Worker         break;
197*cf5a6c84SAndroid Build Coastguard Worker       }
198*cf5a6c84SAndroid Build Coastguard Worker       if (len < 4) goto POLL_INPUT;
199*cf5a6c84SAndroid Build Coastguard Worker     } else {
200*cf5a6c84SAndroid Build Coastguard Worker       perror_msg("poll");
201*cf5a6c84SAndroid Build Coastguard Worker       break;
202*cf5a6c84SAndroid Build Coastguard Worker     }
203*cf5a6c84SAndroid Build Coastguard Worker     // Validate receive packet.
204*cf5a6c84SAndroid Build Coastguard Worker     pktopcode = ntohs(((uint16_t*)rpkt)[0]);
205*cf5a6c84SAndroid Build Coastguard Worker     rblockno = ntohs(((uint16_t*)rpkt)[1]);
206*cf5a6c84SAndroid Build Coastguard Worker     if (pktopcode == TFTPD_OP_ERR) {
207*cf5a6c84SAndroid Build Coastguard Worker       char *message = "DATA Check failure.";
208*cf5a6c84SAndroid Build Coastguard Worker       char *arr[] = {"File not found", "Access violation",
209*cf5a6c84SAndroid Build Coastguard Worker         "Disk full or allocation exceeded", "Illegal TFTP operation",
210*cf5a6c84SAndroid Build Coastguard Worker         "Unknown transfer ID", "File already exists",
211*cf5a6c84SAndroid Build Coastguard Worker         "No such user", "Terminate transfer due to option negotiation"};
212*cf5a6c84SAndroid Build Coastguard Worker 
213*cf5a6c84SAndroid Build Coastguard Worker       if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
214*cf5a6c84SAndroid Build Coastguard Worker       error_msg_raw(message);
215*cf5a6c84SAndroid Build Coastguard Worker       break; // Break the for loop.
216*cf5a6c84SAndroid Build Coastguard Worker     }
217*cf5a6c84SAndroid Build Coastguard Worker 
218*cf5a6c84SAndroid Build Coastguard Worker     // if download requested by client,
219*cf5a6c84SAndroid Build Coastguard Worker     // server will send data pkt and will receive ACK pkt from client.
220*cf5a6c84SAndroid Build Coastguard Worker     if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) {
221*cf5a6c84SAndroid Build Coastguard Worker       if (rblockno == (uint16_t) (blockno - 1)) {
222*cf5a6c84SAndroid Build Coastguard Worker         if (!done) continue; // Send next chunk of data.
223*cf5a6c84SAndroid Build Coastguard Worker         break;
224*cf5a6c84SAndroid Build Coastguard Worker       }
225*cf5a6c84SAndroid Build Coastguard Worker     }
226*cf5a6c84SAndroid Build Coastguard Worker 
227*cf5a6c84SAndroid Build Coastguard Worker     // server will receive DATA pkt and write the data.
228*cf5a6c84SAndroid Build Coastguard Worker     if ((opcode == TFTPD_OP_WRQ) && (pktopcode == TFTPD_OP_DATA)) {
229*cf5a6c84SAndroid Build Coastguard Worker       if (rblockno == blockno) {
230*cf5a6c84SAndroid Build Coastguard Worker         int nw = writeall(fd, &rpkt[4], len-4);
231*cf5a6c84SAndroid Build Coastguard Worker         if (nw != len-4) {
232*cf5a6c84SAndroid Build Coastguard Worker           g_errpkt[3] = TFTPD_ER_FULL;
233*cf5a6c84SAndroid Build Coastguard Worker           send_errpkt(dstaddr, socklen, "write error");
234*cf5a6c84SAndroid Build Coastguard Worker           break;
235*cf5a6c84SAndroid Build Coastguard Worker         }
236*cf5a6c84SAndroid Build Coastguard Worker 
237*cf5a6c84SAndroid Build Coastguard Worker         if (nw != blksize) done = 1;
238*cf5a6c84SAndroid Build Coastguard Worker       }
239*cf5a6c84SAndroid Build Coastguard Worker       continue;
240*cf5a6c84SAndroid Build Coastguard Worker     }
241*cf5a6c84SAndroid Build Coastguard Worker     goto POLL_INPUT;
242*cf5a6c84SAndroid Build Coastguard Worker   } // end of loop
243*cf5a6c84SAndroid Build Coastguard Worker 
244*cf5a6c84SAndroid Build Coastguard Worker CLEAN_APP:
245*cf5a6c84SAndroid Build Coastguard Worker   if (CFG_TOYBOX_FREE) {
246*cf5a6c84SAndroid Build Coastguard Worker     free(spkt);
247*cf5a6c84SAndroid Build Coastguard Worker     free(rpkt);
248*cf5a6c84SAndroid Build Coastguard Worker     close(fd);
249*cf5a6c84SAndroid Build Coastguard Worker   }
250*cf5a6c84SAndroid Build Coastguard Worker }
251*cf5a6c84SAndroid Build Coastguard Worker 
tftpd_main(void)252*cf5a6c84SAndroid Build Coastguard Worker void tftpd_main(void)
253*cf5a6c84SAndroid Build Coastguard Worker {
254*cf5a6c84SAndroid Build Coastguard Worker   int fd = 0, recvmsg_len, opcode, blksize = TFTPD_BLKSIZE, tsize = 0, set =1, bflag = 0;
255*cf5a6c84SAndroid Build Coastguard Worker   struct sockaddr_storage srcaddr, dstaddr;
256*cf5a6c84SAndroid Build Coastguard Worker   socklen_t socklen = sizeof(struct sockaddr_storage);
257*cf5a6c84SAndroid Build Coastguard Worker   char *buf = toybuf;
258*cf5a6c84SAndroid Build Coastguard Worker   char *end;
259*cf5a6c84SAndroid Build Coastguard Worker 
260*cf5a6c84SAndroid Build Coastguard Worker   memset(&srcaddr, 0, sizeof(srcaddr));
261*cf5a6c84SAndroid Build Coastguard Worker   if (getsockname(0, (struct sockaddr *)&srcaddr, &socklen)) help_exit(0);
262*cf5a6c84SAndroid Build Coastguard Worker 
263*cf5a6c84SAndroid Build Coastguard Worker   if (TT.user) TT.pw = xgetpwnam(TT.user);
264*cf5a6c84SAndroid Build Coastguard Worker   if (*toys.optargs) xchroot(*toys.optargs);
265*cf5a6c84SAndroid Build Coastguard Worker 
266*cf5a6c84SAndroid Build Coastguard Worker   recvmsg_len = recvfrom(fd, toybuf, blksize, 0, (void *)&dstaddr, &socklen);
267*cf5a6c84SAndroid Build Coastguard Worker   end = toybuf + recvmsg_len;
268*cf5a6c84SAndroid Build Coastguard Worker 
269*cf5a6c84SAndroid Build Coastguard Worker   TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0);
270*cf5a6c84SAndroid Build Coastguard Worker   if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set,
271*cf5a6c84SAndroid Build Coastguard Worker         sizeof(set)) < 0) perror_exit("setsockopt failed");
272*cf5a6c84SAndroid Build Coastguard Worker   xbind(TT.sfd, (void *)&srcaddr, socklen);
273*cf5a6c84SAndroid Build Coastguard Worker   xconnect(TT.sfd, (void *)&dstaddr, socklen);
274*cf5a6c84SAndroid Build Coastguard Worker   // Error condition.
275*cf5a6c84SAndroid Build Coastguard Worker   if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
276*cf5a6c84SAndroid Build Coastguard Worker     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
277*cf5a6c84SAndroid Build Coastguard Worker     return;
278*cf5a6c84SAndroid Build Coastguard Worker   }
279*cf5a6c84SAndroid Build Coastguard Worker 
280*cf5a6c84SAndroid Build Coastguard Worker   // request is either upload or Download.
281*cf5a6c84SAndroid Build Coastguard Worker   opcode = buf[1];
282*cf5a6c84SAndroid Build Coastguard Worker   if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
283*cf5a6c84SAndroid Build Coastguard Worker       || ((opcode == TFTPD_OP_WRQ) && FLAG(r))) {
284*cf5a6c84SAndroid Build Coastguard Worker     send_errpkt((struct sockaddr*)&dstaddr, socklen,
285*cf5a6c84SAndroid Build Coastguard Worker       (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
286*cf5a6c84SAndroid Build Coastguard Worker     return;
287*cf5a6c84SAndroid Build Coastguard Worker   }
288*cf5a6c84SAndroid Build Coastguard Worker 
289*cf5a6c84SAndroid Build Coastguard Worker   buf += 2;
290*cf5a6c84SAndroid Build Coastguard Worker   if (*buf == '.' || strstr(buf, "/.")) {
291*cf5a6c84SAndroid Build Coastguard Worker     send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
292*cf5a6c84SAndroid Build Coastguard Worker     return;
293*cf5a6c84SAndroid Build Coastguard Worker   }
294*cf5a6c84SAndroid Build Coastguard Worker 
295*cf5a6c84SAndroid Build Coastguard Worker   buf = next_token(buf, end);
296*cf5a6c84SAndroid Build Coastguard Worker   // As per RFC 1350, mode is case in-sensitive.
297*cf5a6c84SAndroid Build Coastguard Worker   if (buf == NULL || strcasecmp(buf, "octet")) {
298*cf5a6c84SAndroid Build Coastguard Worker     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
299*cf5a6c84SAndroid Build Coastguard Worker     return;
300*cf5a6c84SAndroid Build Coastguard Worker   }
301*cf5a6c84SAndroid Build Coastguard Worker 
302*cf5a6c84SAndroid Build Coastguard Worker   //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
303*cf5a6c84SAndroid Build Coastguard Worker   for (buf = next_token(buf, end); buf != NULL; buf = next_token(buf, end)) {
304*cf5a6c84SAndroid Build Coastguard Worker     char *opt = buf;
305*cf5a6c84SAndroid Build Coastguard Worker     buf = next_token(buf, end);
306*cf5a6c84SAndroid Build Coastguard Worker     if (buf == NULL) break; // Missing value.
307*cf5a6c84SAndroid Build Coastguard Worker 
308*cf5a6c84SAndroid Build Coastguard Worker     if (!bflag && !strcasecmp(opt, "blksize")) {
309*cf5a6c84SAndroid Build Coastguard Worker       errno = 0;
310*cf5a6c84SAndroid Build Coastguard Worker       blksize = strtoul(buf, NULL, 10);
311*cf5a6c84SAndroid Build Coastguard Worker       if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
312*cf5a6c84SAndroid Build Coastguard Worker       bflag ^= 1;
313*cf5a6c84SAndroid Build Coastguard Worker     } else if (!tsize && !strcasecmp(opt, "tsize")) tsize ^= 1;
314*cf5a6c84SAndroid Build Coastguard Worker   }
315*cf5a6c84SAndroid Build Coastguard Worker 
316*cf5a6c84SAndroid Build Coastguard Worker   tsize &= (opcode == TFTPD_OP_RRQ);
317*cf5a6c84SAndroid Build Coastguard Worker 
318*cf5a6c84SAndroid Build Coastguard Worker   //do send / receive file.
319*cf5a6c84SAndroid Build Coastguard Worker   do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
320*cf5a6c84SAndroid Build Coastguard Worker       socklen, toybuf + 2, opcode, tsize, blksize);
321*cf5a6c84SAndroid Build Coastguard Worker   if (CFG_TOYBOX_FREE) close(0);
322*cf5a6c84SAndroid Build Coastguard Worker }
323