Diff for /embedaddon/dnsmasq/src/tftp.c between versions 1.1.1.1 and 1.1.1.5

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 18 Line 18
   
 #ifdef HAVE_TFTP  #ifdef HAVE_TFTP
   
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
 static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
 static void free_transfer(struct tftp_transfer *transfer);  static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
static ssize_t tftp_err_oops(char *packet, char *file);static ssize_t tftp_err_oops(char *packet, const char *file);
 static ssize_t get_block(char *packet, struct tftp_transfer *transfer);  static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
 static char *next(char **p, char *end);  static char *next(char **p, char *end);
 static void sanitise(char *buf);  static void sanitise(char *buf);
Line 38  static void sanitise(char *buf); Line 39  static void sanitise(char *buf);
 #define ERR_PERM   2  #define ERR_PERM   2
 #define ERR_FULL   3  #define ERR_FULL   3
 #define ERR_ILL    4  #define ERR_ILL    4
   #define ERR_TID    5
   
 void tftp_request(struct listener *listen, time_t now)  void tftp_request(struct listener *listen, time_t now)
 {  {
Line 49  void tftp_request(struct listener *listen, time_t now) Line 51  void tftp_request(struct listener *listen, time_t now)
   struct iovec iov;    struct iovec iov;
   struct ifreq ifr;    struct ifreq ifr;
   int is_err = 1, if_index = 0, mtu = 0;    int is_err = 1, if_index = 0, mtu = 0;
 #ifdef HAVE_DHCP  
   struct iname *tmp;    struct iname *tmp;
#endif  struct tftp_transfer *transfer = NULL, **up;
  struct tftp_transfer *transfer; 
   int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */    int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)  #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
   int mtuflag = IP_PMTUDISC_DONT;    int mtuflag = IP_PMTUDISC_DONT;
Line 61  void tftp_request(struct listener *listen, time_t now) Line 61  void tftp_request(struct listener *listen, time_t now)
   char *name = NULL;    char *name = NULL;
   char *prefix = daemon->tftp_prefix;    char *prefix = daemon->tftp_prefix;
   struct tftp_prefix *pref;    struct tftp_prefix *pref;
  struct all_addr addra;  union all_addr addra;
  int family = listen->addr.sa.sa_family;
   /* Can always get recvd interface for IPv6 */
   int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6;
   union {    union {
     struct cmsghdr align; /* this ensures alignment */      struct cmsghdr align; /* this ensures alignment */
 #ifdef HAVE_IPV6  
     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];      char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
 #endif  
 #if defined(HAVE_LINUX_NETWORK)  #if defined(HAVE_LINUX_NETWORK)
     char control[CMSG_SPACE(sizeof(struct in_pktinfo))];      char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
 #elif defined(HAVE_SOLARIS_NETWORK)  #elif defined(HAVE_SOLARIS_NETWORK)
    char control[CMSG_SPACE(sizeof(unsigned int))];    char control[CMSG_SPACE(sizeof(struct in_addr)) +
                  CMSG_SPACE(sizeof(unsigned int))];
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)  #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];    char control[CMSG_SPACE(sizeof(struct in_addr)) +
                  CMSG_SPACE(sizeof(struct sockaddr_dl))];
 #endif  #endif
   } control_u;     } control_u; 
   
