Diff for /embedaddon/dnsmasq/src/tftp.c between versions 1.1.1.2 and 1.1.1.4

version 1.1.1.2, 2014/06/15 16:31:38 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 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 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);  static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
 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);
 static ssize_t tftp_err_oops(char *packet, char *file);  static ssize_t tftp_err_oops(char *packet, 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);
Line 50  void tftp_request(struct listener *listen, time_t now) Line 51  void tftp_request(struct listener *listen, time_t now)
   struct ifreq ifr;    struct ifreq ifr;
   int is_err = 1, if_index = 0, mtu = 0;    int is_err = 1, if_index = 0, mtu = 0;
   struct iname *tmp;    struct iname *tmp;
  struct tftp_transfer *transfer;  struct tftp_transfer *transfer = NULL, **up;
   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 59  void tftp_request(struct listener *listen, time_t now) Line 60  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;
#ifdef HAVE_IPV6  int family = listen->addr.sa.sa_family;
   /* Can always get recvd interface for IPv6 */    /* Can always get recvd interface for IPv6 */
  int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;  int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6;
#else 
  int check_dest = !option_bool(OPT_NOWILD); 
#endif 
   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 103  void tftp_request(struct listener *listen, time_t now) Line 101  void tftp_request(struct listener *listen, time_t now)
       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 122  void tftp_request(struct listener *listen, time_t now) Line 122  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 139  void tftp_request(struct listener *listen, time_t now) Line 139  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 155  void tftp_request(struct listener *listen, time_t now) Line 155  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 172  void tftp_request(struct listener *listen, time_t now) Line 172  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 188  void tftp_request(struct listener *listen, time_t now) Line 187  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 (daemon->tftp_interfaces)        if (daemon->tftp_interfaces)
         {          {
Line 215  void tftp_request(struct listener *listen, time_t now) Line 211  void tftp_request(struct listener *listen, time_t now)
       else        else
         {          {
           /* Do the same as DHCP */            /* Do the same as DHCP */
          if (!iface_check(listen->family, &addra, name, NULL))          if (!iface_check(family, &addra, name, NULL))
             {              {
               if (!option_bool(OPT_CLEVERBIND))                if (!option_bool(OPT_CLEVERBIND))
                 enumerate_interfaces(0)                  enumerate_interfaces(0)
              if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&              if (!loopback_exception(listen->tftpfd, family, &addra, name) &&
                  !label_exception(if_index, listen->family, &addra) )                  !label_exception(if_index, family, &addra))
                 return;                  return;
             }              }
                       
Line 232  void tftp_request(struct listener *listen, time_t now) Line 228  void tftp_request(struct listener *listen, time_t now)
 #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 245  void tftp_request(struct listener *listen, time_t now) Line 282  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 262  void tftp_request(struct listener *listen, time_t now) Line 298  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 283  void tftp_request(struct listener *listen, time_t now) Line 323  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 298  void tftp_request(struct listener *listen, time_t now) Line 338  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 316  void tftp_request(struct listener *listen, time_t now) Line 355  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)) ||
Line 336  void tftp_request(struct listener *listen, time_t now) Line 375  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 371  void tftp_request(struct listener *listen, time_t now) Line 411  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 383  void tftp_request(struct listener *listen, time_t now) Line 423  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 396  void tftp_request(struct listener *listen, time_t now) Line 469  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)))
         {          {
Line 406  void tftp_request(struct listener *listen, time_t now) Line 479  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);
       
   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 459  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 531  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 502  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 574  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)) 
         {          {
           /* we overwrote the buffer... */            /* we overwrote the buffer... */
           daemon->srv_save = NULL;            daemon->srv_save = NULL;
                    handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
          if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (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); 
                   
                  /* 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;
Line 571  void check_tftp_listeners(fd_set *rset, time_t now) Line 608  void check_tftp_listeners(fd_set *rset, time_t now)
               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 > 7 && 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,             send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
                         (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);                      &transfer->peer, &transfer->source, transfer->if_index);
                            
           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 605  void check_tftp_listeners(fd_set *rset, time_t now) Line 644  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 641  static void sanitise(char *buf) Line 725  static void sanitise(char *buf)
   
 }  }
   
   #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)  static ssize_t tftp_err(int err, char *packet, char *message, char *file)
 {  {
   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);    char *errstr = strerror(errno);
       
     memset(packet, 0, daemon->packet_buff_sz);
   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, errstr);
   ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
   
   my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);    my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
       
   return  ret;    return  ret;
Line 670  static ssize_t tftp_err_oops(char *packet, char *file) Line 758  static ssize_t tftp_err_oops(char *packet, char *file)
 /* 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 684  static ssize_t get_block(char *packet, struct tftp_tra Line 774  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.2  
changed lines
  Added in v.1.1.1.4


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