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

version 1.1, 2013/07/29 19:37:40 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 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 49  void tftp_request(struct listener *listen, time_t now) Line 50  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 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;
  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 94  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;
  
  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 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 135  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 151  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 168  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 184  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 (!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 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 243  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 264  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 279  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 297  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 317  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 352  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 364  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 377  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 387  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 440  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 483  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 552  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 > 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,             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 586  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 622  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 651  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 665  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  
changed lines
  Added in v.1.1.1.4


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