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