]>
Commit | Line | Data |
---|---|---|
28866e95 | 1 | /* dnsmasq is Copyright (c) 2000-2011 Simon Kelley |
832af0ba SK |
2 | |
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
824af85b SK |
5 | the Free Software Foundation; version 2 dated June, 1991, or |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
832af0ba SK |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
824af85b | 12 | |
73a08a24 SK |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
832af0ba SK |
15 | */ |
16 | ||
17 | #include "dnsmasq.h" | |
18 | ||
19 | #ifdef HAVE_TFTP | |
20 | ||
8ef5ada2 | 21 | static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special); |
832af0ba SK |
22 | static void free_transfer(struct tftp_transfer *transfer); |
23 | static ssize_t tftp_err(int err, char *packet, char *mess, char *file); | |
1b7ecd11 | 24 | static ssize_t tftp_err_oops(char *packet, char *file); |
832af0ba SK |
25 | static ssize_t get_block(char *packet, struct tftp_transfer *transfer); |
26 | static char *next(char **p, char *end); | |
27 | ||
28 | #define OP_RRQ 1 | |
29 | #define OP_WRQ 2 | |
30 | #define OP_DATA 3 | |
31 | #define OP_ACK 4 | |
32 | #define OP_ERR 5 | |
33 | #define OP_OACK 6 | |
34 | ||
35 | #define ERR_NOTDEF 0 | |
36 | #define ERR_FNF 1 | |
37 | #define ERR_PERM 2 | |
38 | #define ERR_FULL 3 | |
39 | #define ERR_ILL 4 | |
40 | ||
5aabfc78 | 41 | void tftp_request(struct listener *listen, time_t now) |
832af0ba SK |
42 | { |
43 | ssize_t len; | |
44 | char *packet = daemon->packet; | |
45 | char *filename, *mode, *p, *end, *opt; | |
28866e95 | 46 | union mysockaddr addr, peer; |
832af0ba | 47 | struct msghdr msg; |
832af0ba | 48 | struct iovec iov; |
1f15b81d | 49 | struct ifreq ifr; |
8ef5ada2 SK |
50 | int is_err = 1, if_index = 0, mtu = 0, special = 0; |
51 | #ifdef HAVE_DHCP | |
832af0ba | 52 | struct iname *tmp; |
8ef5ada2 | 53 | #endif |
5aabfc78 | 54 | struct tftp_transfer *transfer; |
824af85b SK |
55 | int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ |
56 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) | |
1f15b81d | 57 | int mtuflag = IP_PMTUDISC_DONT; |
824af85b | 58 | #endif |
8ef5ada2 | 59 | char namebuff[IF_NAMESIZE]; |
28866e95 | 60 | char pretty_addr[ADDRSTRLEN]; |
8ef5ada2 SK |
61 | char *name; |
62 | char *prefix = daemon->tftp_prefix; | |
63 | struct tftp_prefix *pref; | |
64 | struct interface_list *ir; | |
65 | ||
832af0ba SK |
66 | union { |
67 | struct cmsghdr align; /* this ensures alignment */ | |
28866e95 SK |
68 | #ifdef HAVE_IPV6 |
69 | char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; | |
70 | #endif | |
824af85b | 71 | #if defined(HAVE_LINUX_NETWORK) |
832af0ba | 72 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
824af85b SK |
73 | #elif defined(HAVE_SOLARIS_NETWORK) |
74 | char control[CMSG_SPACE(sizeof(unsigned int))]; | |
7622fc06 | 75 | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
832af0ba SK |
76 | char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
77 | #endif | |
78 | } control_u; | |
79 | ||
80 | msg.msg_controllen = sizeof(control_u); | |
81 | msg.msg_control = control_u.control; | |
82 | msg.msg_flags = 0; | |
83 | msg.msg_name = &peer; | |
84 | msg.msg_namelen = sizeof(peer); | |
85 | msg.msg_iov = &iov; | |
86 | msg.msg_iovlen = 1; | |
87 | ||
88 | iov.iov_base = packet; | |
89 | iov.iov_len = daemon->packet_buff_sz; | |
90 | ||
91 | /* we overwrote the buffer... */ | |
92 | daemon->srv_save = NULL; | |
93 | ||
94 | if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) | |
95 | return; | |
96 | ||
28866e95 | 97 | if (option_bool(OPT_NOWILD)) |
1f15b81d | 98 | { |
28866e95 | 99 | addr = listen->iface->addr; |
1f15b81d | 100 | mtu = listen->iface->mtu; |
8ef5ada2 | 101 | name = listen->iface->name; |
1f15b81d | 102 | } |
1b7ecd11 SK |
103 | else |
104 | { | |
7622fc06 | 105 | struct cmsghdr *cmptr; |
8ef5ada2 SK |
106 | int check; |
107 | struct interface_list *ir; | |
108 | ||
28866e95 SK |
109 | if (msg.msg_controllen < sizeof(struct cmsghdr)) |
110 | return; | |
111 | ||
112 | addr.sa.sa_family = listen->family; | |
1b7ecd11 | 113 | |
832af0ba | 114 | #if defined(HAVE_LINUX_NETWORK) |
28866e95 SK |
115 | if (listen->family == AF_INET) |
116 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
117 | if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) | |
118 | { | |
119 | union { | |
120 | unsigned char *c; | |
121 | struct in_pktinfo *p; | |
122 | } p; | |
123 | p.c = CMSG_DATA(cmptr); | |
124 | addr.in.sin_addr = p.p->ipi_spec_dst; | |
125 | if_index = p.p->ipi_ifindex; | |
126 | } | |
127 | ||
128 | #elif defined(HAVE_SOLARIS_NETWORK) | |
129 | if (listen->family == AF_INET) | |
130 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
1b7ecd11 | 131 | { |
8ef5ada2 SK |
132 | union { |
133 | unsigned char *c; | |
28866e95 SK |
134 | struct in_addr *a; |
135 | unsigned int *i; | |
8ef5ada2 SK |
136 | } p; |
137 | p.c = CMSG_DATA(cmptr); | |
28866e95 SK |
138 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) |
139 | addr.in.sin_addr = *(p.a); | |
140 | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
141 | if_index = *(p.i); | |
1b7ecd11 | 142 | } |
1b7ecd11 | 143 | |
7622fc06 | 144 | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
28866e95 SK |
145 | if (listen->family == AF_INET) |
146 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
147 | { | |
148 | union { | |
149 | unsigned char *c; | |
150 | struct in_addr *a; | |
151 | struct sockaddr_dl *s; | |
152 | } p; | |
153 | p.c = CMSG_DATA(cmptr); | |
154 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) | |
155 | addr.in.sin_addr = *(p.a); | |
156 | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) | |
157 | if_index = p.s->sdl_index; | |
158 | } | |
8ef5ada2 | 159 | |
28866e95 SK |
160 | #endif |
161 | ||
162 | #ifdef HAVE_IPV6 | |
163 | if (listen->family == AF_INET6) | |
164 | { | |
165 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
166 | if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo) | |
167 | { | |
168 | union { | |
169 | unsigned char *c; | |
170 | struct in6_pktinfo *p; | |
171 | } p; | |
172 | p.c = CMSG_DATA(cmptr); | |
173 | ||
174 | addr.in6.sin6_addr = p.p->ipi6_addr; | |
175 | if_index = p.p->ipi6_ifindex; | |
176 | } | |
177 | } | |
832af0ba | 178 | #endif |
1b7ecd11 | 179 | |
28866e95 | 180 | if (!indextoname(listen->tftpfd, if_index, namebuff)) |
1b7ecd11 | 181 | return; |
8ef5ada2 SK |
182 | |
183 | name = namebuff; | |
28866e95 SK |
184 | |
185 | #ifdef HAVE_IPV6 | |
186 | if (listen->family == AF_INET6) | |
187 | check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index); | |
188 | else | |
189 | #endif | |
190 | check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, &if_index); | |
8ef5ada2 SK |
191 | |
192 | /* wierd TFTP service override */ | |
193 | for (ir = daemon->tftp_interfaces; ir; ir = ir->next) | |
194 | if (strcmp(ir->interface, name) == 0) | |
195 | break; | |
196 | ||
197 | if (!ir) | |
198 | { | |
199 | if (!daemon->tftp_unlimited || !check) | |
200 | return; | |
201 | ||
202 | #ifdef HAVE_DHCP | |
203 | /* allowed interfaces are the same as for DHCP */ | |
204 | for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) | |
205 | if (tmp->name && (strcmp(tmp->name, name) == 0)) | |
206 | return; | |
207 | #endif | |
208 | } | |
209 | ||
316e2730 | 210 | strncpy(ifr.ifr_name, name, IF_NAMESIZE); |
1f15b81d SK |
211 | if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) |
212 | mtu = ifr.ifr_mtu; | |
1b7ecd11 SK |
213 | } |
214 | ||
8ef5ada2 SK |
215 | /* check for per-interface prefix */ |
216 | for (pref = daemon->if_prefix; pref; pref = pref->next) | |
217 | if (strcmp(pref->interface, name) == 0) | |
218 | prefix = pref->prefix; | |
219 | ||
220 | /* wierd TFTP interfaces disable special options. */ | |
221 | for (ir = daemon->tftp_interfaces; ir; ir = ir->next) | |
222 | if (strcmp(ir->interface, name) == 0) | |
223 | special = 1; | |
224 | ||
832af0ba | 225 | #ifdef HAVE_SOCKADDR_SA_LEN |
28866e95 | 226 | addr.sa.sa_len = sa_len(&addr); |
832af0ba | 227 | #endif |
28866e95 SK |
228 | |
229 | if (listen->family == AF_INET) | |
230 | addr.in.sin_port = htons(port); | |
231 | #ifdef HAVE_IPV6 | |
232 | else | |
233 | { | |
234 | addr.in6.sin6_port = htons(port); | |
235 | addr.in6.sin6_flowinfo = 0; | |
236 | } | |
237 | #endif | |
238 | ||
5aabfc78 | 239 | if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) |
832af0ba SK |
240 | return; |
241 | ||
28866e95 | 242 | if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1) |
832af0ba SK |
243 | { |
244 | free(transfer); | |
245 | return; | |
246 | } | |
247 | ||
248 | transfer->peer = peer; | |
5aabfc78 | 249 | transfer->timeout = now + 2; |
832af0ba SK |
250 | transfer->backoff = 1; |
251 | transfer->block = 1; | |
252 | transfer->blocksize = 512; | |
9e038946 | 253 | transfer->offset = 0; |
832af0ba SK |
254 | transfer->file = NULL; |
255 | transfer->opt_blocksize = transfer->opt_transize = 0; | |
9e038946 | 256 | transfer->netascii = transfer->carrylf = 0; |
28866e95 SK |
257 | |
258 | prettyprint_addr(&peer, pretty_addr); | |
259 | ||
824af85b SK |
260 | /* if we have a nailed-down range, iterate until we find a free one. */ |
261 | while (1) | |
832af0ba | 262 | { |
28866e95 | 263 | if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 || |
824af85b | 264 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
1f15b81d | 265 | setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || |
824af85b SK |
266 | #endif |
267 | !fix_fd(transfer->sockfd)) | |
268 | { | |
269 | if (errno == EADDRINUSE && daemon->start_tftp_port != 0) | |
270 | { | |
271 | if (++port <= daemon->end_tftp_port) | |
272 | { | |
28866e95 SK |
273 | if (listen->family == AF_INET) |
274 | addr.in.sin_port = htons(port); | |
275 | #ifdef HAVE_IPV6 | |
276 | else | |
277 | addr.in6.sin6_port = htons(port); | |
278 | #endif | |
824af85b SK |
279 | continue; |
280 | } | |
7622fc06 | 281 | my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); |
824af85b SK |
282 | } |
283 | free_transfer(transfer); | |
284 | return; | |
285 | } | |
286 | break; | |
832af0ba | 287 | } |
824af85b | 288 | |
832af0ba SK |
289 | p = packet + 2; |
290 | end = packet + len; | |
291 | ||
292 | if (ntohs(*((unsigned short *)packet)) != OP_RRQ || | |
293 | !(filename = next(&p, end)) || | |
294 | !(mode = next(&p, end)) || | |
9e038946 | 295 | (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) |
28866e95 | 296 | len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_addr); |
832af0ba SK |
297 | else |
298 | { | |
9e038946 SK |
299 | if (strcasecmp(mode, "netascii") == 0) |
300 | transfer->netascii = 1; | |
301 | ||
832af0ba SK |
302 | while ((opt = next(&p, end))) |
303 | { | |
77e94da7 | 304 | if (strcasecmp(opt, "blksize") == 0) |
832af0ba | 305 | { |
77e94da7 | 306 | if ((opt = next(&p, end)) && |
28866e95 | 307 | (special || !option_bool(OPT_TFTP_NOBLOCK))) |
77e94da7 SK |
308 | { |
309 | transfer->blocksize = atoi(opt); | |
310 | if (transfer->blocksize < 1) | |
311 | transfer->blocksize = 1; | |
312 | if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) | |
313 | transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; | |
1f15b81d SK |
314 | /* 32 bytes for IP, UDP and TFTP headers */ |
315 | if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32) | |
316 | transfer->blocksize = (unsigned)mtu - 32; | |
77e94da7 SK |
317 | transfer->opt_blocksize = 1; |
318 | transfer->block = 0; | |
319 | } | |
832af0ba | 320 | } |
77e94da7 | 321 | else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii) |
832af0ba SK |
322 | { |
323 | transfer->opt_transize = 1; | |
324 | transfer->block = 0; | |
325 | } | |
326 | } | |
327 | ||
1f15b81d SK |
328 | /* cope with backslashes from windows boxen. */ |
329 | while ((p = strchr(filename, '\\'))) | |
330 | *p = '/'; | |
331 | ||
f2621c7f | 332 | strcpy(daemon->namebuff, "/"); |
8ef5ada2 | 333 | if (prefix) |
832af0ba | 334 | { |
8ef5ada2 | 335 | if (prefix[0] == '/') |
f2621c7f | 336 | daemon->namebuff[0] = 0; |
8ef5ada2 SK |
337 | strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); |
338 | if (prefix[strlen(prefix)-1] != '/') | |
77e94da7 | 339 | strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
5aabfc78 | 340 | |
28866e95 | 341 | if (!special && option_bool(OPT_TFTP_APREF)) |
5aabfc78 SK |
342 | { |
343 | size_t oldlen = strlen(daemon->namebuff); | |
344 | struct stat statbuf; | |
345 | ||
28866e95 | 346 | strncat(daemon->namebuff, pretty_addr, (MAXDNAME-1) - strlen(daemon->namebuff)); |
77e94da7 | 347 | strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
5aabfc78 SK |
348 | |
349 | /* remove unique-directory if it doesn't exist */ | |
350 | if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) | |
351 | daemon->namebuff[oldlen] = 0; | |
352 | } | |
353 | ||
f2621c7f SK |
354 | /* Absolute pathnames OK if they match prefix */ |
355 | if (filename[0] == '/') | |
356 | { | |
357 | if (strstr(filename, daemon->namebuff) == filename) | |
358 | daemon->namebuff[0] = 0; | |
359 | else | |
360 | filename++; | |
361 | } | |
832af0ba | 362 | } |
f2621c7f | 363 | else if (filename[0] == '/') |
832af0ba | 364 | daemon->namebuff[0] = 0; |
77e94da7 | 365 | strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
832af0ba | 366 | |
5aabfc78 | 367 | /* check permissions and open file */ |
8ef5ada2 | 368 | if ((transfer->file = check_tftp_fileperm(&len, prefix, special))) |
832af0ba | 369 | { |
1b7ecd11 SK |
370 | if ((len = get_block(packet, transfer)) == -1) |
371 | len = tftp_err_oops(packet, daemon->namebuff); | |
372 | else | |
373 | is_err = 0; | |
832af0ba SK |
374 | } |
375 | } | |
376 | ||
377 | while (sendto(transfer->sockfd, packet, len, 0, | |
378 | (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR); | |
379 | ||
380 | if (is_err) | |
381 | free_transfer(transfer); | |
382 | else | |
383 | { | |
832af0ba SK |
384 | transfer->next = daemon->tftp_trans; |
385 | daemon->tftp_trans = transfer; | |
386 | } | |
387 | } | |
1b7ecd11 | 388 | |
8ef5ada2 | 389 | static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special) |
1b7ecd11 SK |
390 | { |
391 | char *packet = daemon->packet, *namebuff = daemon->namebuff; | |
392 | struct tftp_file *file; | |
5aabfc78 | 393 | struct tftp_transfer *t; |
1b7ecd11 SK |
394 | uid_t uid = geteuid(); |
395 | struct stat statbuf; | |
5aabfc78 | 396 | int fd = -1; |
1b7ecd11 SK |
397 | |
398 | /* trick to ban moving out of the subtree */ | |
8ef5ada2 | 399 | if (prefix && strstr(namebuff, "/../")) |
5aabfc78 SK |
400 | goto perm; |
401 | ||
402 | if ((fd = open(namebuff, O_RDONLY)) == -1) | |
1b7ecd11 | 403 | { |
5aabfc78 SK |
404 | if (errno == ENOENT) |
405 | { | |
406 | *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff); | |
407 | return NULL; | |
408 | } | |
1b7ecd11 SK |
409 | else if (errno == EACCES) |
410 | goto perm; | |
411 | else | |
412 | goto oops; | |
413 | } | |
5aabfc78 SK |
414 | |
415 | /* stat the file descriptor to avoid stat->open races */ | |
416 | if (fstat(fd, &statbuf) == -1) | |
417 | goto oops; | |
418 | ||
1b7ecd11 SK |
419 | /* running as root, must be world-readable */ |
420 | if (uid == 0) | |
421 | { | |
422 | if (!(statbuf.st_mode & S_IROTH)) | |
5aabfc78 | 423 | goto perm; |
1b7ecd11 SK |
424 | } |
425 | /* in secure mode, must be owned by user running dnsmasq */ | |
28866e95 | 426 | else if (!special && option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) |
5aabfc78 SK |
427 | goto perm; |
428 | ||
429 | /* If we're doing many tranfers from the same file, only | |
430 | open it once this saves lots of file descriptors | |
431 | when mass-booting a big cluster, for instance. | |
432 | Be conservative and only share when inode and name match | |
433 | this keeps error messages sane. */ | |
434 | for (t = daemon->tftp_trans; t; t = t->next) | |
435 | if (t->file->dev == statbuf.st_dev && | |
436 | t->file->inode == statbuf.st_ino && | |
437 | strcmp(t->file->filename, namebuff) == 0) | |
438 | { | |
439 | close(fd); | |
440 | t->file->refcount++; | |
441 | return t->file; | |
442 | } | |
443 | ||
444 | if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1))) | |
1b7ecd11 SK |
445 | { |
446 | errno = ENOMEM; | |
447 | goto oops; | |
448 | } | |
449 | ||
5aabfc78 | 450 | file->fd = fd; |
1b7ecd11 | 451 | file->size = statbuf.st_size; |
5aabfc78 SK |
452 | file->dev = statbuf.st_dev; |
453 | file->inode = statbuf.st_ino; | |
1b7ecd11 SK |
454 | file->refcount = 1; |
455 | strcpy(file->filename, namebuff); | |
456 | return file; | |
5aabfc78 | 457 | |
1b7ecd11 | 458 | perm: |
5aabfc78 | 459 | errno = EACCES; |
1b7ecd11 | 460 | *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); |
5aabfc78 SK |
461 | if (fd != -1) |
462 | close(fd); | |
1b7ecd11 SK |
463 | return NULL; |
464 | ||
5aabfc78 | 465 | oops: |
1b7ecd11 | 466 | *len = tftp_err_oops(packet, namebuff); |
5aabfc78 SK |
467 | if (fd != -1) |
468 | close(fd); | |
1b7ecd11 SK |
469 | return NULL; |
470 | } | |
471 | ||
5aabfc78 | 472 | void check_tftp_listeners(fd_set *rset, time_t now) |
832af0ba SK |
473 | { |
474 | struct tftp_transfer *transfer, *tmp, **up; | |
475 | ssize_t len; | |
28866e95 | 476 | char pretty_addr[ADDRSTRLEN]; |
832af0ba SK |
477 | |
478 | struct ack { | |
479 | unsigned short op, block; | |
480 | } *mess = (struct ack *)daemon->packet; | |
481 | ||
482 | /* Check for activity on any existing transfers */ | |
483 | for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) | |
484 | { | |
485 | tmp = transfer->next; | |
486 | ||
487 | if (FD_ISSET(transfer->sockfd, rset)) | |
488 | { | |
489 | /* we overwrote the buffer... */ | |
490 | daemon->srv_save = NULL; | |
28866e95 SK |
491 | |
492 | prettyprint_addr(&transfer->peer, pretty_addr); | |
832af0ba SK |
493 | |
494 | if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack)) | |
495 | { | |
496 | if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) | |
497 | { | |
498 | /* Got ack, ensure we take the (re)transmit path */ | |
499 | transfer->timeout = now; | |
500 | transfer->backoff = 0; | |
9e038946 SK |
501 | if (transfer->block++ != 0) |
502 | transfer->offset += transfer->blocksize - transfer->expansion; | |
832af0ba SK |
503 | } |
504 | else if (ntohs(mess->op) == OP_ERR) | |
505 | { | |
506 | char *p = daemon->packet + sizeof(struct ack); | |
507 | char *end = daemon->packet + len; | |
508 | char *err = next(&p, end); | |
28866e95 | 509 | |
832af0ba SK |
510 | /* Sanitise error message */ |
511 | if (!err) | |
512 | err = ""; | |
513 | else | |
514 | { | |
572b41eb SK |
515 | unsigned char *q, *r; |
516 | for (q = r = (unsigned char *)err; *r; r++) | |
517 | if (isprint(*r)) | |
832af0ba SK |
518 | *(q++) = *r; |
519 | *q = 0; | |
520 | } | |
28866e95 | 521 | |
316e2730 | 522 | my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), |
f2621c7f | 523 | (int)ntohs(mess->block), err, |
28866e95 | 524 | pretty_addr); |
832af0ba SK |
525 | |
526 | /* Got err, ensure we take abort */ | |
527 | transfer->timeout = now; | |
528 | transfer->backoff = 100; | |
529 | } | |
530 | } | |
531 | } | |
532 | ||
533 | if (difftime(now, transfer->timeout) >= 0.0) | |
534 | { | |
535 | int endcon = 0; | |
536 | ||
537 | /* timeout, retransmit */ | |
5aabfc78 | 538 | transfer->timeout += 1 + (1<<transfer->backoff); |
832af0ba SK |
539 | |
540 | /* we overwrote the buffer... */ | |
541 | daemon->srv_save = NULL; | |
542 | ||
543 | if ((len = get_block(daemon->packet, transfer)) == -1) | |
544 | { | |
1b7ecd11 | 545 | len = tftp_err_oops(daemon->packet, transfer->file->filename); |
832af0ba SK |
546 | endcon = 1; |
547 | } | |
548 | else if (++transfer->backoff > 5) | |
549 | { | |
550 | /* don't complain about timeout when we're awaiting the last | |
551 | ACK, some clients never send it */ | |
552 | if (len != 0) | |
28866e95 SK |
553 | { |
554 | my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), | |
555 | transfer->file->filename, pretty_addr); | |
556 | len = 0; | |
557 | endcon = 1; | |
558 | } | |
832af0ba SK |
559 | } |
560 | ||
561 | if (len != 0) | |
562 | while(sendto(transfer->sockfd, daemon->packet, len, 0, | |
563 | (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR); | |
564 | ||
565 | if (endcon || len == 0) | |
566 | { | |
28866e95 SK |
567 | if (!endcon) |
568 | my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, pretty_addr); | |
832af0ba SK |
569 | /* unlink */ |
570 | *up = tmp; | |
571 | free_transfer(transfer); | |
572 | continue; | |
573 | } | |
574 | } | |
575 | ||
576 | up = &transfer->next; | |
28866e95 | 577 | } |
832af0ba SK |
578 | } |
579 | ||
580 | static void free_transfer(struct tftp_transfer *transfer) | |
581 | { | |
582 | close(transfer->sockfd); | |
583 | if (transfer->file && (--transfer->file->refcount) == 0) | |
584 | { | |
585 | close(transfer->file->fd); | |
586 | free(transfer->file); | |
587 | } | |
588 | free(transfer); | |
589 | } | |
590 | ||
591 | static char *next(char **p, char *end) | |
592 | { | |
593 | char *ret = *p; | |
594 | size_t len; | |
595 | ||
596 | if (*(end-1) != 0 || | |
597 | *p == end || | |
598 | (len = strlen(ret)) == 0) | |
599 | return NULL; | |
600 | ||
601 | *p += len + 1; | |
602 | return ret; | |
603 | } | |
604 | ||
605 | static ssize_t tftp_err(int err, char *packet, char *message, char *file) | |
606 | { | |
607 | struct errmess { | |
608 | unsigned short op, err; | |
609 | char message[]; | |
610 | } *mess = (struct errmess *)packet; | |
611 | ssize_t ret = 4; | |
612 | char *errstr = strerror(errno); | |
613 | ||
614 | mess->op = htons(OP_ERR); | |
615 | mess->err = htons(err); | |
616 | ret += (snprintf(mess->message, 500, message, file, errstr) + 1); | |
316e2730 | 617 | my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); |
832af0ba SK |
618 | |
619 | return ret; | |
620 | } | |
621 | ||
1b7ecd11 SK |
622 | static ssize_t tftp_err_oops(char *packet, char *file) |
623 | { | |
624 | return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file); | |
625 | } | |
626 | ||
832af0ba SK |
627 | /* return -1 for error, zero for done. */ |
628 | static ssize_t get_block(char *packet, struct tftp_transfer *transfer) | |
629 | { | |
630 | if (transfer->block == 0) | |
631 | { | |
632 | /* send OACK */ | |
633 | char *p; | |
634 | struct oackmess { | |
635 | unsigned short op; | |
636 | char data[]; | |
637 | } *mess = (struct oackmess *)packet; | |
638 | ||
639 | p = mess->data; | |
640 | mess->op = htons(OP_OACK); | |
641 | if (transfer->opt_blocksize) | |
642 | { | |
643 | p += (sprintf(p, "blksize") + 1); | |
644 | p += (sprintf(p, "%d", transfer->blocksize) + 1); | |
645 | } | |
646 | if (transfer->opt_transize) | |
647 | { | |
648 | p += (sprintf(p,"tsize") + 1); | |
649 | p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1); | |
650 | } | |
651 | ||
652 | return p - packet; | |
653 | } | |
654 | else | |
655 | { | |
656 | /* send data packet */ | |
657 | struct datamess { | |
658 | unsigned short op, block; | |
659 | unsigned char data[]; | |
660 | } *mess = (struct datamess *)packet; | |
661 | ||
9e038946 | 662 | size_t size = transfer->file->size - transfer->offset; |
832af0ba | 663 | |
9e038946 | 664 | if (transfer->offset > transfer->file->size) |
832af0ba SK |
665 | return 0; /* finished */ |
666 | ||
667 | if (size > transfer->blocksize) | |
668 | size = transfer->blocksize; | |
669 | ||
832af0ba SK |
670 | mess->op = htons(OP_DATA); |
671 | mess->block = htons((unsigned short)(transfer->block)); | |
672 | ||
9e038946 SK |
673 | if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 || |
674 | !read_write(transfer->file->fd, mess->data, size, 1)) | |
832af0ba | 675 | return -1; |
9e038946 SK |
676 | |
677 | transfer->expansion = 0; | |
678 | ||
679 | /* Map '\n' to CR-LF in netascii mode */ | |
680 | if (transfer->netascii) | |
681 | { | |
682 | size_t i; | |
683 | int newcarrylf; | |
684 | ||
685 | for (i = 0, newcarrylf = 0; i < size; i++) | |
686 | if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf)) | |
687 | { | |
688 | if (size == transfer->blocksize) | |
689 | { | |
690 | transfer->expansion++; | |
691 | if (i == size - 1) | |
692 | newcarrylf = 1; /* don't expand LF again if it moves to the next block */ | |
693 | } | |
694 | else | |
695 | size++; /* room in this block */ | |
696 | ||
697 | /* make space and insert CR */ | |
698 | memmove(&mess->data[i+1], &mess->data[i], size - (i + 1)); | |
699 | mess->data[i] = '\r'; | |
700 | ||
701 | i++; | |
702 | } | |
703 | transfer->carrylf = newcarrylf; | |
704 | ||
705 | } | |
706 | ||
707 | return size + 4; | |
832af0ba SK |
708 | } |
709 | } | |
710 | ||
711 | #endif |