Annotation of embedaddon/dnsmasq/src/tftp.c, revision 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>