Line 93  void tftp_request(struct listener *listen, time_t now) Line 95  void tftp_request(struct listener *listen, time_t now)
   
   if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)    if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
     return;      return;
   
   #ifdef HAVE_DUMPFILE
     dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd);
   #endif
       
  if (option_bool(OPT_NOWILD))  /* Can always get recvd interface for IPv6 */
   if (!check_dest)
     {      {
       if (listen->iface)        if (listen->iface)
         {          {
           addr = listen->iface->addr;            addr = listen->iface->addr;
           mtu = listen->iface->mtu;  
           name = listen->iface->name;            name = listen->iface->name;
             mtu = listen->iface->mtu;
             if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
               mtu = daemon->tftp_mtu;
         }          }
       else        else
         {          {
Line 118  void tftp_request(struct listener *listen, time_t now) Line 127  void tftp_request(struct listener *listen, time_t now)
       if (msg.msg_controllen < sizeof(struct cmsghdr))        if (msg.msg_controllen < sizeof(struct cmsghdr))
         return;          return;
               
      addr.sa.sa_family = listen->family;      addr.sa.sa_family = family;
               
 #if defined(HAVE_LINUX_NETWORK)  #if defined(HAVE_LINUX_NETWORK)
      if (listen->family == AF_INET)      if (family == AF_INET)
         for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
           if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)            if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
             {              {
Line 135  void tftp_request(struct listener *listen, time_t now) Line 144  void tftp_request(struct listener *listen, time_t now)
             }              }
               
 #elif defined(HAVE_SOLARIS_NETWORK)  #elif defined(HAVE_SOLARIS_NETWORK)
      if (listen->family == AF_INET)      if (family == AF_INET)
         for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
           {            {
             union {              union {
Line 151  void tftp_request(struct listener *listen, time_t now) Line 160  void tftp_request(struct listener *listen, time_t now)
           }            }
               
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)  #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
      if (listen->family == AF_INET)      if (family == AF_INET)
         for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
           {            {
             union {              union {
Line 168  void tftp_request(struct listener *listen, time_t now) Line 177  void tftp_request(struct listener *listen, time_t now)
                       
 #endif  #endif
   
#ifdef HAVE_IPV6      if (family == AF_INET6)
      if (listen->family == AF_INET6) 
         {          {
           for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))            for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
             if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)              if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Line 184  void tftp_request(struct listener *listen, time_t now) Line 192  void tftp_request(struct listener *listen, time_t now)
                 if_index = p.p->ipi6_ifindex;                  if_index = p.p->ipi6_ifindex;
               }                }
         }          }
 #endif  
               
       if (!indextoname(listen->tftpfd, if_index, namebuff))        if (!indextoname(listen->tftpfd, if_index, namebuff))
         return;          return;
   
       name = namebuff;        name = namebuff;
               
      addra.addr.addr4 = addr.in.sin_addr;      addra.addr4 = addr.in.sin_addr;
   
#ifdef HAVE_IPV6      if (family == AF_INET6)
      if (listen->family == AF_INET6)        addra.addr6 = addr.in6.sin6_addr;
        addra.addr.addr6 = addr.in6.sin6_addr; 
#endif 
   
      if (!iface_check(listen->family, &addra, name, NULL))      if (daemon->tftp_interfaces)
         {          {
          if (!option_bool(OPT_CLEVERBIND))          /* dedicated tftp interface list */
            enumerate_interfaces();           for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
          if (!loopback_exception(listen->tftpfd, listen->family, &addra, name))            if (tmp->name && wildcard_match(tmp->name, name))
               break;
 
           if (!tmp)
             return;              return;
         }          }
            else
         {
           /* Do the same as DHCP */
           if (!iface_check(family, &addra, name, NULL))
             {
               if (!option_bool(OPT_CLEVERBIND))
                 enumerate_interfaces(0)
               if (!loopback_exception(listen->tftpfd, family, &addra, name) &&
                   !label_exception(if_index, family, &addra))
                 return;
             }
           
 #ifdef HAVE_DHCP        #ifdef HAVE_DHCP      
      /* allowed interfaces are the same as for DHCP */          /* allowed interfaces are the same as for DHCP */
      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)          for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
        if (tmp->name && wildcard_match(tmp->name, name))            if (tmp->name && wildcard_match(tmp->name, name))
          return;              return;
 #endif  #endif
              }
      strncpy(ifr.ifr_name, name, IF_NAMESIZE);
       safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
       if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)        if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
        mtu = ifr.ifr_mtu;              {
           mtu = ifr.ifr_mtu;  
           if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
             mtu = daemon->tftp_mtu;    
         }
     }      }
   
     /* Failed to get interface mtu - can use configured value. */
     if (mtu == 0)
       mtu = daemon->tftp_mtu;
   
     /* data transfer via server listening socket */
     if (option_bool(OPT_SINGLE_PORT))
       {
         int tftp_cnt;
   
         for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next)
           {
             tftp_cnt++;
   
             if (sockaddr_isequal(&peer, &transfer->peer))
               {
                 if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
                   {
                     /* Handle repeated RRQ or abandoned transfer from same host and port 
                        by unlinking and reusing the struct transfer. */
                     *up = transfer->next;
                     break;
                   }
                 else
                   {
                     handle_tftp(now, transfer, len);
                     return;
                   }
               }
           }
         
         /* Enforce simultaneous transfer limit. In non-single-port mode
            this is doene by not listening on the server socket when
            too many transfers are in progress. */
         if (!transfer && tftp_cnt >= daemon->tftp_max)
           return;
       }
       
   if (name)    if (name)
     {      {
Line 226  void tftp_request(struct listener *listen, time_t now) Line 287  void tftp_request(struct listener *listen, time_t now)
           prefix = pref->prefix;              prefix = pref->prefix;  
     }      }
   
  if (listen->family == AF_INET)  if (family == AF_INET)
     {      {
       addr.in.sin_port = htons(port);        addr.in.sin_port = htons(port);
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
       addr.in.sin_len = sizeof(addr.in);        addr.in.sin_len = sizeof(addr.in);
 #endif  #endif
     }      }
 #ifdef HAVE_IPV6  
   else    else
     {      {
       addr.in6.sin6_port = htons(port);        addr.in6.sin6_port = htons(port);
Line 243  void tftp_request(struct listener *listen, time_t now) Line 303  void tftp_request(struct listener *listen, time_t now)
       addr.in6.sin6_len = sizeof(addr.in6);        addr.in6.sin6_len = sizeof(addr.in6);
 #endif  #endif
     }      }
 #endif  
   
  if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))  /* May reuse struct transfer from abandoned transfer in single port mode. */
   if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
     return;      return;
       
  if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)  if (option_bool(OPT_SINGLE_PORT))
     transfer->sockfd = listen->tftpfd;
   else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
     {      {
       free(transfer);        free(transfer);
       return;        return;
     }      }
       
   transfer->peer = peer;    transfer->peer = peer;
     transfer->source = addra;
     transfer->if_index = if_index;
   transfer->timeout = now + 2;    transfer->timeout = now + 2;
   transfer->backoff = 1;    transfer->backoff = 1;
   transfer->block = 1;    transfer->block = 1;
