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