Line | Count | Source |
1 | | /* dnsmasq is Copyright (c) 2000-2025 Simon Kelley |
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 |
5 | | the Free Software Foundation; version 2 dated June, 1991, or |
6 | | (at your option) version 3 dated 29 June, 2007. |
7 | | |
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. |
12 | | |
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/>. |
15 | | */ |
16 | | |
17 | | #include "dnsmasq.h" |
18 | | |
19 | | #ifdef HAVE_TFTP |
20 | | |
21 | | static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len); |
22 | | static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client); |
23 | | static void free_transfer(struct tftp_transfer *transfer); |
24 | | static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2); |
25 | | static ssize_t tftp_err_oops(char *packet, const char *file); |
26 | | static ssize_t get_block(struct tftp_transfer *transfer); |
27 | | static char *next(char **p, char *end); |
28 | | static void sanitise(char *buf); |
29 | | |
30 | 0 | #define OP_RRQ 1 |
31 | 0 | #define OP_WRQ 2 |
32 | | #define OP_DATA 3 |
33 | 0 | #define OP_ACK 4 |
34 | 0 | #define OP_ERR 5 |
35 | | #define OP_OACK 6 |
36 | | |
37 | 0 | #define ERR_NOTDEF 0 |
38 | 0 | #define ERR_FNF 1 |
39 | 0 | #define ERR_PERM 2 |
40 | | #define ERR_FULL 3 |
41 | 0 | #define ERR_ILL 4 |
42 | 0 | #define ERR_TID 5 |
43 | | |
44 | | static void tftp_request(char *packet, ssize_t plen, struct listener *listen, time_t now) |
45 | 0 | { |
46 | 0 | ssize_t len; |
47 | 0 | char *filename, *mode, *p, *end; |
48 | 0 | union mysockaddr addr, peer; |
49 | 0 | struct msghdr msg; |
50 | 0 | struct iovec iov; |
51 | 0 | struct ifreq ifr; |
52 | 0 | int is_err = 1, if_index = 0, mtu = 0; |
53 | 0 | struct iname *tmp; |
54 | 0 | struct tftp_transfer *transfer = NULL, **up; |
55 | 0 | int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ |
56 | 0 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
57 | 0 | int mtuflag = IP_PMTUDISC_DONT; |
58 | 0 | #endif |
59 | 0 | char namebuff[IF_NAMESIZE]; |
60 | 0 | char *name = NULL; |
61 | 0 | char *prefix = daemon->tftp_prefix; |
62 | 0 | struct tftp_prefix *pref; |
63 | 0 | union all_addr addra; |
64 | 0 | int family = listen->addr.sa.sa_family; |
65 | | /* Can always get recvd interface for IPv6 */ |
66 | 0 | int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6; |
67 | 0 | union { |
68 | 0 | struct cmsghdr align; /* this ensures alignment */ |
69 | 0 | char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
70 | 0 | #if defined(HAVE_LINUX_NETWORK) |
71 | 0 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
72 | | #elif defined(HAVE_SOLARIS_NETWORK) |
73 | | char control[CMSG_SPACE(sizeof(struct in_addr)) + |
74 | | CMSG_SPACE(sizeof(unsigned int))]; |
75 | | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
76 | | char control[CMSG_SPACE(sizeof(struct in_addr)) + |
77 | | CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
78 | | #endif |
79 | 0 | } control_u; |
80 | |
|
81 | 0 | msg.msg_controllen = sizeof(control_u); |
82 | 0 | msg.msg_control = control_u.control; |
83 | 0 | msg.msg_flags = 0; |
84 | 0 | msg.msg_name = &peer; |
85 | 0 | msg.msg_namelen = sizeof(peer); |
86 | 0 | msg.msg_iov = &iov; |
87 | 0 | msg.msg_iovlen = 1; |
88 | | |
89 | | /* packet buff is DNS name workspace. */ |
90 | 0 | iov.iov_base = packet; |
91 | 0 | iov.iov_len = plen; |
92 | | |
93 | 0 | if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) |
94 | 0 | return; |
95 | | |
96 | 0 | #ifdef HAVE_DUMPFILE |
97 | 0 | dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd); |
98 | 0 | #endif |
99 | | |
100 | | /* Can always get recvd interface for IPv6 */ |
101 | 0 | if (!check_dest) |
102 | 0 | { |
103 | 0 | if (listen->iface) |
104 | 0 | { |
105 | 0 | addr = listen->iface->addr; |
106 | 0 | name = listen->iface->name; |
107 | 0 | mtu = listen->iface->mtu; |
108 | 0 | if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) |
109 | 0 | mtu = daemon->tftp_mtu; |
110 | 0 | } |
111 | 0 | else |
112 | 0 | { |
113 | | /* we're listening on an address that doesn't appear on an interface, |
114 | | ask the kernel what the socket is bound to */ |
115 | 0 | socklen_t tcp_len = sizeof(union mysockaddr); |
116 | 0 | if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1) |
117 | 0 | return; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | else |
121 | 0 | { |
122 | 0 | struct cmsghdr *cmptr; |
123 | |
|
124 | 0 | if (msg.msg_controllen < sizeof(struct cmsghdr)) |
125 | 0 | return; |
126 | | |
127 | 0 | addr.sa.sa_family = family; |
128 | | |
129 | 0 | #if defined(HAVE_LINUX_NETWORK) |
130 | 0 | if (family == AF_INET) |
131 | 0 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
132 | 0 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
133 | 0 | { |
134 | 0 | union { |
135 | 0 | unsigned char *c; |
136 | 0 | struct in_pktinfo *p; |
137 | 0 | } p; |
138 | 0 | p.c = CMSG_DATA(cmptr); |
139 | 0 | addr.in.sin_addr = p.p->ipi_spec_dst; |
140 | 0 | if_index = p.p->ipi_ifindex; |
141 | 0 | } |
142 | | |
143 | | #elif defined(HAVE_SOLARIS_NETWORK) |
144 | | if (family == AF_INET) |
145 | | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
146 | | { |
147 | | union { |
148 | | unsigned char *c; |
149 | | struct in_addr *a; |
150 | | unsigned int *i; |
151 | | } p; |
152 | | p.c = CMSG_DATA(cmptr); |
153 | | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) |
154 | | addr.in.sin_addr = *(p.a); |
155 | | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
156 | | if_index = *(p.i); |
157 | | } |
158 | | |
159 | | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
160 | | if (family == AF_INET) |
161 | | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
162 | | { |
163 | | union { |
164 | | unsigned char *c; |
165 | | struct in_addr *a; |
166 | | struct sockaddr_dl *s; |
167 | | } p; |
168 | | p.c = CMSG_DATA(cmptr); |
169 | | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) |
170 | | addr.in.sin_addr = *(p.a); |
171 | | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
172 | | if_index = p.s->sdl_index; |
173 | | } |
174 | | |
175 | | #endif |
176 | |
|
177 | 0 | if (family == AF_INET6) |
178 | 0 | { |
179 | 0 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
180 | 0 | if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) |
181 | 0 | { |
182 | 0 | union { |
183 | 0 | unsigned char *c; |
184 | 0 | struct in6_pktinfo *p; |
185 | 0 | } p; |
186 | 0 | p.c = CMSG_DATA(cmptr); |
187 | | |
188 | 0 | addr.in6.sin6_addr = p.p->ipi6_addr; |
189 | 0 | if_index = p.p->ipi6_ifindex; |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | 0 | if (!indextoname(listen->tftpfd, if_index, namebuff)) |
194 | 0 | return; |
195 | | |
196 | 0 | name = namebuff; |
197 | | |
198 | 0 | if (family == AF_INET6) |
199 | 0 | addra.addr6 = addr.in6.sin6_addr; |
200 | 0 | else |
201 | 0 | addra.addr4 = addr.in.sin_addr; |
202 | | |
203 | 0 | if (daemon->tftp_interfaces) |
204 | 0 | { |
205 | | /* dedicated tftp interface list */ |
206 | 0 | for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next) |
207 | 0 | if (tmp->name && wildcard_match(tmp->name, name)) |
208 | 0 | break; |
209 | |
|
210 | 0 | if (!tmp) |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | else |
214 | 0 | { |
215 | | /* Do the same as DHCP */ |
216 | 0 | if (!iface_check(family, &addra, name, NULL)) |
217 | 0 | { |
218 | 0 | if (!option_bool(OPT_CLEVERBIND)) |
219 | 0 | enumerate_interfaces(0); |
220 | 0 | if (!loopback_exception(listen->tftpfd, family, &addra, name) && |
221 | 0 | !label_exception(if_index, family, &addra)) |
222 | 0 | return; |
223 | 0 | } |
224 | | |
225 | 0 | #ifdef HAVE_DHCP |
226 | | /* allowed interfaces are the same as for DHCP */ |
227 | 0 | for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) |
228 | 0 | if (tmp->name && (tmp->flags & INAME_4) && (tmp->flags & INAME_6) && |
229 | 0 | wildcard_match(tmp->name, name)) |
230 | 0 | return; |
231 | 0 | #endif |
232 | 0 | } |
233 | | |
234 | 0 | safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); |
235 | 0 | if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) |
236 | 0 | { |
237 | 0 | mtu = ifr.ifr_mtu; |
238 | 0 | if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) |
239 | 0 | mtu = daemon->tftp_mtu; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | /* Failed to get interface mtu - can use configured value. */ |
244 | 0 | if (mtu == 0) |
245 | 0 | mtu = daemon->tftp_mtu; |
246 | | |
247 | | /* data transfer via server listening socket */ |
248 | 0 | if (option_bool(OPT_SINGLE_PORT)) |
249 | 0 | { |
250 | 0 | int tftp_cnt; |
251 | |
|
252 | 0 | for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) |
253 | 0 | { |
254 | 0 | tftp_cnt++; |
255 | |
|
256 | 0 | if (sockaddr_isequal(&peer, &transfer->peer)) |
257 | 0 | { |
258 | 0 | if (ntohs(*((unsigned short *)packet)) == OP_RRQ) |
259 | 0 | { |
260 | | /* Handle repeated RRQ or abandoned transfer from same host and port |
261 | | by unlinking and reusing the struct transfer. */ |
262 | 0 | *up = transfer->next; |
263 | 0 | break; |
264 | 0 | } |
265 | 0 | else |
266 | 0 | { |
267 | 0 | handle_tftp(packet, now, transfer, len); |
268 | 0 | return; |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | | /* Enforce simultaneous transfer limit. In non-single-port mode |
274 | | this is done by not listening on the server socket when |
275 | | too many transfers are in progress. */ |
276 | 0 | if (!transfer && tftp_cnt >= daemon->tftp_max) |
277 | 0 | return; |
278 | 0 | } |
279 | | |
280 | 0 | if (name) |
281 | 0 | { |
282 | | /* check for per-interface prefix */ |
283 | 0 | for (pref = daemon->if_prefix; pref; pref = pref->next) |
284 | 0 | if (strcmp(pref->interface, name) == 0) |
285 | 0 | prefix = pref->prefix; |
286 | 0 | } |
287 | |
|
288 | 0 | if (family == AF_INET) |
289 | 0 | { |
290 | 0 | addr.in.sin_port = htons(port); |
291 | | #ifdef HAVE_SOCKADDR_SA_LEN |
292 | | addr.in.sin_len = sizeof(addr.in); |
293 | | #endif |
294 | 0 | } |
295 | 0 | else |
296 | 0 | { |
297 | 0 | addr.in6.sin6_port = htons(port); |
298 | 0 | addr.in6.sin6_flowinfo = 0; |
299 | 0 | addr.in6.sin6_scope_id = 0; |
300 | | #ifdef HAVE_SOCKADDR_SA_LEN |
301 | | addr.in6.sin6_len = sizeof(addr.in6); |
302 | | #endif |
303 | 0 | } |
304 | | |
305 | | /* May reuse struct transfer from abandoned transfer in single port mode. */ |
306 | 0 | if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer)))) |
307 | 0 | return; |
308 | | |
309 | 0 | memset(transfer, 0, sizeof(struct tftp_transfer)); |
310 | | |
311 | 0 | if (option_bool(OPT_SINGLE_PORT)) |
312 | 0 | transfer->sockfd = listen->tftpfd; |
313 | 0 | else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1) |
314 | 0 | { |
315 | 0 | free(transfer); |
316 | 0 | return; |
317 | 0 | } |
318 | | |
319 | 0 | transfer->peer = peer; |
320 | 0 | transfer->source = addra; |
321 | 0 | transfer->if_index = if_index; |
322 | 0 | transfer->timeout = 2; |
323 | 0 | transfer->start = now; |
324 | 0 | transfer->backoff = 1; |
325 | 0 | transfer->block = 1; |
326 | 0 | transfer->ackprev = 0; |
327 | 0 | transfer->block_hi = 0; |
328 | 0 | transfer->blocksize = 512; |
329 | 0 | transfer->windowsize = 1; |
330 | | |
331 | 0 | (void)prettyprint_addr(&peer, daemon->addrbuff); |
332 | | |
333 | | /* if we have a nailed-down range, iterate until we find a free one. */ |
334 | 0 | while (!option_bool(OPT_SINGLE_PORT)) |
335 | 0 | { |
336 | 0 | if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 || |
337 | 0 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
338 | 0 | setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || |
339 | 0 | #endif |
340 | 0 | !fix_fd(transfer->sockfd)) |
341 | 0 | { |
342 | 0 | if (errno == EADDRINUSE && daemon->start_tftp_port != 0) |
343 | 0 | { |
344 | 0 | if (++port <= daemon->end_tftp_port) |
345 | 0 | { |
346 | 0 | if (family == AF_INET) |
347 | 0 | addr.in.sin_port = htons(port); |
348 | 0 | else |
349 | 0 | addr.in6.sin6_port = htons(port); |
350 | | |
351 | 0 | continue; |
352 | 0 | } |
353 | 0 | my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); |
354 | 0 | } |
355 | 0 | free_transfer(transfer); |
356 | 0 | return; |
357 | 0 | } |
358 | 0 | break; |
359 | 0 | } |
360 | | |
361 | 0 | p = packet + 2; |
362 | 0 | end = packet + len; |
363 | |
|
364 | 0 | len = 0; |
365 | | |
366 | 0 | if (ntohs(*((unsigned short *)packet)) == OP_WRQ) |
367 | 0 | len = tftp_err(ERR_ILL, packet, _("unsupported write request from %s"),daemon->addrbuff, NULL); |
368 | 0 | else if (ntohs(*((unsigned short *)packet)) == OP_RRQ) |
369 | 0 | { |
370 | 0 | if (!(filename = next(&p, end))) |
371 | 0 | len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL); |
372 | 0 | else if (!(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) |
373 | 0 | len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"),daemon->addrbuff, NULL); |
374 | 0 | else |
375 | 0 | { |
376 | 0 | char *opt, *arg; |
377 | | |
378 | 0 | if (strcasecmp(mode, "netascii") == 0) |
379 | 0 | transfer->netascii = 1; |
380 | | |
381 | 0 | while ((opt = next(&p, end)) && (arg = next(&p, end))) |
382 | 0 | { |
383 | 0 | unsigned int val = atoi(arg); |
384 | | |
385 | 0 | if (strcasecmp(opt, "blksize") == 0 && !option_bool(OPT_TFTP_NOBLOCK)) |
386 | 0 | { |
387 | | /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */ |
388 | 0 | int overhead = (family == AF_INET) ? 32 : 52; |
389 | 0 | if (val < 1) |
390 | 0 | val = 1; |
391 | 0 | if (val > (unsigned)daemon->packet_buff_sz - 4) |
392 | 0 | val = (unsigned)daemon->packet_buff_sz - 4; |
393 | 0 | if (mtu != 0 && val > (unsigned)mtu - overhead) |
394 | 0 | val = (unsigned)mtu - overhead; |
395 | 0 | transfer->blocksize = val; |
396 | 0 | transfer->opt_blocksize = 1; |
397 | 0 | transfer->block = 0; |
398 | 0 | } |
399 | 0 | else if (strcasecmp(opt, "tsize") == 0 && !transfer->netascii) |
400 | 0 | { |
401 | 0 | transfer->opt_transize = 1; |
402 | 0 | transfer->block = 0; |
403 | 0 | } |
404 | 0 | else if (strcasecmp(opt, "timeout") == 0) |
405 | 0 | { |
406 | 0 | if (val > 255) |
407 | 0 | val = 255; |
408 | 0 | transfer->timeout = val; |
409 | 0 | transfer->opt_timeout = 1; |
410 | 0 | transfer->block = 0; |
411 | 0 | } |
412 | 0 | else if (strcasecmp(opt, "windowsize") == 0 && !transfer->netascii) |
413 | 0 | { |
414 | | /* windowsize option only supported for binary transfers. */ |
415 | 0 | if (val < 1) |
416 | 0 | val = 1; |
417 | 0 | if (val > TFTP_MAX_WINDOW) |
418 | 0 | val = TFTP_MAX_WINDOW; |
419 | 0 | transfer->windowsize = val; |
420 | 0 | transfer->opt_windowsize = 1; |
421 | 0 | transfer->block = 0; |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | /* cope with backslashes from windows boxen. */ |
426 | 0 | for (p = filename; *p; p++) |
427 | 0 | if (*p == '\\') |
428 | 0 | *p = '/'; |
429 | 0 | else if (option_bool(OPT_TFTP_LC)) |
430 | 0 | *p = tolower((unsigned char)*p); |
431 | | |
432 | 0 | strcpy(daemon->namebuff, "/"); |
433 | 0 | if (prefix) |
434 | 0 | { |
435 | 0 | if (prefix[0] == '/') |
436 | 0 | daemon->namebuff[0] = 0; |
437 | 0 | strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); |
438 | 0 | if (prefix[strlen(prefix)-1] != '/') |
439 | 0 | strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
440 | | |
441 | 0 | if (option_bool(OPT_TFTP_APREF_IP)) |
442 | 0 | { |
443 | 0 | size_t oldlen = strlen(daemon->namebuff); |
444 | 0 | struct stat statbuf; |
445 | | |
446 | 0 | strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff)); |
447 | 0 | strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
448 | | |
449 | | /* remove unique-directory if it doesn't exist */ |
450 | 0 | if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) |
451 | 0 | daemon->namebuff[oldlen] = 0; |
452 | 0 | } |
453 | | |
454 | 0 | if (option_bool(OPT_TFTP_APREF_MAC)) |
455 | 0 | { |
456 | 0 | unsigned char *macaddr = NULL; |
457 | 0 | unsigned char macbuf[DHCP_CHADDR_MAX]; |
458 | | |
459 | 0 | #ifdef HAVE_DHCP |
460 | 0 | if (daemon->dhcp && peer.sa.sa_family == AF_INET) |
461 | 0 | { |
462 | | /* Check if the client IP is in our lease database */ |
463 | 0 | struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr); |
464 | 0 | if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN) |
465 | 0 | macaddr = lease->hwaddr; |
466 | 0 | } |
467 | 0 | #endif |
468 | | |
469 | | /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */ |
470 | 0 | if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0) |
471 | 0 | macaddr = macbuf; |
472 | | |
473 | 0 | if (macaddr) |
474 | 0 | { |
475 | 0 | size_t oldlen = strlen(daemon->namebuff); |
476 | 0 | struct stat statbuf; |
477 | | |
478 | 0 | snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/", |
479 | 0 | macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); |
480 | | |
481 | | /* remove unique-directory if it doesn't exist */ |
482 | 0 | if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) |
483 | 0 | daemon->namebuff[oldlen] = 0; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | /* Absolute pathnames OK if they match prefix */ |
488 | 0 | if (filename[0] == '/') |
489 | 0 | { |
490 | 0 | if (strstr(filename, daemon->namebuff) == filename) |
491 | 0 | daemon->namebuff[0] = 0; |
492 | 0 | else |
493 | 0 | filename++; |
494 | 0 | } |
495 | 0 | } |
496 | 0 | else if (filename[0] == '/') |
497 | 0 | daemon->namebuff[0] = 0; |
498 | 0 | strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
499 | | |
500 | | /* check permissions and open file */ |
501 | 0 | if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff))) |
502 | 0 | { |
503 | 0 | transfer->lastack = transfer->block; |
504 | 0 | transfer->retransmit = now + transfer->timeout; |
505 | | /* This packet is may be the first data packet, but only if windowsize == 1 |
506 | | To get windowsize greater then one requires an option negotiation, |
507 | | in which case this packet is the OACK. */ |
508 | 0 | if ((len = get_block(transfer)) == -1) |
509 | 0 | len = tftp_err_oops(packet, daemon->namebuff); |
510 | 0 | else |
511 | 0 | { |
512 | 0 | is_err = 0; |
513 | | /* get_block put the packet to send in a different buffer. */ |
514 | 0 | packet = daemon->packet; |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | 0 | } |
519 | | |
520 | 0 | if (len) |
521 | 0 | { |
522 | 0 | send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index); |
523 | | |
524 | 0 | #ifdef HAVE_DUMPFILE |
525 | 0 | dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); |
526 | 0 | #endif |
527 | 0 | } |
528 | | |
529 | 0 | if (is_err) |
530 | 0 | free_transfer(transfer); |
531 | 0 | else |
532 | 0 | { |
533 | 0 | transfer->next = daemon->tftp_trans; |
534 | 0 | daemon->tftp_trans = transfer; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client) |
539 | 0 | { |
540 | 0 | char *namebuff = daemon->namebuff; |
541 | 0 | struct tftp_file *file; |
542 | 0 | struct tftp_transfer *t; |
543 | 0 | uid_t uid = geteuid(); |
544 | 0 | struct stat statbuf; |
545 | 0 | int fd = -1; |
546 | | |
547 | | /* trick to ban moving out of the subtree */ |
548 | 0 | if (prefix && strstr(namebuff, "/../")) |
549 | 0 | goto perm; |
550 | | |
551 | 0 | if ((fd = open(namebuff, O_RDONLY)) == -1) |
552 | 0 | { |
553 | 0 | if (errno == ENOENT) |
554 | 0 | { |
555 | 0 | *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client); |
556 | 0 | return NULL; |
557 | 0 | } |
558 | 0 | else if (errno == EACCES) |
559 | 0 | goto perm; |
560 | 0 | else |
561 | 0 | goto oops; |
562 | 0 | } |
563 | | |
564 | | /* stat the file descriptor to avoid stat->open races */ |
565 | 0 | if (fstat(fd, &statbuf) == -1) |
566 | 0 | goto oops; |
567 | | |
568 | | /* running as root, must be world-readable */ |
569 | 0 | if (uid == 0) |
570 | 0 | { |
571 | 0 | if (!(statbuf.st_mode & S_IROTH)) |
572 | 0 | goto perm; |
573 | 0 | } |
574 | | /* in secure mode, must be owned by user running dnsmasq */ |
575 | 0 | else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) |
576 | 0 | goto perm; |
577 | | |
578 | | /* If we're doing many transfers from the same file, only |
579 | | open it once this saves lots of file descriptors |
580 | | when mass-booting a big cluster, for instance. |
581 | | Be conservative and only share when inode and name match |
582 | | this keeps error messages sane. */ |
583 | 0 | for (t = daemon->tftp_trans; t; t = t->next) |
584 | 0 | if (t->file->dev == statbuf.st_dev && |
585 | 0 | t->file->inode == statbuf.st_ino && |
586 | 0 | strcmp(t->file->filename, namebuff) == 0) |
587 | 0 | { |
588 | 0 | close(fd); |
589 | 0 | t->file->refcount++; |
590 | 0 | return t->file; |
591 | 0 | } |
592 | | |
593 | 0 | if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1))) |
594 | 0 | { |
595 | 0 | errno = ENOMEM; |
596 | 0 | goto oops; |
597 | 0 | } |
598 | | |
599 | 0 | file->fd = fd; |
600 | 0 | file->size = statbuf.st_size; |
601 | 0 | file->dev = statbuf.st_dev; |
602 | 0 | file->inode = statbuf.st_ino; |
603 | 0 | file->posn = 0; |
604 | 0 | file->refcount = 1; |
605 | 0 | strcpy(file->filename, namebuff); |
606 | 0 | return file; |
607 | | |
608 | 0 | perm: |
609 | 0 | *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES)); |
610 | 0 | if (fd != -1) |
611 | 0 | close(fd); |
612 | 0 | return NULL; |
613 | | |
614 | 0 | oops: |
615 | 0 | *len = tftp_err_oops(packet, namebuff); |
616 | 0 | if (fd != -1) |
617 | 0 | close(fd); |
618 | 0 | return NULL; |
619 | 0 | } |
620 | | |
621 | | void check_tftp_listeners(time_t now) |
622 | 0 | { |
623 | | /* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */ |
624 | 0 | char *packet = daemon->workspacename; |
625 | 0 | ssize_t plen = MAXDNAME * 2; |
626 | 0 | struct listener *listener; |
627 | 0 | struct tftp_transfer *transfer, *tmp, **up; |
628 | | |
629 | 0 | for (listener = daemon->listeners; listener; listener = listener->next) |
630 | 0 | if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN)) |
631 | 0 | tftp_request(packet, plen, listener, now); |
632 | | |
633 | | /* In single port mode, all packets come via port 69 and tftp_request() */ |
634 | 0 | if (!option_bool(OPT_SINGLE_PORT)) |
635 | 0 | for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) |
636 | 0 | if (poll_check(transfer->sockfd, POLLIN)) |
637 | 0 | { |
638 | 0 | union mysockaddr peer; |
639 | 0 | socklen_t addr_len = sizeof(union mysockaddr); |
640 | 0 | ssize_t len; |
641 | | |
642 | 0 | if ((len = recvfrom(transfer->sockfd, packet, plen, 0, &peer.sa, &addr_len)) > 0) |
643 | 0 | { |
644 | 0 | #ifdef HAVE_DUMPFILE |
645 | 0 | dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd); |
646 | 0 | #endif |
647 | |
|
648 | 0 | if (sockaddr_isequal(&peer, &transfer->peer)) |
649 | 0 | handle_tftp(packet, now, transfer, len); |
650 | 0 | else |
651 | 0 | { |
652 | | /* Wrong source address. See rfc1350 para 4. */ |
653 | 0 | prettyprint_addr(&peer, daemon->addrbuff); |
654 | 0 | len = tftp_err(ERR_TID, packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL); |
655 | 0 | while(retry_send(sendto(transfer->sockfd, packet, len, 0, &peer.sa, sa_len(&peer)))); |
656 | |
|
657 | 0 | #ifdef HAVE_DUMPFILE |
658 | 0 | dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); |
659 | 0 | #endif |
660 | 0 | } |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | 0 | for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) |
665 | 0 | { |
666 | 0 | int endcon = 0, error = 0, timeout = 0; |
667 | | |
668 | 0 | tmp = transfer->next; |
669 | | |
670 | | /* ->start set to zero in handle_tftp() when we recv an error packet. */ |
671 | 0 | if (transfer->start == 0) |
672 | 0 | endcon = error = 1; |
673 | 0 | else if (difftime(now, transfer->start) > TFTP_TRANSFER_TIME) |
674 | 0 | { |
675 | 0 | endcon = 1; |
676 | | /* don't complain about timeout when we're awaiting the last |
677 | | ACK, some clients never send it */ |
678 | 0 | if (get_block(transfer) > 0) |
679 | 0 | error = timeout = 1; |
680 | 0 | } |
681 | 0 | else if (difftime(now, transfer->retransmit) >= 0.0) |
682 | 0 | { |
683 | | /* Do transmission or re-transmission. When we get an ACK, the call to handle_tftp() |
684 | | bumps transfer->lastack and trips the retransmit timer so that we send the next block(s) |
685 | | here. */ |
686 | 0 | ssize_t len; |
687 | 0 | unsigned int i, winsize; |
688 | | |
689 | 0 | transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2)); |
690 | 0 | transfer->backoff++; |
691 | 0 | transfer->block = transfer->lastack; |
692 | | |
693 | | /* send a window'a worth of blocks unless we're retransmitting OACK */ |
694 | 0 | winsize = transfer->block ? transfer->windowsize : 1; |
695 | | |
696 | 0 | for (i = 0; i < winsize; i++, transfer->block++) |
697 | 0 | { |
698 | 0 | if ((len = get_block(transfer)) == 0) |
699 | 0 | { |
700 | 0 | if (i == 0) |
701 | 0 | endcon = 1; /* got last ACK */ |
702 | |
|
703 | 0 | break; |
704 | 0 | } |
705 | | |
706 | 0 | if (len == -1) |
707 | 0 | { |
708 | 0 | len = tftp_err_oops(daemon->packet, transfer->file->filename); |
709 | 0 | endcon = error = 1; |
710 | 0 | } |
711 | | |
712 | 0 | send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len, |
713 | 0 | &transfer->peer, &transfer->source, transfer->if_index); |
714 | 0 | #ifdef HAVE_DUMPFILE |
715 | 0 | dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd); |
716 | 0 | #endif |
717 | 0 | } |
718 | | |
719 | | /* prefetch the block we'll probably need when we get an ACK. */ |
720 | 0 | if (!endcon) |
721 | 0 | get_block(transfer); |
722 | 0 | } |
723 | | |
724 | 0 | if (endcon) |
725 | 0 | { |
726 | 0 | strcpy(daemon->namebuff, transfer->file->filename); |
727 | 0 | sanitise(daemon->namebuff); |
728 | 0 | (void)prettyprint_addr(&transfer->peer, daemon->addrbuff); |
729 | 0 | if (timeout) |
730 | 0 | my_syslog(MS_TFTP | LOG_ERR, _("timeout sending %s to %s"), daemon->namebuff, daemon->addrbuff); |
731 | 0 | else if (error) |
732 | 0 | my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), daemon->namebuff, daemon->addrbuff); |
733 | 0 | else |
734 | 0 | my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, daemon->addrbuff); |
735 | | |
736 | | /* unlink */ |
737 | 0 | *up = tmp; |
738 | 0 | if (error) |
739 | 0 | free_transfer(transfer); |
740 | 0 | else |
741 | 0 | { |
742 | | /* put on queue to be sent to script and deleted */ |
743 | 0 | transfer->next = daemon->tftp_done_trans; |
744 | 0 | daemon->tftp_done_trans = transfer; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | else |
748 | 0 | up = &transfer->next; |
749 | 0 | } |
750 | 0 | } |
751 | | |
752 | | static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len) |
753 | 0 | { |
754 | 0 | struct ack { |
755 | 0 | unsigned short op, block; |
756 | 0 | } *mess = (struct ack *)packet; |
757 | | |
758 | 0 | if (len >= (ssize_t)sizeof(struct ack)) |
759 | 0 | { |
760 | 0 | if (ntohs(mess->op) == OP_ACK) |
761 | 0 | { |
762 | | /* Handle 16-bit block number wrap-around. */ |
763 | 0 | u16 new = ntohs(mess->block); |
764 | 0 | u32 block; |
765 | | |
766 | | /* If the last ack received was in the top quarter of a 64k block |
767 | | and this one is in the bottom quarter, assume it has wrapped. |
768 | | |
769 | | Since this is UDP and an old packet can in theory wander in we may also |
770 | | need to drop back to a previous segment. Such an ACK is ignored below; |
771 | | here we're just getting the most likely 32 bit value from the |
772 | | 16 bits that we have. */ |
773 | 0 | if (new <= 0x4000 && transfer->ackprev >= 0xc000) |
774 | 0 | transfer->block_hi++; |
775 | 0 | else if (new >= 0xc000 && transfer->ackprev <= 0x4000 && transfer->block_hi != 0) |
776 | 0 | transfer->block_hi--; |
777 | |
|
778 | 0 | transfer->ackprev = new; |
779 | 0 | block = (((u32)transfer->block_hi) << 16) + (u32)new; |
780 | | |
781 | | /* Ignore duplicate ACKs and ACKs for blocks we've not yet sent. */ |
782 | 0 | if (block >= transfer->lastack && |
783 | 0 | block <= transfer->block) |
784 | 0 | { |
785 | | /* Got ack, move forward and ensure we take the (re)transmit path */ |
786 | 0 | transfer->retransmit = transfer->start = now; |
787 | 0 | transfer->backoff = 0; |
788 | 0 | transfer->lastack = block + 1; |
789 | | |
790 | | /* We have no easy function from block no. to file offset when |
791 | | expanding line breaks in netascii mode, so we update the offset here |
792 | | as each block is acknowledged. This explains why the window size must be |
793 | | one for a netascii transfer; to avoid the block no. doing anything |
794 | | other than incrementing by one. */ |
795 | 0 | if (transfer->netascii && block != 0) |
796 | 0 | { |
797 | 0 | transfer->offset += (off_t)transfer->blocksize - (off_t)transfer->expansion; |
798 | 0 | transfer->lastcarrylf = transfer->carrylf; |
799 | 0 | } |
800 | 0 | } |
801 | 0 | } |
802 | 0 | else if (ntohs(mess->op) == OP_ERR) |
803 | 0 | { |
804 | 0 | char *p = packet + sizeof(struct ack); |
805 | 0 | char *end = packet + len; |
806 | 0 | char *err = next(&p, end); |
807 | | |
808 | 0 | (void)prettyprint_addr(&transfer->peer, daemon->addrbuff); |
809 | | |
810 | | /* Sanitise error message */ |
811 | 0 | if (!err) |
812 | 0 | err = ""; |
813 | 0 | else |
814 | 0 | sanitise(err); |
815 | | |
816 | 0 | my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), |
817 | 0 | (int)ntohs(mess->block), err, |
818 | 0 | daemon->addrbuff); |
819 | | |
820 | | /* Got err, ensure we take abort */ |
821 | 0 | transfer->start = 0; |
822 | 0 | } |
823 | 0 | } |
824 | 0 | } |
825 | | |
826 | | static void free_transfer(struct tftp_transfer *transfer) |
827 | 0 | { |
828 | 0 | if (!option_bool(OPT_SINGLE_PORT)) |
829 | 0 | close(transfer->sockfd); |
830 | |
|
831 | 0 | if (transfer->file && (--transfer->file->refcount) == 0) |
832 | 0 | { |
833 | 0 | close(transfer->file->fd); |
834 | 0 | free(transfer->file); |
835 | 0 | } |
836 | | |
837 | 0 | free(transfer); |
838 | 0 | } |
839 | | |
840 | | static char *next(char **p, char *end) |
841 | 0 | { |
842 | 0 | char *n, *ret = *p; |
843 | | |
844 | | /* Look for end of string, without running off the end of the packet. */ |
845 | 0 | for (n = *p; n < end && *n != 0; n++); |
846 | | |
847 | | /* ran off the end or zero length string - failed */ |
848 | 0 | if (n == end || n == ret) |
849 | 0 | return NULL; |
850 | | |
851 | 0 | *p = n + 1; |
852 | 0 | return ret; |
853 | 0 | } |
854 | | |
855 | | /* If we don't do anything, don't write the the input/ouptut |
856 | | buffer. This allows us to pass in safe read-only strings constants. */ |
857 | | static void sanitise(char *buf) |
858 | 0 | { |
859 | 0 | unsigned char *q, *r; |
860 | |
|
861 | 0 | for (q = r = (unsigned char *)buf; *r; r++) |
862 | 0 | if (isprint((int)*r)) |
863 | 0 | { |
864 | 0 | if (q != r) |
865 | 0 | *q = *r; |
866 | 0 | q++; |
867 | 0 | } |
868 | | |
869 | 0 | if (q != r) |
870 | 0 | *q = 0; |
871 | 0 | } |
872 | | |
873 | 0 | #define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ |
874 | | static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2) |
875 | 0 | { |
876 | 0 | struct errmess { |
877 | 0 | unsigned short op, err; |
878 | 0 | char message[]; |
879 | 0 | } *mess = (struct errmess *)packet; |
880 | 0 | ssize_t len, ret = 4; |
881 | |
|
882 | 0 | if (file) |
883 | 0 | sanitise(file); |
884 | | |
885 | 0 | mess->op = htons(OP_ERR); |
886 | 0 | mess->err = htons(err); |
887 | 0 | len = snprintf(mess->message, MAXMESSAGE, message, file, arg2); |
888 | 0 | ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ |
889 | | |
890 | 0 | if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP)) |
891 | 0 | my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); |
892 | | |
893 | 0 | return ret; |
894 | 0 | } |
895 | | |
896 | | static ssize_t tftp_err_oops(char *packet, const char *file) |
897 | 0 | { |
898 | | /* May have >1 refs to file, so potentially mangle a copy of the name */ |
899 | 0 | if (file != daemon->namebuff) |
900 | 0 | strcpy(daemon->namebuff, file); |
901 | 0 | return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno)); |
902 | 0 | } |
903 | | |
904 | | /* return -1 for error, zero for done. */ |
905 | | static ssize_t get_block(struct tftp_transfer *transfer) |
906 | 0 | { |
907 | 0 | static off_t saved_offset = 0; |
908 | 0 | static ssize_t saved_len = 0; |
909 | |
|
910 | 0 | if (transfer->block == 0) |
911 | 0 | { |
912 | | /* send OACK */ |
913 | 0 | char *p; |
914 | 0 | struct oackmess { |
915 | 0 | unsigned short op; |
916 | 0 | char data[]; |
917 | 0 | } *mess = (struct oackmess *)daemon->packet; |
918 | | |
919 | | /* we overwrote the buffer... */ |
920 | 0 | daemon->srv_save = NULL; |
921 | 0 | memset(daemon->packet, 0, daemon->packet_buff_sz); |
922 | | |
923 | 0 | p = mess->data; |
924 | 0 | mess->op = htons(OP_OACK); |
925 | 0 | if (transfer->opt_blocksize) |
926 | 0 | { |
927 | 0 | p += (sprintf(p, "blksize") + 1); |
928 | 0 | p += (sprintf(p, "%u", transfer->blocksize) + 1); |
929 | 0 | } |
930 | 0 | if (transfer->opt_transize) |
931 | 0 | { |
932 | 0 | p += (sprintf(p,"tsize") + 1); |
933 | 0 | p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1); |
934 | 0 | } |
935 | 0 | if (transfer->opt_timeout) |
936 | 0 | { |
937 | 0 | p += (sprintf(p,"timeout") + 1); |
938 | 0 | p += (sprintf(p, "%u", transfer->timeout) + 1); |
939 | 0 | } |
940 | 0 | if (transfer->opt_windowsize) |
941 | 0 | { |
942 | 0 | p += (sprintf(p,"windowsize") + 1); |
943 | 0 | p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1); |
944 | 0 | } |
945 | | |
946 | 0 | return p - daemon->packet; |
947 | 0 | } |
948 | 0 | else |
949 | 0 | { |
950 | | /* send data packet */ |
951 | 0 | struct datamess { |
952 | 0 | unsigned short op, block; |
953 | 0 | unsigned char data[]; |
954 | 0 | } *mess = (struct datamess *)daemon->packet; |
955 | | |
956 | 0 | size_t size; |
957 | | |
958 | 0 | if (!transfer->netascii) |
959 | 0 | transfer->offset = (off_t)(transfer->block - 1) * (off_t)transfer->blocksize; |
960 | | |
961 | 0 | if (transfer->offset > transfer->file->size) |
962 | 0 | return 0; /* finished */ |
963 | | |
964 | | /* We may have a prefetched block already in the buffer. */ |
965 | 0 | if (daemon->srv_save == transfer && saved_offset == transfer->offset) |
966 | 0 | return saved_len; |
967 | | |
968 | | /* we overwrote the buffer... */ |
969 | 0 | daemon->srv_save = NULL; |
970 | |
|
971 | 0 | if ((size = transfer->file->size - transfer->offset) > (size_t)transfer->blocksize) |
972 | 0 | size = (size_t)transfer->blocksize; |
973 | | |
974 | 0 | mess->op = htons(OP_DATA); |
975 | 0 | mess->block = htons((unsigned short)(transfer->block)); |
976 | |
|
977 | 0 | if (size != 0) |
978 | 0 | { |
979 | 0 | if (transfer->file->posn != transfer->offset && |
980 | 0 | lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1) |
981 | 0 | return -1; |
982 | | |
983 | 0 | if (!read_write(transfer->file->fd, mess->data, size, RW_READ)) |
984 | 0 | return -1; |
985 | | |
986 | 0 | transfer->file->posn = transfer->offset + size; |
987 | 0 | } |
988 | | |
989 | | /* Map '\n' to CR-LF in netascii mode */ |
990 | 0 | if (transfer->netascii) |
991 | 0 | { |
992 | 0 | size_t i; |
993 | | |
994 | | /* Map '\n' to CR-LF in netascii mode */ |
995 | 0 | transfer->expansion = transfer->carrylf = 0; |
996 | | |
997 | 0 | for (i = 0; i < size; i++) |
998 | 0 | if (mess->data[i] == '\n' && (i != 0 || !transfer->lastcarrylf)) |
999 | 0 | { |
1000 | 0 | transfer->expansion++; |
1001 | |
|
1002 | 0 | if (size != transfer->blocksize) |
1003 | 0 | size++; /* room in this block */ |
1004 | 0 | else if (i == size - 1) |
1005 | 0 | transfer->carrylf = 1; /* don't expand LF again if it moves to the next block */ |
1006 | | |
1007 | | /* make space and insert CR */ |
1008 | 0 | memmove(&mess->data[i+1], &mess->data[i], size - (i + 1)); |
1009 | 0 | mess->data[i] = '\r'; |
1010 | | |
1011 | 0 | i++; |
1012 | 0 | } |
1013 | 0 | } |
1014 | |
|
1015 | 0 | daemon->srv_save = transfer; |
1016 | 0 | saved_offset = transfer->offset; |
1017 | 0 | saved_len = size + 4; |
1018 | |
|
1019 | 0 | return saved_len; |
1020 | 0 | } |
1021 | 0 | } |
1022 | | |
1023 | | |
1024 | | int do_tftp_script_run(void) |
1025 | 0 | { |
1026 | 0 | struct tftp_transfer *transfer; |
1027 | |
|
1028 | 0 | if ((transfer = daemon->tftp_done_trans)) |
1029 | 0 | { |
1030 | 0 | daemon->tftp_done_trans = transfer->next; |
1031 | 0 | #ifdef HAVE_SCRIPT |
1032 | 0 | queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer); |
1033 | 0 | #endif |
1034 | 0 | free_transfer(transfer); |
1035 | 0 | return 1; |
1036 | 0 | } |
1037 | | |
1038 | 0 | return 0; |
1039 | 0 | } |
1040 | | #endif |