Line 264  void tftp_request(struct listener *listen, time_t now) Line 328  void tftp_request(struct listener *listen, time_t now)
   transfer->opt_blocksize = transfer->opt_transize = 0;    transfer->opt_blocksize = transfer->opt_transize = 0;
   transfer->netascii = transfer->carrylf = 0;    transfer->netascii = transfer->carrylf = 0;
     
  prettyprint_addr(&peer, daemon->addrbuff);  (void)prettyprint_addr(&peer, daemon->addrbuff);
       
   /* if we have a nailed-down range, iterate until we find a free one. */    /* if we have a nailed-down range, iterate until we find a free one. */
  while (1)  while (!option_bool(OPT_SINGLE_PORT))
     {      {
       if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||        if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)  #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Line 279  void tftp_request(struct listener *listen, time_t now) Line 343  void tftp_request(struct listener *listen, time_t now)
             {              {
               if (++port <= daemon->end_tftp_port)                if (++port <= daemon->end_tftp_port)
                 {                   { 
                  if (listen->family == AF_INET)                  if (family == AF_INET)
                     addr.in.sin_port = htons(port);                      addr.in.sin_port = htons(port);
 #ifdef HAVE_IPV6  
                   else                    else
                     addr.in6.sin6_port = htons(port);                    addr.in6.sin6_port = htons(port);
#endif                  
                   continue;                    continue;
                 }                  }
               my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));                my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Line 297  void tftp_request(struct listener *listen, time_t now) Line 360  void tftp_request(struct listener *listen, time_t now)
       
   p = packet + 2;    p = packet + 2;
   end = packet + len;    end = packet + len;
  
   if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||    if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
       !(filename = next(&p, end)) ||        !(filename = next(&p, end)) ||
       !(mode = next(&p, end)) ||        !(mode = next(&p, end)) ||
       (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))        (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
     {      {
      len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);      len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
       is_err = 1;        is_err = 1;
     }      }
   else    else
