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

version 1.1.1.3, 2016/11/02 09:57:01 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 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 124  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 141  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 157  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 174  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 190  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 217  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 234  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;  
Line 247  void tftp_request(struct listener *listen, time_t now) Line 241  void tftp_request(struct listener *listen, time_t now)
   if (mtu == 0)    if (mtu == 0)
     mtu = daemon->tftp_mtu;      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)
     {      {
       /* check for per-interface prefix */         /* check for per-interface prefix */ 
Line 255  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 272  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 293  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 308  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 326  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 347  void tftp_request(struct listener *listen, time_t now) Line 376  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 */                    /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
                  int overhead = (listen->family == AF_INET) ? 32 : 52;                  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;
Line 382  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 394  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 407  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 417  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 470  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 516  static struct tftp_file *check_tftp_fileperm(ssize_t * Line 577  static struct tftp_file *check_tftp_fileperm(ssize_t *
 void check_tftp_listeners(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)
   
  /* 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 (poll_check(transfer->sockfd, POLLIN))        if (poll_check(transfer->sockfd, POLLIN))
         {          {
           /* 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 582  void check_tftp_listeners(time_t now) Line 608  void check_tftp_listeners(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 616  void check_tftp_listeners(time_t now) Line 644  void check_tftp_listeners(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 652  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 681  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 695  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.3  
changed lines
  Added in v.1.1.1.4


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