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