Line 317  void tftp_request(struct listener *listen, time_t now) Line 380  void tftp_request(struct listener *listen, time_t now)
             {              {
               if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))                if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
                 {                  {
                     /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
                     int overhead = (family == AF_INET) ? 32 : 52;
                   transfer->blocksize = atoi(opt);                    transfer->blocksize = atoi(opt);
                   if (transfer->blocksize < 1)                    if (transfer->blocksize < 1)
                     transfer->blocksize = 1;                      transfer->blocksize = 1;
                   if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)                    if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
                     transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;                      transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
                  /* 32 bytes for IP, UDP and TFTP headers */                  if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
                  if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)                    transfer->blocksize = (unsigned)mtu - overhead;
                    transfer->blocksize = (unsigned)mtu - 32; 
                   transfer->opt_blocksize = 1;                    transfer->opt_blocksize = 1;
                   transfer->block = 0;                    transfer->block = 0;
                 }                  }
Line 341  void tftp_request(struct listener *listen, time_t now) Line 405  void tftp_request(struct listener *listen, time_t now)
         if (*p == '\\')          if (*p == '\\')
           *p = '/';            *p = '/';
         else if (option_bool(OPT_TFTP_LC))          else if (option_bool(OPT_TFTP_LC))
          *p = tolower(*p);          *p = tolower((unsigned char)*p);
                                   
       strcpy(daemon->namebuff, "/");        strcpy(daemon->namebuff, "/");
       if (prefix)        if (prefix)
Line 352  void tftp_request(struct listener *listen, time_t now) Line 416  void tftp_request(struct listener *listen, time_t now)
           if (prefix[strlen(prefix)-1] != '/')            if (prefix[strlen(prefix)-1] != '/')
             strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));              strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
   
          if (option_bool(OPT_TFTP_APREF))          if (option_bool(OPT_TFTP_APREF_IP))
             {              {
               size_t oldlen = strlen(daemon->namebuff);                size_t oldlen = strlen(daemon->namebuff);
               struct stat statbuf;                struct stat statbuf;
Line 364  void tftp_request(struct listener *listen, time_t now) Line 428  void tftp_request(struct listener *listen, time_t now)
               if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))                if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
                 daemon->namebuff[oldlen] = 0;                  daemon->namebuff[oldlen] = 0;
             }              }
                          
           if (option_bool(OPT_TFTP_APREF_MAC))
             {
               unsigned char *macaddr = NULL;
               unsigned char macbuf[DHCP_CHADDR_MAX];
               
 #ifdef HAVE_DHCP
               if (daemon->dhcp && peer.sa.sa_family == AF_INET)
                 {
                   /* Check if the client IP is in our lease database */
                   struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
                   if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
                     macaddr = lease->hwaddr;
                 }
 #endif
               
               /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
               if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
                 macaddr = macbuf;
               
               if (macaddr)
                 {
                   size_t oldlen = strlen(daemon->namebuff);
                   struct stat statbuf;
 
                   snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
                            macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
                   
                   /* remove unique-directory if it doesn't exist */
                   if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
                     daemon->namebuff[oldlen] = 0;
                 }
             }
           
           /* Absolute pathnames OK if they match prefix */            /* Absolute pathnames OK if they match prefix */
           if (filename[0] == '/')            if (filename[0] == '/')
             {              {
Line 377  void tftp_request(struct listener *listen, time_t now) Line 474  void tftp_request(struct listener *listen, time_t now)
       else if (filename[0] == '/')        else if (filename[0] == '/')
         daemon->namebuff[0] = 0;          daemon->namebuff[0] = 0;
       strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));        strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
      
       /* check permissions and open file */        /* check permissions and open file */
      if ((transfer->file = check_tftp_fileperm(&len, prefix)))      if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
         {          {
           if ((len = get_block(packet, transfer)) == -1)            if ((len = get_block(packet, transfer)) == -1)
             len = tftp_err_oops(packet, daemon->namebuff);              len = tftp_err_oops(packet, daemon->namebuff);
Line 387  void tftp_request(struct listener *listen, time_t now) Line 484  void tftp_request(struct listener *listen, time_t now)
             is_err = 0;              is_err = 0;
         }          }
     }      }
   
     send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
   
   #ifdef HAVE_DUMPFILE
     dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
   #endif
       
   while (sendto(transfer->sockfd, packet, len, 0,   
                 (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);  
     
   if (is_err)    if (is_err)
     free_transfer(transfer);      free_transfer(transfer);
   else    else
Line 400  void tftp_request(struct listener *listen, time_t now) Line 500  void tftp_request(struct listener *listen, time_t now)
     }      }
 }  }
     
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
 {  {
   char *packet = daemon->packet, *namebuff = daemon->namebuff;    char *packet = daemon->packet, *namebuff = daemon->namebuff;
   struct tftp_file *file;    struct tftp_file *file;
Line 417  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 517  static struct tftp_file *check_tftp_fileperm(ssize_t *
     {      {
       if (errno == ENOENT)        if (errno == ENOENT)
         {          {
          *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);          *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client);
           return NULL;            return NULL;
         }          }
       else if (errno == EACCES)        else if (errno == EACCES)
Line 440  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 540  static struct tftp_file *check_tftp_fileperm(ssize_t *
   else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)    else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
     goto perm;      goto perm;
               
  /* If we're doing many tranfers from the same file, only   /* If we're doing many transfers from the same file, only 
      open it once this saves lots of file descriptors        open it once this saves lots of file descriptors 
      when mass-booting a big cluster, for instance.        when mass-booting a big cluster, for instance. 
      Be conservative and only share when inode and name match       Be conservative and only share when inode and name match
Line 470  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 570  static struct tftp_file *check_tftp_fileperm(ssize_t *
   return file;    return file;
       
  perm:   perm:
  errno = EACCES;  *len =  tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES));
  *len =  tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); 
   if (fd != -1)    if (fd != -1)
     close(fd);      close(fd);
   return NULL;    return NULL;
Line 483  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 582  static struct tftp_file *check_tftp_fileperm(ssize_t *
   return NULL;    return NULL;
 }  }
   
void check_tftp_listeners(fd_set *rset, time_t now)void check_tftp_listeners(time_t now)
 {  {
   struct tftp_transfer *transfer, *tmp, **up;    struct tftp_transfer *transfer, *tmp, **up;
   ssize_t len;  
       
  struct ack {  /* In single port mode, all packets come via port 69 and tftp_request() */
    unsigned short op, block;  if (!option_bool(OPT_SINGLE_PORT))
  } *mess = (struct ack *)daemon->packet;    for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
        if (poll_check(transfer->sockfd, POLLIN))
  /* Check for activity on any existing transfers */ 
  for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) 
    { 
      tmp = transfer->next; 
       
      prettyprint_addr(&transfer->peer, daemon->addrbuff); 
      
      if (FD_ISSET(transfer->sockfd, rset)) 
         {          {
             union mysockaddr peer;
             socklen_t addr_len = sizeof(union mysockaddr);
             ssize_t len;
             
           /* we overwrote the buffer... */            /* we overwrote the buffer... */
           daemon->srv_save = NULL;            daemon->srv_save = NULL;
          
          if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))          if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
             {              {
              if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)               if (sockaddr_isequal(&peer, &transfer->peer)) 
                 handle_tftp(now, transfer, len);
               else
                 {                  {
                  /* Got ack, ensure we take the (re)transmit path */                  /* Wrong source address. See rfc1350 para 4. */
                  transfer->timeout = now;                  prettyprint_addr(&peer, daemon->addrbuff);
                  transfer->backoff = 0;                  len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
                  if (transfer->block++ != 0)                  while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
                    transfer->offset += transfer->blocksize - transfer->expansion;
 #ifdef HAVE_DUMPFILE
                   dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
 #endif
                 }                  }
               else if (ntohs(mess->op) == OP_ERR)  
                 {  
                   char *p = daemon->packet + sizeof(struct ack);  
                   char *end = daemon->packet + len;  
                   char *err = next(&p, end);  
                     
                   /* Sanitise error message */  
                   if (!err)  
                     err = "";  
                   else  
                     sanitise(err);  
                     
                   my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),  
                             (int)ntohs(mess->block), err,   
                             daemon->addrbuff);    
                     
                   /* Got err, ensure we take abort */  
                   transfer->timeout = now;  
                   transfer->backoff = 100;  
                 }  
             }              }
         }          }
             
     for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
       {
         tmp = transfer->next;
               
       if (difftime(now, transfer->timeout) >= 0.0)        if (difftime(now, transfer->timeout) >= 0.0)
         {          {
           int endcon = 0;            int endcon = 0;
             ssize_t len;
   
           /* timeout, retransmit */            /* timeout, retransmit */
          transfer->timeout += 1 + (1<<transfer->backoff);          transfer->timeout += 1 + (1<<(transfer->backoff/2));
                                       
           /* we overwrote the buffer... */            /* we overwrote the buffer... */
           daemon->srv_save = NULL;            daemon->srv_save = NULL;
         
           if ((len = get_block(daemon->packet, transfer)) == -1)            if ((len = get_block(daemon->packet, transfer)) == -1)
             {              {
               len = tftp_err_oops(daemon->packet, transfer->file->filename);                len = tftp_err_oops(daemon->packet, transfer->file->filename);
               endcon = 1;                endcon = 1;
             }              }
          /* don't complain about timeout when we're awaiting the last          else if (++transfer->backoff > 7)
             ACK, some clients never send it */ 
          else if (++transfer->backoff > 5 && len != 0) 
             {              {
              endcon = 1;              /* don't complain about timeout when we're awaiting the last
                  ACK, some clients never send it */
               if ((unsigned)len == transfer->blocksize + 4)
                 endcon = 1;
               len = 0;                len = 0;
             }              }
   
           if (len != 0)            if (len != 0)
            while(sendto(transfer->sockfd, daemon->packet, len, 0,             {
                         (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);              send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
                         &transfer->peer, &transfer->source, transfer->if_index);
 #ifdef HAVE_DUMPFILE
               dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
 #endif
             }
                       
           if (endcon || len == 0)            if (endcon || len == 0)
             {              {
               strcpy(daemon->namebuff, transfer->file->filename);                strcpy(daemon->namebuff, transfer->file->filename);
               sanitise(daemon->namebuff);                sanitise(daemon->namebuff);
                 (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
               my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);                my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
               /* unlink */                /* unlink */
               *up = tmp;                *up = tmp;
Line 586  void check_tftp_listeners(fd_set *rset, time_t now) Line 677  void check_tftp_listeners(fd_set *rset, time_t now)
       up = &transfer->next;        up = &transfer->next;
     }          }    
 }  }
             
   /* packet in daemon->packet as this is called. */
   static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
   {
     struct ack {
       unsigned short op, block;
     } *mess = (struct ack *)daemon->packet;
     
     if (len >= (ssize_t)sizeof(struct ack))
       {
         if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) 
           {
             /* Got ack, ensure we take the (re)transmit path */
             transfer->timeout = now;
             transfer->backoff = 0;
             if (transfer->block++ != 0)
               transfer->offset += transfer->blocksize - transfer->expansion;
           }
         else if (ntohs(mess->op) == OP_ERR)
           {
             char *p = daemon->packet + sizeof(struct ack);
             char *end = daemon->packet + len;
             char *err = next(&p, end);
             
             (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
             
             /* Sanitise error message */
             if (!err)
               err = "";
             else
               sanitise(err);
             
             my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
                       (int)ntohs(mess->block), err, 
                       daemon->addrbuff);  
             
             /* Got err, ensure we take abort */
             transfer->timeout = now;
             transfer->backoff = 100;
           }
       }
   }
   
 static void free_transfer(struct tftp_transfer *transfer)  static void free_transfer(struct tftp_transfer *transfer)
 {  {
  close(transfer->sockfd);  if (!option_bool(OPT_SINGLE_PORT))
     close(transfer->sockfd);
 
   if (transfer->file && (--transfer->file->refcount) == 0)    if (transfer->file && (--transfer->file->refcount) == 0)
     {      {
       close(transfer->file->fd);        close(transfer->file->fd);
       free(transfer->file);        free(transfer->file);
     }      }
     
   free(transfer);    free(transfer);
 }  }
   
Line 622  static void sanitise(char *buf) Line 758  static void sanitise(char *buf)
   
 }  }
   
static ssize_t tftp_err(int err, char *packet, char *message, char *file)#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ 
 static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2)
 {  {
   struct errmess {    struct errmess {
     unsigned short op, err;      unsigned short op, err;
     char message[];      char message[];
   } *mess = (struct errmess *)packet;    } *mess = (struct errmess *)packet;
  ssize_t ret = 4;  ssize_t len, ret = 4;
  char *errstr = strerror(errno);    
   memset(packet, 0, daemon->packet_buff_sz);
   if (file)
     sanitise(file);
       
   sanitise(file);  
   
   mess->op = htons(OP_ERR);    mess->op = htons(OP_ERR);
   mess->err = htons(err);    mess->err = htons(err);
  ret += (snprintf(mess->message, 500,  message, file, errstr) + 1);  len = snprintf(mess->message, MAXMESSAGE,  message, file, arg2);
  my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);  ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
       
     if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
       my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
     
   return  ret;    return  ret;
 }  }
   
static ssize_t tftp_err_oops(char *packet, char *file)static ssize_t tftp_err_oops(char *packet, const char *file)
 {  {
   /* May have >1 refs to file, so potentially mangle a copy of the name */    /* May have >1 refs to file, so potentially mangle a copy of the name */
  strcpy(daemon->namebuff, file);  if (file != daemon->namebuff)
  return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);    strcpy(daemon->namebuff, file);
   return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno));
 }  }
   
 /* return -1 for error, zero for done. */  /* return -1 for error, zero for done. */
 static ssize_t get_block(char *packet, struct tftp_transfer *transfer)  static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
 {  {
     memset(packet, 0, daemon->packet_buff_sz);
     
   if (transfer->block == 0)    if (transfer->block == 0)
     {      {
       /* send OACK */        /* send OACK */
Line 665  static ssize_t get_block(char *packet, struct tftp_tra Line 809  static ssize_t get_block(char *packet, struct tftp_tra
       if (transfer->opt_blocksize)        if (transfer->opt_blocksize)
         {          {
           p += (sprintf(p, "blksize") + 1);            p += (sprintf(p, "blksize") + 1);
          p += (sprintf(p, "%d", transfer->blocksize) + 1);          p += (sprintf(p, "%u", transfer->blocksize) + 1);
         }          }
       if (transfer->opt_transize)        if (transfer->opt_transize)
         {          {

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>