int
nc_message(int s, struct nc_request *req, 
           struct nc_reply *reply, struct sockaddr *slave)
{
    fd_set fds;
    struct timeval timeout;

    req->seq = htonl(test_seq);
    if (sendto(s, req, sizeof(*req), 0, slave, sa_len(slave)) < 0) {
        perror("sendto");
        return false;
    }
    FD_ZERO(&fds);
    FD_SET(s, &fds);
    timeout.tv_sec = NC_REPLY_TIMEOUT;
    timeout.tv_usec = 0;
    if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
        test_printf("No response to command\n");
        return false;
    }
    if (recvfrom(s, reply, sizeof(*reply), 0, 0, 0) < 0) {
        perror("recvfrom");
        return false;
    }
    if (reply->seq != req->seq) {
        test_printf("Response out of order - sent: %d, recvd: %d\n",
                    ntohl(req->seq), ntohl(reply->seq));
        return false;
    }
    return true;
}
Exemple #2
0
static int nl_async(struct nlmsghdr *h)
{
  if (h->nlmsg_type == NLMSG_ERROR)
    {
      struct nlmsgerr *err = NLMSG_DATA(h);
      if (err->error != 0)
	my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
      return 0;
    }
  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
    {
      /* We arrange to receive netlink multicast messages whenever the network route is added.
	 If this happens and we still have a DNS packet in the buffer, we re-send it.
	 This helps on DoD links, where frequently the packet which triggers dialling is
	 a DNS query, which then gets lost. By re-sending, we can avoid the lookup
	 failing. */ 
      struct rtmsg *rtm = NLMSG_DATA(h);
      
      if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
	{
  	  /* Force re-reading resolv file right now, for luck. */
	  daemon->last_resolv = 0;
	  
	  if (daemon->srv_save)
	    {
	      int fd;

	      if (daemon->srv_save->sfd)
		fd = daemon->srv_save->sfd->fd;
	      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
		fd = daemon->rfd_save->fd;
	      else
		return 0;
	      
	      while(sendto(fd, daemon->packet, daemon->packet_len, 0,
			   &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
	    }
	}
      return 0;
    }
  else if (h->nlmsg_type == RTM_NEWADDR) 
    {
#ifdef HAVE_DHCP6
      /* force RAs to sync new network and pick up new interfaces.  */
      if (daemon->ra_contexts)
	{
	  schedule_subnet_map();
	  ra_start_unsolicted(dnsmasq_time(), NULL);
	  /* cause lease_update_file to run after we return, in case we were called from
	     iface_enumerate and can't re-enter it now */
	  send_alarm(0, 0);
	}
#endif	 
      return 1; /* clever bind mode - rescan */
    }	 
  
  return 0;
}
Exemple #3
0
/* We arrange to receive netlink multicast messages whenever the network route is added.
   If this happens and we still have a DNS packet in the buffer, we re-send it.
   This helps on DoD links, where frequently the packet which triggers dialling is
   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
   failing. */ 
static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h)
{
  if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
    {
      struct rtmsg *rtm = NLMSG_DATA(h);
      if (rtm->rtm_type == RTN_UNICAST &&
	  rtm->rtm_scope == RT_SCOPE_LINK) 
	while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
		     &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
    }
}
Exemple #4
0
//junzhao 2004.5.31
void check_servers_response(int peerfd, int peerfd6, int strict_order, char *dnamebuff)
{
        time_t now = time(NULL);
        int i;
        struct frec *f;
        static char packet[sizeof(HEADER) + 1];
        static HEADER *header = (HEADER *)packet;
																		
        for(i=0; i<FTABSIZ; i++)
        {
                f = &ftab[i];
		
                 if(f->new_id == 0)
                {
                        continue;
                }
                if(f->dnsMsgBufPtr != NULL)
                {
                        if(now - (f->time) >= DNS_TIMER)
                        {                                                                                                                                      
                                //DBG_printf("Wait for dns response time out and forward query to next server ...\n");
                                if(reforward_query(f, peerfd, peerfd6, strict_order, dnamebuff) == 0)
                                {
                                        f->time = now;
                                        continue;
                                }
                                else
                                {
                                        //DBG_printf("we tried all without success and time out, delete dnsMsgBuf\n");
                                        free_dns_msg_buf(f ->dnsMsgBufPtr);
                                        f ->dnsMsgBufPtr = NULL;
                                }
                        }
                }
                else if(now - (f->time) >= WAIT_TIMEOUT)
                {
                        memset(packet, 0, sizeof(packet));
                        /* could not send on, return empty answer */
                        header->id = htons(f->orig_id);
                        header->qr = 1; /* response */
                        header->aa = 0; /* authoritive - never */
                        header->ra = 1; /* recursion if available */
                        header->tc = 0; /* not truncated */
                        header->rcode = NOERROR; /* no error */
                        header->ancount = htons(0); /* no answers */
                        header->nscount = htons(0);
                        header->arcount = htons(0);
                        sendto(f->fd, packet, sizeof(packet), 0, &f->source.sa, sa_len(&f->source));
                        f->new_id = 0; /* cancel */
                }
        }
        return;
}
Exemple #5
0
static int nl_async(struct nlmsghdr *h)
{
  if (h->nlmsg_type == NLMSG_ERROR)
    {
      struct nlmsgerr *err = NLMSG_DATA(h);
      if (err->error != 0)
	my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
      return 0;
    }
  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
    {
      /* We arrange to receive netlink multicast messages whenever the network route is added.
	 If this happens and we still have a DNS packet in the buffer, we re-send it.
	 This helps on DoD links, where frequently the packet which triggers dialling is
	 a DNS query, which then gets lost. By re-sending, we can avoid the lookup
	 failing. */ 
      struct rtmsg *rtm = NLMSG_DATA(h);
      
      if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
	{
  	  /* Force re-reading resolv file right now, for luck. */
	  daemon->last_resolv = 0;
	  
	  if (daemon->srv_save)
	    {
	      int fd;

	      if (daemon->srv_save->sfd)
		fd = daemon->srv_save->sfd->fd;
	      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
		fd = daemon->rfd_save->fd;
	      else
		return 0;
	      
	      while(sendto(fd, daemon->packet, daemon->packet_len, 0,
			   &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
	    }
	}
      return 0;
    }
  else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
    return 1; /* clever bind mode - rescan */
  
  return 0;
}
Exemple #6
0
/* We arrange to receive netlink multicast messages whenever the network route is added.
   If this happens and we still have a DNS packet in the buffer, we re-send it.
   This helps on DoD links, where frequently the packet which triggers dialling is
   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
   failing. */ 
static void nl_routechange(struct nlmsghdr *h)
{
  if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
    {
      struct rtmsg *rtm = NLMSG_DATA(h);
      int fd;

      if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
	return;

      if (daemon->srv_save->sfd)
	fd = daemon->srv_save->sfd->fd;
      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
	fd = daemon->rfd_save->fd;
      else
	return;

      while(sendto(fd, daemon->packet, daemon->packet_len, 0,
		   &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
    }
}
Exemple #7
0
/* We arrange to receive netlink multicast messages whenever the network route is added.
   If this happens and we still have a DNS packet in the buffer, we re-send it.
   This helps on DoD links, where frequently the packet which triggers dialling is
   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
   failing. Note that we only accept these messages from the kernel (pid == 0) */ 
static void nl_routechange(struct nlmsghdr *h)
{
  if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
    {
      struct rtmsg *rtm = NLMSG_DATA(h);
      int fd;

      if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
	return;

      /* Force re-reading resolv file right now, for luck. */
      daemon->last_resolv = 0;
      
#ifdef HAVE_DHCP6
      /* force RAs to sync new network and pick up new interfaces.  */
      if (daemon->ra_contexts)
	{
	  schedule_subnet_map();
	  ra_start_unsolicted(dnsmasq_time(), NULL);
	  /* cause lease_update_file to run after we return, in case we were called from
	     iface_enumerate and can't re-enter it now */
	  send_alarm(0, 0);
	}
#endif

      if (daemon->srv_save)
	{
	  if (daemon->srv_save->sfd)
	    fd = daemon->srv_save->sfd->fd;
	  else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
	    fd = daemon->rfd_save->fd;
	  else
	    return;
	  
	  while(sendto(fd, daemon->packet, daemon->packet_len, 0,
		       &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
	}
    }
}
Exemple #8
0
static int forward_query(int udpfd, union mysockaddr *udpaddr,
			 struct all_addr *dst_addr, unsigned int dst_iface,
			 struct dns_header *header, size_t plen, time_t now, struct frec *forward)
{
  char *domain = NULL;
  int type = 0, norebind = 0;
  struct all_addr *addrp = NULL;
  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
  unsigned int flags = 0;
  unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
  struct server *start = NULL;
  
  /* RFC 4035: sect 4.6 para 2 */
  header->hb4 &= ~HB4_AD;
  
  /* may be no servers available. */
  if (!daemon->servers)
    forward = NULL;
  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
    {
      /* retry on existing query, send to all available servers  */
      domain = forward->sentto->domain;
      forward->sentto->failed_queries++;
      if (!option_bool(OPT_ORDER))
	{
	  forward->forwardall = 1;
	  daemon->last_server = NULL;
	}
      type = forward->sentto->flags & SERV_TYPE;
      if (!(start = forward->sentto->next))
	start = daemon->servers; /* at end of list, recycle */
      header->id = htons(forward->new_id);
    }
  else 
    {
      if (gotname)
	flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
      
      if (!flags && !(forward = get_new_frec(now, NULL)))
	/* table full - server failure. */
	flags = F_NEG;
      
      if (forward)
	{
	  forward->source = *udpaddr;
	  forward->dest = *dst_addr;
	  forward->iface = dst_iface;
	  forward->orig_id = ntohs(header->id);
	  forward->new_id = get_id(crc);
	  forward->fd = udpfd;
	  forward->crc = crc;
	  forward->forwardall = 0;
	  if (norebind)
	    forward->flags |= FREC_NOREBIND;
	  if (header->hb4 & HB4_CD)
	    forward->flags |= FREC_CHECKING_DISABLED;

	  header->id = htons(forward->new_id);
	  
	  /* In strict_order mode, always try servers in the order 
	     specified in resolv.conf, if a domain is given 
	     always try all the available servers,
	     otherwise, use the one last known to work. */
	  
	  if (type == 0)
	    {
	      if (option_bool(OPT_ORDER))
		start = daemon->servers;
	      else if (!(start = daemon->last_server) ||
		       daemon->forwardcount++ > FORWARD_TEST ||
		       difftime(now, daemon->forwardtime) > FORWARD_TIME)
		{
		  start = daemon->servers;
		  forward->forwardall = 1;
		  daemon->forwardcount = 0;
		  daemon->forwardtime = now;
		}
	    }
	  else
	    {
	      start = daemon->servers;
	      if (!option_bool(OPT_ORDER))
		forward->forwardall = 1;
	    }
	}
    }

  /* check for send errors here (no route to host) 
     if we fail to send to all nameservers, send back an error
     packet straight away (helps modem users when offline)  */
  
  if (!flags && forward)
    {
      struct server *firstsentto = start;
      int forwarded = 0;
      
      if (udpaddr && option_bool(OPT_ADD_MAC))
	plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
      
      while (1)
	{ 
	  /* only send to servers dealing with our domain.
	     domain may be NULL, in which case server->domain 
	     must be NULL also. */
	  
	  if (type == (start->flags & SERV_TYPE) &&
	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
	      !(start->flags & SERV_LITERAL_ADDRESS))
	    {
	      int fd;

	      /* find server socket to use, may need to get random one. */
	      if (start->sfd)
		fd = start->sfd->fd;
	      else 
		{
#ifdef HAVE_IPV6
		  if (start->addr.sa.sa_family == AF_INET6)
		    {
		      if (!forward->rfd6 &&
			  !(forward->rfd6 = allocate_rfd(AF_INET6)))
			break;
		      daemon->rfd_save = forward->rfd6;
		      fd = forward->rfd6->fd;
		    }
		  else
#endif
		    {
		      if (!forward->rfd4 &&
			  !(forward->rfd4 = allocate_rfd(AF_INET)))
			break;
		      daemon->rfd_save = forward->rfd4;
		      fd = forward->rfd4->fd;
		    }

#ifdef HAVE_CONNTRACK
		  /* Copy connection mark of incoming query to outgoing connection. */
		  if (option_bool(OPT_CONNTRACK))
		    {
		      unsigned int mark;
		      if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
			setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
		    }
#endif
		}
	      
	      if (sendto(fd, (char *)header, plen, 0,
			 &start->addr.sa,
			 sa_len(&start->addr)) == -1)
		{
		  if (retry_send())
		    continue;
		}
	      else
		{
		  /* Keep info in case we want to re-send this packet */
		  daemon->srv_save = start;
		  daemon->packet_len = plen;
		  
		  if (!gotname)
		    strcpy(daemon->namebuff, "query");
		  if (start->addr.sa.sa_family == AF_INET)
		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in.sin_addr, NULL); 
#ifdef HAVE_IPV6
		  else
		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
#endif 
		  start->queries++;
		  forwarded = 1;
		  forward->sentto = start;
		  if (!forward->forwardall) 
		    break;
		  forward->forwardall++;
		}
	    } 
	  
	  if (!(start = start->next))
 	    start = daemon->servers;
	  
	  if (start == firstsentto)
	    break;
	}
      
      if (forwarded)
	return 1;
      
      /* could not send on, prepare to return */ 
      header->id = htons(forward->orig_id);
      free_frec(forward); /* cancel */
    }	  
  
  /* could not send on, return empty answer or address if known for whole domain */
  if (udpfd != -1)
    {
      plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
      send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
    }

  return 0;
}
Exemple #9
0
int main (int argc, char **argv)
{
  int i;
  int cachesize = CACHESIZ;
  int port = NAMESERVER_PORT;
  unsigned int options;
  int first_loop = 1;
#ifdef HAVE_FILE_SYSTEM
  int logged_resolv = 0, logged_lease = 0; 
  char *resolv = RESOLVFILE;
  char *runfile = RUNFILE;
  time_t resolv_changed = 0;
  char *lease_file = NULL;
  off_t lease_file_size = (off_t)0;
  ino_t lease_file_inode = (ino_t)0;
#endif
  struct irec *iface;
  int peerfd, peerfd6;
  struct irec *interfaces = NULL;
  char *mxname = NULL;
  char *mxtarget = NULL;
  char *domain_suffix = NULL;
  char *username = CHUSER;
  struct iname *if_names = NULL;
  struct iname *if_addrs = NULL;
  struct server *serv_addrs = NULL;
  char *dnamebuff, *packet;
  struct server *servers, *last_server;
 
  sighup = 1; /* init cache the first time through */
  sigusr1 = 0; /* but don't dump */
  signal(SIGUSR1, sig_handler);
  signal(SIGHUP, sig_handler);

  /* These get allocated here to avoid overflowing the small stack
     on embedded systems. dnamebuff is big enough to hold one
     maximal sixed domain name and gets passed into all the processing
     code. We manage to get away with one buffer. */
  dnamebuff = safe_malloc(MAXDNAME);
  /* Size: we check after adding each record, so there must be 
     memory for the largest packet, and the largest record */
  packet = safe_malloc(PACKETSZ+MAXDNAME+RRFIXEDSZ);

#ifdef HAVE_FILE_SYSTEM
  options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
		      &username, &domain_suffix, &runfile, &if_names, &if_addrs, 
		      &serv_addrs, &cachesize, &port) ;
#else
  options = read_opts(argc, argv, dnamebuff, NULL, &mxname, &mxtarget, NULL,
		      &username, &domain_suffix, NULL, &if_names, &if_addrs, 
		      &serv_addrs, &cachesize, &port) ;
#endif
  
  /* peerfd is not bound to a low port
     so that we can send queries out on it without them getting
     blocked at firewalls */
  
  if ((peerfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 && 
      errno != EAFNOSUPPORT &&
      errno != EINVAL)
    die("dnsmasq: cannot create socket: %s", NULL);
  
#ifdef HAVE_IPV6
  if ((peerfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1 && 
      errno != EAFNOSUPPORT &&
      errno != EINVAL)
    die("dnsmasq: cannot create IPv6 socket: %s", NULL);
#else
  peerfd6 = -1;
#endif
  
  if (peerfd == -1 && peerfd6 == -1)
    die("dnsmasq: no kernel support for IPv4 _or_ IPv6.", NULL);

  interfaces = find_all_interfaces(if_names, if_addrs, port);
  
  /* open a socket bound to NS port on each local interface.
     this is necessary to ensure that our replies originate from
     the address they were sent to. See Stevens page 531 */
  for (iface = interfaces; iface; iface = iface->next)
    {
      if ((iface->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1)
	die("cannot create socket: %s", NULL);
	      
      if (bind(iface->fd, &iface->addr.sa, sa_len(&iface->addr)))
	die("bind failed: %s", NULL);
    }

  forward_init(1);

  cache_init(cachesize, options & OPT_LOG);
  
  setbuf(stdout, NULL);

#ifdef HAVE_FILE_SYSTEM
  if (!(options & OPT_DEBUG))
    {
      FILE *pidfile;
      struct passwd *ent_pw;
        
      /* The following code "daemonizes" the process. 
	 See Stevens section 12.4 */

#ifdef HAVE_FORK
      if (fork() != 0 )
	exit(0);
      
      setsid();
      
      if (fork() != 0)
	exit(0);
#endif
      
      chdir("/");
      umask(022); /* make pidfile 0644 */
      
      /* write pidfile _after_ forking ! */
      if (runfile && (pidfile = fopen(runfile, "w")))
      	{
	  fprintf(pidfile, "%d\n", (int) getpid());
	  fclose(pidfile);
	}
      
      umask(0);

      for (i=0; i<64; i++)
	{
	  if (i == peerfd || i == peerfd6)
	    continue;
	  for (iface = interfaces; iface; iface = iface->next)
	    if (iface->fd == i)
	      break;
	  if (!iface)
	    close(i);
	}

      /* Change uid and gid for security */
      if (username && (ent_pw = getpwnam(username)))
	{
	  gid_t dummy;
	  struct group *gp;
	  /* remove all supplimentary groups */
	  setgroups(0, &dummy);
	  /* change group to "dip" if it exists, for /etc/ppp/resolv.conf 
	     otherwise get the group for "nobody" */
	  if ((gp = getgrnam("dip")) || (gp = getgrgid(ent_pw->pw_gid)))
	    setgid(gp->gr_gid); 
	  /* finally drop root */
	  setuid(ent_pw->pw_uid);
	}
    }
#else
#endif

  /* In debug mode, log to stderr too and cut the prefix crap. */
  openlog("dnsmasq", options & OPT_DEBUG ? LOG_PERROR : LOG_PID, LOG_DAEMON);
  
  if (cachesize)
    syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
  else
    syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
  
  if (mxname)
    syslog(LOG_INFO, "serving MX record for mailhost %s target %s", 
	   mxname, mxtarget);
  
  if (getuid() == 0 || geteuid() == 0)
    syslog(LOG_WARNING, "failed to drop root privs");
  
  serv_addrs = servers = last_server = check_servers(serv_addrs, interfaces, peerfd, peerfd6);
  
  while (1)
    {
      int ready, maxfd = peerfd > peerfd6 ? peerfd : peerfd6;
      fd_set rset;
      HEADER *header;
#ifdef HAVE_FILE_SYSTEM
      struct stat statbuf;
#endif
   
      if (first_loop)
	/* do init stuff only first time round. */
	{
	  first_loop = 0;
	  ready = 0;
	}
      else
	{
	  FD_ZERO(&rset);

	  if (peerfd != -1)
	    FD_SET(peerfd, &rset);
	  if (peerfd6 != -1)
	    FD_SET(peerfd6, &rset);
	    	  
	  for (iface = interfaces; iface; iface = iface->next)
	    {
	      FD_SET(iface->fd, &rset);
	      if (iface->fd > maxfd)
		maxfd = iface->fd;
	    }
	  
	  ready = select(maxfd+1, &rset, NULL, NULL, NULL);
	  
	  if (ready == -1)
	    {
	      if (errno == EINTR)
		ready = 0; /* do signal handlers */
	      else
		continue;
	    }
	}

#ifdef HAVE_FILE_SYSTEM
      if (sighup)
	{
	  signal(SIGHUP, SIG_IGN);
	  cache_reload(options & OPT_NO_HOSTS, dnamebuff);
	  if (resolv && (options & OPT_NO_POLL))
	    servers = last_server = 
	      check_servers(reload_servers(resolv, dnamebuff, servers), 
			    interfaces, peerfd, peerfd6);
	  sighup = 0;
	  signal(SIGHUP, sig_handler);
	}

      if (sigusr1)
	{
	  signal(SIGUSR1, SIG_IGN);
	  dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
	  sigusr1 = 0;
	  signal(SIGUSR1, sig_handler);
	}

      if (resolv && !(options & OPT_NO_POLL))
	{
	  if (stat(resolv, &statbuf) == -1)
	    {
	      if (!logged_resolv)
		syslog(LOG_WARNING, "failed to access %s: %m", resolv);
	      logged_resolv = 1;
	    }
	  else
	    {
	      logged_resolv = 0;
	      if ((statbuf.st_mtime > resolv_changed) &&
		  (statbuf.st_mtime < time(NULL) || resolv_changed == 0))
		{
		  resolv_changed = statbuf.st_mtime;
		  servers = last_server = 
		    check_servers(reload_servers(resolv, dnamebuff, servers),
				  interfaces, peerfd, peerfd6);
		}
	    }
	}
#else
#endif

#ifdef HAVE_FILE_SYSTEM
      if (lease_file)
	{
	  if (stat(lease_file, &statbuf) == -1)
	    {
	      if (!logged_lease)
		syslog(LOG_WARNING, "failed to access %s: %m", lease_file);
	      logged_lease = 1;
	    }
	  else
	    { 
	      logged_lease = 0;
	      if ((lease_file_size == (off_t)0) ||
		  (statbuf.st_size > lease_file_size) ||
		  (statbuf.st_ino != lease_file_inode))
		{
		  lease_file_size = statbuf.st_size;
		  lease_file_inode = statbuf.st_ino;
		  load_dhcp(lease_file, domain_suffix, time(NULL), dnamebuff);
		}
	    }
	}
#else
#endif

      if (ready == 0)
	continue; /* no sockets ready */
      
      if (peerfd != -1 && FD_ISSET(peerfd, &rset))
	last_server = reply_query(peerfd, packet, dnamebuff, last_server);
      if (peerfd6 != -1 && FD_ISSET(peerfd6, &rset))
	last_server = reply_query(peerfd6, packet, dnamebuff, last_server);

      for (iface = interfaces; iface; iface = iface->next)
	{
	  if (FD_ISSET(iface->fd, &rset))
	    {
	      /* request packet, deal with query */
	      union mysockaddr udpaddr;
	      socklen_t udplen = sizeof(udpaddr);
	      int m, n = recvfrom(iface->fd, packet, PACKETSZ, 0, &udpaddr.sa, &udplen); 
	      udpaddr.sa.sa_family = iface->addr.sa.sa_family;
#ifdef HAVE_IPV6
	      if (udpaddr.sa.sa_family == AF_INET6)
		udpaddr.in6.sin6_flowinfo = htonl(0);
#endif	      
	      header = (HEADER *)packet;
	      if (n >= (int)sizeof(HEADER) && !header->qr)
		{
		  m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, 
				      mxname, mxtarget, options, dnamebuff);
		  if (m >= 1)
		    {
		      /* answered from cache, send reply */
		      sendto(iface->fd, (char *)header, m, 0, 
			     &udpaddr.sa, sa_len(&udpaddr));
		    }
		  else 
		    {
		      /* cannot answer from cache, send on to real nameserver */
		      last_server = forward_query(iface->fd, peerfd, peerfd6, &udpaddr, header, n, 
						  options && OPT_ORDER, dnamebuff,
						  servers, last_server);
		    }
		}
	      
	    }
	}
    }
  
  return 0;
}
static void
nc_master_test(struct sockaddr *slave, int test_tcp, int test_udp,
               int test_slave_loads, int test_master_loads)
{
    int s, i;
    struct sockaddr_storage my_addr;
    struct pause pause_times[] = {
        {0,0}, {1,10}, {5,10}, {10,10}, {1,1} };


    s = socket(slave->sa_family, SOCK_DGRAM, 0);
    if (s < 0) {
        pexit("datagram socket");
    }

    memset(&my_addr, 0, sizeof(my_addr));
    ((struct sockaddr *)&my_addr)->sa_family = slave->sa_family;
#ifndef __linux
    ((struct sockaddr *)&my_addr)->sa_len = slave->sa_len;
#endif
    switch (slave->sa_family) {
    case AF_INET:
        ((struct sockaddr_in *)&my_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
        ((struct sockaddr_in *)&my_addr)->sin_port = htons(NC_MASTER_PORT);
        break;
    case AF_INET6:
        ((struct sockaddr_in6 *)&my_addr)->sin6_addr = in6addr_any;
        ((struct sockaddr_in6 *)&my_addr)->sin6_port = htons(NC_MASTER_PORT);
        break;
    default:
        pexit("strange sockaddr family");
    }
    if (bind(s, (struct sockaddr *) &my_addr, sa_len((struct sockaddr *)&my_addr)) < 0) {
        pexit("UDP bind <main>");
    }

    test_printf("================== No load, master at 100%% ========================\n");
    if (test_udp) {
        do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 640, 1024, 0, 0);
        do_udp_test(s, NC_REQUEST_UDP_SEND, slave, 640, 1024, 0, 0);
        do_udp_test(s, NC_REQUEST_UDP_RECV, slave, 640, 1024, 0, 0);
    }
    if (test_tcp) {
        do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 640, 1024, 0, 0);
        do_tcp_test(s, NC_REQUEST_TCP_SEND, slave, 640, 1024, 0, 0);
        do_tcp_test(s, NC_REQUEST_TCP_RECV, slave, 640, 1024, 0, 0);
        do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 64, 10240, 0, 0);
    }

    if (test_slave_loads) {
        if (do_set_load(s, slave, 0)) {
            test_printf("\n====================== Various slave compute loads ===================\n");
            for (i = 0;  i < 60;  i += 10) {
                test_printf(">>>>>>>>>>>> slave processing load at %d%%\n", i);                
                do_set_load(s, slave, i);
                if (test_udp) {
                    do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 2048, 1024, 0, 0);
                }
                if (test_tcp) {
                    do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 2048, 1024, 0, 0);
                }
            }
        }
    }

    if (test_master_loads) {
        if (do_start_idle(s, slave)) {
            test_printf("\n====================== Various master loads ===================\n");
            test_printf("Testing IDLE for %d seconds\n", IDLE_TEST_TIME);
            test_delay(IDLE_TEST_TIME*100);
            do_stop_idle(s, slave, true);
            for (i = 0;  i < LENGTH(pause_times);  i++) {
                if (test_udp) {
                    do_start_idle(s, slave);
                    do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 2048, 1024, 
                                pause_times[i].pause_ticks, pause_times[i].pause_threshold);
                    do_stop_idle(s, slave, false);
                }
                if (test_tcp) {
                    do_start_idle(s, slave);
                    do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 2048, 1024, 
                                pause_times[i].pause_ticks, pause_times[i].pause_threshold);
                    do_stop_idle(s, slave, false);
                }
            }
        }
    }

//    do_disconnect(s, slave);
    close(s);
}
void
do_tcp_test(int s1, int type, struct sockaddr *slave,
            int nbufs, int buflen, int pause_time, int pause_threshold)
{
    int i, s, td_len, tot_len, wlen, len, seq, seq_errors, total_packets, res;
    struct sockaddr_storage test_chan_slave;
    struct timeval start_time, end_time;
    struct nc_request req;
    struct nc_reply reply;
    struct nc_test_results results;
    struct nc_test_data *tdp;
    int lost_packets = 0;
    int conn_failures = 0;
    int need_send, need_recv;
    const char *type_name;
    int pkt_ctr = 0;
    unsigned char *dp;

    need_recv = true;  need_send = true;  type_name = "TCP echo";
    switch (type) {
    case NC_REQUEST_TCP_RECV:
        need_recv = false;
        need_send = true;
        type_name = "TCP recv";
        break;
    case NC_REQUEST_TCP_SEND:
        need_recv = true;
        need_send = false;
        type_name = "TCP send";
        break;
    case NC_REQUEST_TCP_ECHO:
        break;
    }

    new_test();
    req.type = htonl(type);
    req.nbufs = htonl(nbufs);
    req.buflen = htonl(buflen);
    req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
    req.master_port = htonl(NC_TESTING_MASTER_PORT);
    nc_message(s1, &req, &reply, slave);
    if (reply.response != ntohl(NC_REPLY_ACK)) {
        test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
        return;
    }

    s = socket(slave->sa_family, SOCK_STREAM, 0);
    if (s < 0) {
        pexit("datagram socket");
    }

    test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
    if (pause_time) {
        test_printf(" - %dms delay after %d packet%s\n", pause_time*10, 
                    pause_threshold, pause_threshold > 1 ? "s" : "");
    } else {
        test_printf(" - no delays\n");
    }

    test_delay(3*100);  
    memcpy(&test_chan_slave, slave, sa_len(slave));
    switch (slave->sa_family) {
    case AF_INET:
        ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req.slave_port));
        break;
    case AF_INET6:
        ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req.slave_port));
        break;
    default:
        pexit("strange TCP sockaddr");
    }
    while (connect(s, (struct sockaddr *)&test_chan_slave, sa_len(slave)) < 0) { 
        perror("Can't connect to slave");
        if (++conn_failures > MAX_ERRORS) {
            test_printf("Too many connection failures - giving up\n");
            return;
        }
        if (errno == ECONNREFUSED) {
            // Give the slave a little time
            test_delay(100);  // 1 second
        } else {
            return;
        }
    }

    gettimeofday(&start_time, 0);
    seq = 0;  seq_errors = 0;  total_packets = 0;
    for (i = 0;  i < nbufs;  i++) {
        td_len = buflen + sizeof(struct nc_test_data);
        if (need_send) {
            tdp = (struct nc_test_data *)out_buf;
            tdp->key1 = htonl(NC_TEST_DATA_KEY1);
            tdp->key2 = htonl(NC_TEST_DATA_KEY2);
            tdp->seq = htonl(i);
            tdp->len = htonl(td_len);
            tot_len = 0;
            dp = (unsigned char *)tdp;
            while (tot_len < td_len) {
                len = td_len - tot_len;
                if ((wlen = write(s, dp, len)) != len) {
                    if (wlen < 0) {
                        test_printf("Slave connection broken\n");
                        perror("write");
                        close(s);
                        return;
                    } else {
                        test_printf("block: %d, short write - only %d of %d\n", 
                                    total_packets, wlen, len);
                    }
                }
                tot_len += wlen;
                dp += wlen;
            }
            total_packets++;
        }
        if (need_recv) {
            tdp = (struct nc_test_data *)in_buf;
            res = do_read(s, tdp, td_len);
            if (res != td_len) {
                lost_packets++;
                if (res < 0) {
                    test_printf("Slave connection broken\n");
                    perror("read");
                    break;
                } else {
                    test_printf("Slave timed out after %d buffers [read %d bytes]\n", i, res);
                }
            } else {
                if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
                    (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
                    if (ntohl(tdp->seq) != seq) {
                        test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
                                    ntohl(tdp->seq), seq);
                        seq_errors++;
                        if (!need_send) {
                            // Reset sequence to what the slave wants
                            seq = ntohl(tdp->seq);
                        }
                    }
                } else {
                    test_printf("Bad data packet - key: %x/%x, seq: %d\n",
                                ntohl(tdp->key1), ntohl(tdp->key2),
                                ntohl(tdp->seq));
                }
                total_packets++;
            }
            seq++;
            if (seq == nbufs) {
                break;
            }
            if (pause_time && (++pkt_ctr == pause_threshold)) {
                pkt_ctr = 0;
                test_delay(pause_time);
            }
        }
    }
    gettimeofday(&end_time, 0);
    show_results(type_name, &start_time, &end_time, total_packets, buflen, 
                 lost_packets, seq_errors);
    // Fetch results record
    if (do_read(s, &results, sizeof(results)) != sizeof(results)) {
        test_printf("No results record sent\n");
    } else {
        show_test_results(&results);
    }
    close(s);
}
/* reforward last query */
int reforward_query(struct frec *forward, int peerfd, int peerfd6, int strict_order, char *dnamebuff)
{
    char *domain;
    struct DNS_MSG_BUF * dnsMsgBufPtr;
    HEADER *header;
    int plen;
    if (forward == NULL)
    {
        return -1;
    }
    dnsMsgBufPtr = forward->dnsMsgBufPtr;
    if (dnsMsgBufPtr == NULL)
    {
        return -1;
    }
    if(dnsMsgBufPtr->nextsentto == dnsMsgBufPtr->firstsentto)
    {
        return -1;
    }
    plen = dnsMsgBufPtr->plen;
    header = (HEADER *)&dnsMsgBufPtr->header;

    /* retry on existing query, send to next server */
    domain = dnsMsgBufPtr->firstsentto->domain;
    header->id = htons(forward->new_id);

    /* check for send errors here (no route to host)
    if we fail to send to all nameservers, send back an error
    packet straight away (helps modem users when offline)  */
    while (1)
    {
        int af = dnsMsgBufPtr->nextsentto->addr.sa.sa_family;
        int fd = af == AF_INET ? peerfd : peerfd6;

        /* only send to servers dealing with our domain.
        domain may be NULL, in which case server->domain
        must be NULL also. */

        if ((!domain && !dnsMsgBufPtr->nextsentto->domain) ||
                (domain && dnsMsgBufPtr->nextsentto->domain &&
                 strcmp(domain, dnsMsgBufPtr->nextsentto->domain) == 0))
        {
            if (sendto(fd, (char *)header, plen, 0, &dnsMsgBufPtr->nextsentto->addr.sa,
                       sa_len(&dnsMsgBufPtr->nextsentto->addr)) != -1)
            {
                //junzhao 2004.5.31
                if (af == AF_INET)
                {
                    log_query(F_SERVER | F_IPV4 | F_FORWARD, dnsMsgBufPtr->gotname ? dnamebuff :
                              "query",
                              (struct all_addr *)&dnsMsgBufPtr->nextsentto->addr.in.sin_addr);
                }
#ifdef HAVE_IPV6
                else
                {
                    log_query(F_SERVER | F_IPV6 | F_FORWARD, dnsMsgBufPtr->gotname ? dnamebuff :
                              "query",
                              (struct all_addr *)&dnsMsgBufPtr->nextsentto->addr.in6.sin6_addr);
                }
#endif

                if (!(dnsMsgBufPtr->nextsentto = dnsMsgBufPtr->nextsentto->next))
                {
                    dnsMsgBufPtr->nextsentto = dnsMsgBufPtr->servers;
                }
                return 0;
            }
        }

        if (!(dnsMsgBufPtr->nextsentto = dnsMsgBufPtr->nextsentto->next))
        {
            dnsMsgBufPtr->nextsentto = dnsMsgBufPtr->servers;
        }

        /* check if we tried all without success */
        if (dnsMsgBufPtr->nextsentto == dnsMsgBufPtr->firstsentto)
        {
            break;
        }
    }//while 1

    /* could not send on, prepare to return */
    return -1;
}
Exemple #13
0
static int forward_query(int udpfd, union mysockaddr *udpaddr,
			 struct all_addr *dst_addr, unsigned int dst_iface,
			 HEADER *header, size_t plen, time_t now, struct frec *forward)
{
  char *domain = NULL;
  int type = 0;
  struct all_addr *addrp = NULL;
  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
  unsigned short flags = 0;
  unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
  struct server *start = NULL;
    
  /* may be no servers available. */
  if (!daemon->servers)
    forward = NULL;
  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
    {
      /* retry on existing query, send to all available servers  */
      domain = forward->sentto->domain;
      forward->sentto->failed_queries++;
      if (!(daemon->options & OPT_ORDER))
	{
	  forward->forwardall = 1;
	  daemon->last_server = NULL;
	}
      type = forward->sentto->flags & SERV_TYPE;
      if (!(start = forward->sentto->next))
	start = daemon->servers; /* at end of list, recycle */
      header->id = htons(forward->new_id);
    }
  else 
    {
      if (gotname)
	flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
      
      if (!flags && !(forward = get_new_frec(now, NULL)))
	/* table full - server failure. */
	flags = F_NEG;
      
      if (forward)
	{
	  /* force unchanging id for signed packets */
	  int is_sign;
	  find_pseudoheader(header, plen, NULL, NULL, &is_sign);
	  
	  forward->source = *udpaddr;
	  forward->dest = *dst_addr;
	  forward->iface = dst_iface;
	  forward->orig_id = ntohs(header->id);
	  forward->new_id = get_id(is_sign, forward->orig_id, crc);
	  forward->fd = udpfd;
	  forward->crc = crc;
	  forward->forwardall = 0;
	  header->id = htons(forward->new_id);

	  /* In strict_order mode, or when using domain specific servers
	     always try servers in the order specified in resolv.conf,
	     otherwise, use the one last known to work. */
	  
	  if (type != 0  || (daemon->options & OPT_ORDER))
	    start = daemon->servers;
	  else if (!(start = daemon->last_server) ||
		   daemon->forwardcount++ > FORWARD_TEST ||
		   difftime(now, daemon->forwardtime) > FORWARD_TIME)
	    {
	      start = daemon->servers;
	      forward->forwardall = 1;
	      daemon->forwardcount = 0;
	      daemon->forwardtime = now;
	    }
	}
    }

  /* check for send errors here (no route to host) 
     if we fail to send to all nameservers, send back an error
     packet straight away (helps modem users when offline)  */
  
  if (!flags && forward)
    {
      struct server *firstsentto = start;
      int forwarded = 0;

      while (1)
	{ 
	  /* only send to servers dealing with our domain.
	     domain may be NULL, in which case server->domain 
	     must be NULL also. */
	  
	  if (type == (start->flags & SERV_TYPE) &&
	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
	      !(start->flags & SERV_LITERAL_ADDRESS))
	    {
	      int fd;

	      /* find server socket to use, may need to get random one. */
	      if (start->sfd)
		fd = start->sfd->fd;
	      else 
		{
#ifdef HAVE_IPV6
		  if (start->addr.sa.sa_family == AF_INET6)
		    {
		      if (!forward->rfd6 &&
			  !(forward->rfd6 = allocate_rfd(AF_INET6)))
			break;
		      daemon->rfd_save = forward->rfd6;
		      fd = forward->rfd6->fd;
		    }
		  else
#endif
		    {
		      if (!forward->rfd4 &&
			  !(forward->rfd4 = allocate_rfd(AF_INET)))
			break;
		      daemon->rfd_save = forward->rfd4;
		      fd = forward->rfd4->fd;
		    }

#ifdef ANDROID
		  // Mark the socket so it goes out on the correct network. Note
		  // that we never clear the mark, only re-set it the next time we
		  // allocate a new random fd. This is because we buffer DNS
		  // queries (in daemon->srv_save, daemon->packet_len) and socket
		  // file descriptors (in daemon->rfd_save) with the expectation of
		  // being able to use them again.
		  //
		  // Server fds are marked separately in allocate_sfd.
		  setsockopt(fd, SOL_SOCKET, SO_MARK, &start->mark, sizeof(start->mark));
#endif
		}

	      if (sendto(fd, (char *)header, plen, 0,
			 &start->addr.sa,
			 sa_len(&start->addr)) == -1)
		{
		  if (retry_send())
		    continue;
		}
	      else
		{
		  /* Keep info in case we want to re-send this packet */
		  daemon->srv_save = start;
		  daemon->packet_len = plen;
		  
		  if (!gotname)
		    strcpy(daemon->namebuff, "query");
		  if (start->addr.sa.sa_family == AF_INET)
		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in.sin_addr, NULL); 
#ifdef HAVE_IPV6
		  else
		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
#endif 
		  start->queries++;
		  forwarded = 1;
		  forward->sentto = start;
		  if (!forward->forwardall) 
		    break;
		  forward->forwardall++;
		}
	    } 
	  
	  if (!(start = start->next))
 	    start = daemon->servers;
	  
	  if (start == firstsentto)
	    break;
	}
      
      if (forwarded)
	return 1;
      
      /* could not send on, prepare to return */ 
      header->id = htons(forward->orig_id);
      free_frec(forward); /* cancel */
    }	  
  
  /* could not send on, return empty answer or address if known for whole domain */
  if (udpfd != -1)
    {
      plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
      send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
    }

  return 0;
}
Exemple #14
0
void tftp_request(struct listener *listen, time_t now)
{
  ssize_t len;
  char *packet = daemon->packet;
  char *filename, *mode, *p, *end, *opt;
  union mysockaddr addr, peer;
  struct msghdr msg;
  struct iovec iov;
  struct ifreq ifr;
  int is_err = 1, if_index = 0, mtu = 0, special = 0;
#ifdef HAVE_DHCP
  struct iname *tmp;
#endif
  struct tftp_transfer *transfer;
  int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
  int mtuflag = IP_PMTUDISC_DONT;
#endif
  char namebuff[IF_NAMESIZE];
  char pretty_addr[ADDRSTRLEN];
  char *name;
  char *prefix = daemon->tftp_prefix;
  struct tftp_prefix *pref;
  struct interface_list *ir;

  union {
    struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_IPV6
    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(HAVE_LINUX_NETWORK)
    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(HAVE_SOLARIS_NETWORK)
    char control[CMSG_SPACE(sizeof(unsigned int))];
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
  } control_u; 

  msg.msg_controllen = sizeof(control_u);
  msg.msg_control = control_u.control;
  msg.msg_flags = 0;
  msg.msg_name = &peer;
  msg.msg_namelen = sizeof(peer);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  iov.iov_base = packet;
  iov.iov_len = daemon->packet_buff_sz;

  /* we overwrote the buffer... */
  daemon->srv_save = NULL;

  if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
    return;
  
  if (option_bool(OPT_NOWILD))
    {
      addr = listen->iface->addr;
      mtu = listen->iface->mtu;
      name = listen->iface->name;
    }
  else
    {
      struct cmsghdr *cmptr;
      int check;
      struct interface_list *ir;

      if (msg.msg_controllen < sizeof(struct cmsghdr))
        return;
      
      addr.sa.sa_family = listen->family;
      
#if defined(HAVE_LINUX_NETWORK)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
	    {
	      union {
		unsigned char *c;
		struct in_pktinfo *p;
	      } p;
	      p.c = CMSG_DATA(cmptr);
	      addr.in.sin_addr = p.p->ipi_spec_dst;
	      if_index = p.p->ipi_ifindex;
	    }
      
#elif defined(HAVE_SOLARIS_NETWORK)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  {
	    union {
	      unsigned char *c;
	      struct in_addr *a;
	      unsigned int *i;
	    } p;
	    p.c = CMSG_DATA(cmptr);
	    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
	    addr.in.sin_addr = *(p.a);
	    else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
	    if_index = *(p.i);
	  }
      
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  {
	    union {
	      unsigned char *c;
	      struct in_addr *a;
	      struct sockaddr_dl *s;
	    } p;
	    p.c = CMSG_DATA(cmptr);
	    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
	      addr.in.sin_addr = *(p.a);
	    else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
	      if_index = p.s->sdl_index;
	  }
	  
#endif

#ifdef HAVE_IPV6
      if (listen->family == AF_INET6)
        {
          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
            if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
              {
                union {
                  unsigned char *c;
                  struct in6_pktinfo *p;
                } p;
                p.c = CMSG_DATA(cmptr);
                  
                addr.in6.sin6_addr = p.p->ipi6_addr;
                if_index = p.p->ipi6_ifindex;
              }
        }
#endif
      
      if (!indextoname(listen->tftpfd, if_index, namebuff))
	return;

      name = namebuff;

#ifdef HAVE_IPV6
      if (listen->family == AF_INET6)
	check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index);
      else
#endif
        check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, &if_index);

      /* wierd TFTP service override */
      for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
	if (strcmp(ir->interface, name) == 0)
	  break;
       
      if (!ir)
	{
	  if (!daemon->tftp_unlimited || !check)
	    return;
	  
#ifdef HAVE_DHCP      
	  /* allowed interfaces are the same as for DHCP */
	  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
	    if (tmp->name && (strcmp(tmp->name, name) == 0))
	      return;
#endif
	}

      strncpy(ifr.ifr_name, name, IF_NAMESIZE);
      if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
	mtu = ifr.ifr_mtu;      
    }
  
  /* check for per-interface prefix */ 
  for (pref = daemon->if_prefix; pref; pref = pref->next)
    if (strcmp(pref->interface, name) == 0)
      prefix = pref->prefix;

  /* wierd TFTP interfaces disable special options. */
  for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
    if (strcmp(ir->interface, name) == 0)
      special = 1;

#ifdef HAVE_SOCKADDR_SA_LEN
  addr.sa.sa_len = sa_len(&addr);
#endif

  if (listen->family == AF_INET)
    addr.in.sin_port = htons(port);
#ifdef HAVE_IPV6
  else
    {
      addr.in6.sin6_port = htons(port);
      addr.in6.sin6_flowinfo = 0;
    }
#endif

  if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
    return;
  
  if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
    {
      free(transfer);
      return;
    }
  
  transfer->peer = peer;
  transfer->timeout = now + 2;
  transfer->backoff = 1;
  transfer->block = 1;
  transfer->blocksize = 512;
  transfer->offset = 0;
  transfer->file = NULL;
  transfer->opt_blocksize = transfer->opt_transize = 0;
  transfer->netascii = transfer->carrylf = 0;
 
  prettyprint_addr(&peer, pretty_addr);
  
  /* if we have a nailed-down range, iterate until we find a free one. */
  while (1)
    {
      if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
	  setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
	  !fix_fd(transfer->sockfd))
	{
	  if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
	    {
	      if (++port <= daemon->end_tftp_port)
		{ 
		  if (listen->family == AF_INET)
		    addr.in.sin_port = htons(port);
#ifdef HAVE_IPV6
		  else
		     addr.in6.sin6_port = htons(port);
#endif
		  continue;
		}
	      my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
	    }
	  free_transfer(transfer);
	  return;
	}
      break;
    }
  
  p = packet + 2;
  end = packet + len;

  if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
      !(filename = next(&p, end)) ||
      !(mode = next(&p, end)) ||
      (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
    len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_addr);
  else
    {
      if (strcasecmp(mode, "netascii") == 0)
	transfer->netascii = 1;
      
      while ((opt = next(&p, end)))
	{
	  if (strcasecmp(opt, "blksize") == 0)
	    {
	      if ((opt = next(&p, end)) &&
		  (special || !option_bool(OPT_TFTP_NOBLOCK)))
		{
		  transfer->blocksize = atoi(opt);
		  if (transfer->blocksize < 1)
		    transfer->blocksize = 1;
		  if (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 - 32)
		    transfer->blocksize = (unsigned)mtu - 32;
		  transfer->opt_blocksize = 1;
		  transfer->block = 0;
		}
	    }
	  else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
	    {
	      transfer->opt_transize = 1;
	      transfer->block = 0;
	    }
	}

      /* cope with backslashes from windows boxen. */
      while ((p = strchr(filename, '\\')))
	*p = '/';

      strcpy(daemon->namebuff, "/");
      if (prefix)
	{
	  if (prefix[0] == '/')
	    daemon->namebuff[0] = 0;
	  strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
	  if (prefix[strlen(prefix)-1] != '/')
	    strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));

	  if (!special && option_bool(OPT_TFTP_APREF))
	    {
	      size_t oldlen = strlen(daemon->namebuff);
	      struct stat statbuf;
	      
	      strncat(daemon->namebuff, pretty_addr, (MAXDNAME-1) - strlen(daemon->namebuff));
	      strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
	      
	      /* 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 */
	  if (filename[0] == '/')
	    {
	      if (strstr(filename, daemon->namebuff) == filename)
		daemon->namebuff[0] = 0;
	      else
		filename++;
	    }
	}
      else if (filename[0] == '/')
	daemon->namebuff[0] = 0;
      strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));

      /* check permissions and open file */
      if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
	{
	  if ((len = get_block(packet, transfer)) == -1)
	    len = tftp_err_oops(packet, daemon->namebuff);
	  else
	    is_err = 0;
	}
    }
  
  while (sendto(transfer->sockfd, packet, len, 0, 
		(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
  
  if (is_err)
    free_transfer(transfer);
  else
    {
      transfer->next = daemon->tftp_trans;
      daemon->tftp_trans = transfer;
    }
}
Exemple #15
0
/* returns new last_server */	
static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr,
			  struct all_addr *dst_addr, unsigned int dst_iface,
			  HEADER *header, size_t plen, time_t now, struct frec *forward)
{
  char *domain = NULL;
  int type = 0;
  struct all_addr *addrp = NULL;
  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
  unsigned short flags = 0;
  unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
  struct server *start = NULL;
    
  /* may be no servers available. */
  if (!daemon->servers)
    forward = NULL;
  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
    {
      /* retry on existing query, send to all available servers  */
      domain = forward->sentto->domain;
      if (!(daemon->options & OPT_ORDER))
	{
	  forward->forwardall = 1;
	  daemon->last_server = NULL;
	}
      type = forward->sentto->flags & SERV_TYPE;
      if (!(start = forward->sentto->next))
	start = daemon->servers; /* at end of list, recycle */
      header->id = htons(forward->new_id);
    }
  else 
    {
      if (gotname)
	flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
      
      if (!flags && !(forward = get_new_frec(daemon, now, NULL)))
	/* table full - server failure. */
	flags = F_NEG;
      
      if (forward)
	{
	  forward->source = *udpaddr;
	  forward->dest = *dst_addr;
	  forward->iface = dst_iface;
	  forward->new_id = get_id();
	  forward->fd = udpfd;
	  forward->orig_id = ntohs(header->id);
	  forward->crc = crc;
	  forward->forwardall = 0;
	  header->id = htons(forward->new_id);

	  /* In strict_order mode, or when using domain specific servers
	     always try servers in the order specified in resolv.conf,
	     otherwise, use the one last known to work. */
	  
	  if (type != 0  || (daemon->options & OPT_ORDER))
	    start = daemon->servers;
	  else if (!(start = daemon->last_server))
	    {
	      start = daemon->servers;
	      forward->forwardall = 1;
	    }
	}
    }

  /* check for send errors here (no route to host) 
     if we fail to send to all nameservers, send back an error
     packet straight away (helps modem users when offline)  */
  
  if (!flags && forward)
    {
      struct server *firstsentto = start;
      int forwarded = 0;

      while (1)
	{ 
	  /* only send to servers dealing with our domain.
	     domain may be NULL, in which case server->domain 
	     must be NULL also. */
	  
	  if (type == (start->flags & SERV_TYPE) &&
	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
	      !(start->flags & SERV_LITERAL_ADDRESS))
	    {
	      if (sendto(start->sfd->fd, (char *)header, plen, 0,
			 &start->addr.sa,
			 sa_len(&start->addr)) == -1)
		{
		  if (retry_send())
		    continue;
		}
	      else
		{
		  /* Keep info in case we want to re-send this packet */
		  daemon->srv_save = start;
		  daemon->packet_len = plen;
		  
		  if (!gotname)
		    strcpy(daemon->namebuff, "query");
		  if (start->addr.sa.sa_family == AF_INET)
		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in.sin_addr, 0,
			      NULL, 0); 
#ifdef HAVE_IPV6
		  else
		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&start->addr.in6.sin6_addr, 0,
			      NULL, 0);
#endif 
		  forwarded = 1;
		  forward->sentto = start;
		  if (!forward->forwardall) 
		    break;
		  forward->forwardall++;
		}
	    } 
	  
	  if (!(start = start->next))
 	    start = daemon->servers;
	  
	  if (start == firstsentto)
	    break;
	}
      
      if (forwarded)
	  return;
      
      /* could not send on, prepare to return */ 
      header->id = htons(forward->orig_id);
      forward->new_id = 0; /* cancel */
    }	  
  
  /* could not send on, return empty answer or address if known for whole domain */
  if (udpfd != -1)
    {
      plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
      send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
    }

  return;
}
/* returns new last_server */
struct server *forward_query(int udpfd, int peerfd, int peerfd6,
                             union mysockaddr *udpaddr, HEADER *header,
                             int plen, int strict_order, char *dnamebuff,
                             struct server *servers, struct server *last_server)
{
    time_t now = time(NULL);
    struct frec *forward;
    char *domain = NULL;
    struct server *serv, *tmp_server = NULL;
    int gotname = extract_request(header, (unsigned int)plen, dnamebuff);
    /* may be  recursion not speced or no servers available. */
    if (!header->rd || !servers)
        forward = NULL;
    else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
    {
        /* retry on existing query, send to next server */
        domain = forward->sentto->domain;
        if (!(forward->sentto = forward->sentto->next))
            forward->sentto = servers; /* at end of list, recycle */
        header->id = htons(forward->new_id);
    }
    else
    {
        /* new query, pick nameserver and send */
        forward = get_new_frec(now);

        /* If the query ends in the domain in one of our servers, set
        domain to point to that name. We find the largest match to allow both
         domain.org and sub.domain.org to exist. */

        if (gotname)
        {
            unsigned int namelen = strlen(dnamebuff);
            unsigned int matchlen = 0;
            for (serv=servers; serv; serv=serv->next)
                if (serv->domain)
                {
                    unsigned int domainlen = strlen(serv->domain);
                    //cprintf("tallest:=====( domainlen=%d, namelen=%d, dnamebuff=%s, serv->domain=%s, servip=%s, sa=%x )=====\n"
                    //	,domainlen, namelen, dnamebuff, serv->domain, inet_ntoa(serv->addr.in.sin_addr), serv->addr.sa.sa_data);
                    if (namelen >= domainlen &&
                            strcmp(dnamebuff + namelen - domainlen, serv->domain) == 0 &&
                            domainlen > matchlen)
                    {
                        //cprintf("tallest:=====( Match it!! dnamebuff=%s, serv->domain=%s, Using DNS=%s )=====\n", dnamebuff, serv->domain, inet_ntoa(serv->addr.in.sin_addr));
                        domain = serv->domain;
                        matchlen = domainlen;
                    }
                }
        }

        /* In strict_order mode, or when using domain specific servers
        always try servers in the order specified in resolv.conf,
         otherwise, use the one last known to work. */

        if (domain || strict_order)
            forward->sentto = servers;
        else
            forward->sentto = last_server;

        forward->source = *udpaddr;
        forward->new_id = get_id();
        forward->fd = udpfd;
        forward->orig_id = ntohs(header->id);
        header->id = htons(forward->new_id);
    }

    /* check for send errors here (no route to host)
       if we fail to send to all nameservers, send back an error
       packet straight away (helps modem users when offline)  */

    if (forward)
    {
        struct server *firstsentto = forward->sentto;
        int sendnotfail = 0;// add by zg 2006.10.23 to fix cdrouter3.3 item 129(cdrouter_app_25) bug
        while (1)
        {
            int af = forward->sentto->addr.sa.sa_family;
            int fd = af == AF_INET ? peerfd : peerfd6;

            /* only send to servers dealing with our domain.
               domain may be NULL, in which case server->domain
               must be NULL also. */

            if ((!domain && !forward->sentto->domain) ||
                    (domain && forward->sentto->domain && strcmp(domain, forward->sentto->domain) == 0))
            {
#ifdef DNS_SUPPORT
                int ret;
                char *ping_argv[] = { "ping",
                                      "-c", "1",
                                      inet_ntoa(forward->sentto->addr.in.sin_addr),
                                      NULL
                                    };
                ret = _eval(ping_argv, NULL, 10, NULL);
                if (ret != 0) {
                    syslog(LOG_WARNING,
                           "Fail to connect to nameserver(%s) !!\n",inet_ntoa(forward->sentto->addr.in.sin_addr));

                    cprintf("Fail to connect to nameserver(%s) !!\n",inet_ntoa(forward->sentto->addr.in.sin_addr));
                }
#endif
                if (sendto(fd, (char *)header, plen, 0,
                           &forward->sentto->addr.sa,
                           sa_len(&forward->sentto->addr)) != -1)
                {
                    sendnotfail = 1;// add by zg 2006.10.23 to fix cdrouter3.3 item 129(cdrouter_app_25) bug

                    /* ******* zg porting DWG814I Source code on 2006.11.06 ******* */
                    /* ******* To fixed cdrouterv3.3 item 333(dna_45) item 334(dns_45) failed bug ******* */
                    if(forward->dnsMsgBufPtr == NULL)
                    {
                        forward->dnsMsgBufPtr = get_dns_msg_buf();
                        if(forward->dnsMsgBufPtr != NULL)
                        {
                            struct DNS_MSG_BUF * dnsMsgBufPtr;
                            //DBG_printf("send query ok and backup it\n");
                            dnsMsgBufPtr = forward->dnsMsgBufPtr;
                            dnsMsgBufPtr->udpfd = udpfd;
                            dnsMsgBufPtr->plen = plen;
                            dnsMsgBufPtr->firstsentto = firstsentto;
                            if (!(dnsMsgBufPtr->nextsentto = forward->sentto->next))
                            {
                                dnsMsgBufPtr->nextsentto = servers;
                            }
                            dnsMsgBufPtr->servers = servers;
                            memcpy(&dnsMsgBufPtr->udpaddr, udpaddr, sizeof(union mysockaddr));
                            memcpy(dnsMsgBufPtr->header, header, plen);
                            ((HEADER *)(&dnsMsgBufPtr->header))->id = forward->orig_id;
                        }
                    }
                    if (af == AF_INET)
                        log_query(F_SERVER | F_IPV4 | F_FORWARD, gotname ? dnamebuff : "query",
                                  (struct all_addr *)&forward->sentto->addr.in.sin_addr);
#ifdef HAVE_IPV6
                    else
                        log_query(F_SERVER | F_IPV6 | F_FORWARD, gotname ? dnamebuff : "query",
                                  (struct all_addr *)&forward->sentto->addr.in6.sin6_addr);
#endif
                    break;
                    /* *******end by zg porting DWG814I Source code on 2006.11.06 ******* */

                    /* for no-domain, dont't update last_server */
                    // modify by zg 2006.10.23 to fix cdrouter3.3 item 129(cdrouter_app_25) bug
                    //return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
#if 0
                    if (af == AF_INET)
                        log_query(F_SERVER | F_IPV4 | F_FORWARD, gotname ? dnamebuff : "query",
                                  (struct all_addr *)&forward->sentto->addr.in.sin_addr);
#ifdef HAVE_IPV6
                    else
                        log_query(F_SERVER | F_IPV6 | F_FORWARD, gotname ? dnamebuff : "query",
                                  (struct all_addr *)&forward->sentto->addr.in6.sin6_addr);
#endif
                    /* for no-domain, dont't update last_server */
                    //return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
                    tmp_server = domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);	// For cdrouter_app_25
#endif
                }
            }

            if (!(forward->sentto = forward->sentto->next))
                forward->sentto = servers;

            /* check if we tried all without success */
            if (forward->sentto == firstsentto) {
#if 0
                if (tmp_server != NULL)
                    return tmp_server;
                else
#endif
                    break;
            }
        }

        /* could not send on, prepare to return */
        /******** add by zg 2006.10.23 to fix cdrouter3.3 item 129(cdrouter_app_25) bug ********/
        if(!sendnotfail)
        {
            header->id = htons(forward->orig_id);
            forward->new_id = 0; /* cancel */
        }
        else
            return last_server;
        /**************** end by zg 2006.10.23 ***************/

    }

    /* could not send on, return empty answer */
    header->qr = 1; /* response */
    header->aa = 0; /* authoritive - never */
    header->ra = 1; /* recursion if available */
    header->tc = 0; /* not truncated */
    header->rcode = NOERROR; /* no error */
    header->ancount = htons(0); /* no answers */
    header->nscount = htons(0);
    header->arcount = htons(0);
    sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr));

    return last_server;
}
/* returns new last_server */
struct server *reply_query(int fd, char *packet, char *dnamebuff, struct server *last_server,unsigned long *timetolive)
{
    /* packet from peer server, extract data for cache, and send to
       original requester */
    struct frec *forward;
    HEADER *header;
    int n = recv(fd, packet, PACKETSZ, 0);
    header = (HEADER *)packet;
    if (n >= (int)sizeof(HEADER) && header->qr)
    {
        if ((forward = lookup_frec(ntohs(header->id))))
        {
            //if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
            if (header->rcode == NOERROR)
            {
                /* ******* zg porting DWG814I Source code on 2006.11.06 ******* */
                /* ******* To fixed cdrouterv3.3 item 333(dna_45) item 334(dns_45) failed bug ******* */
                if(forward ->dnsMsgBufPtr != NULL)
                {
                    free_dns_msg_buf(forward ->dnsMsgBufPtr);
                    forward ->dnsMsgBufPtr = NULL;
                }
                /* ******* end by zg porting DWG814I Source code on 2006.11.06 ******* */

                if (!forward->sentto->domain)
                    last_server = forward->sentto; /* known good */
                if (header->opcode == QUERY) {
                    strncpy(query_name, dnamebuff, sizeof(query_name));
                    extract_addresses(header, (unsigned int)n, dnamebuff, timetolive);
                }

                header->id = htons(forward->orig_id);
                /* There's no point returning an upstream reply marked as truncated,
                   since that will prod the resolver into moving to TCP - which we
                   don't support. */
                header->tc = 0; /* goodbye truncate */
                sendto(forward->fd, packet, n, 0,
                       &forward->source.sa, sa_len(&forward->source));
                forward->new_id = 0; /* cancel */
            }
            /* ******* zg porting DWG814I Source code on 2006.11.06 ******* */
            /* ******* To fixed cdrouterv3.3 item 333(dna_45) item 334(dns_45) failed bug ******* */
            else switch(header->rcode)
                {
                case SERVFAIL:
                case NOTIMP:
                case REFUSED:
                    //DBG_printf("SERVER fail, not implement, refuse, try next server\n");
                    break;

                default:
                    //DBG_printf("SERVER response general error, return it to client\n");
                    if(forward ->dnsMsgBufPtr != NULL)
                    {
                        free_dns_msg_buf(forward ->dnsMsgBufPtr);
                        forward ->dnsMsgBufPtr = NULL;
                    }

                    header->id = htons(forward->orig_id);
                    /* There's no point returning an upstream reply marked as truncated,
                    since that will prod the resolver into moving to TCP - which we
                    don't support. */
                    header->tc = 0; /* goodbye truncate */
                    sendto(forward->fd, packet, n, 0,
                           &forward->source.sa, sa_len(&forward->source));
                    forward->new_id = 0; /* cancel */
                    break;
                }
            /* ******* end by zg porting DWG814I Source code on 2006.11.06 ******* */

        }
    }

    return last_server;
}
Exemple #18
0
void tftp_request(struct listener *listen, time_t now)
{
  ssize_t len;
  char *packet = daemon->packet;
  char *filename, *mode, *p, *end, *opt;
  union mysockaddr addr, peer;
  struct msghdr msg;
  struct iovec iov;
  struct ifreq ifr;
  int is_err = 1, if_index = 0, mtu = 0;
  struct iname *tmp;
  struct tftp_transfer *transfer;
  int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
  int mtuflag = IP_PMTUDISC_DONT;
#endif
  char namebuff[IF_NAMESIZE];
  char *name = NULL;
  char *prefix = daemon->tftp_prefix;
  struct tftp_prefix *pref;
  union all_addr addra;
  /* Can always get recvd interface for IPv6 */
  int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
  union {
    struct cmsghdr align; /* this ensures alignment */
    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#if defined(HAVE_LINUX_NETWORK)
    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(HAVE_SOLARIS_NETWORK)
    char control[CMSG_SPACE(sizeof(unsigned int))];
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
  } control_u; 

  msg.msg_controllen = sizeof(control_u);
  msg.msg_control = control_u.control;
  msg.msg_flags = 0;
  msg.msg_name = &peer;
  msg.msg_namelen = sizeof(peer);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  iov.iov_base = packet;
  iov.iov_len = daemon->packet_buff_sz;

  /* we overwrote the buffer... */
  daemon->srv_save = NULL;

  if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
    return;

  /* Can always get recvd interface for IPv6 */
  if (!check_dest)
    {
      if (listen->iface)
	{
	  addr = listen->iface->addr;
	  name = listen->iface->name;
	  mtu = listen->iface->mtu;
	  if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
	    mtu = daemon->tftp_mtu;
	}
      else
	{
	  /* we're listening on an address that doesn't appear on an interface,
	     ask the kernel what the socket is bound to */
	  socklen_t tcp_len = sizeof(union mysockaddr);
	  if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
	    return;
	}
    }
  else
    {
      struct cmsghdr *cmptr;

      if (msg.msg_controllen < sizeof(struct cmsghdr))
        return;
      
      addr.sa.sa_family = listen->family;
      
#if defined(HAVE_LINUX_NETWORK)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
	    {
	      union {
		unsigned char *c;
		struct in_pktinfo *p;
	      } p;
	      p.c = CMSG_DATA(cmptr);
	      addr.in.sin_addr = p.p->ipi_spec_dst;
	      if_index = p.p->ipi_ifindex;
	    }
      
#elif defined(HAVE_SOLARIS_NETWORK)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  {
	    union {
	      unsigned char *c;
	      struct in_addr *a;
	      unsigned int *i;
	    } p;
	    p.c = CMSG_DATA(cmptr);
	    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
	    addr.in.sin_addr = *(p.a);
	    else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
	    if_index = *(p.i);
	  }
      
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
      if (listen->family == AF_INET)
	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
	  {
	    union {
	      unsigned char *c;
	      struct in_addr *a;
	      struct sockaddr_dl *s;
	    } p;
	    p.c = CMSG_DATA(cmptr);
	    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
	      addr.in.sin_addr = *(p.a);
	    else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
	      if_index = p.s->sdl_index;
	  }
	  
#endif

      if (listen->family == AF_INET6)
        {
          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
            if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
              {
                union {
                  unsigned char *c;
                  struct in6_pktinfo *p;
                } p;
                p.c = CMSG_DATA(cmptr);
                  
                addr.in6.sin6_addr = p.p->ipi6_addr;
                if_index = p.p->ipi6_ifindex;
              }
        }
      
      if (!indextoname(listen->tftpfd, if_index, namebuff))
	return;

      name = namebuff;
      
      addra.addr4 = addr.in.sin_addr;

      if (listen->family == AF_INET6)
	addra.addr6 = addr.in6.sin6_addr;

      if (daemon->tftp_interfaces)
	{
	  /* dedicated tftp interface list */
	  for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
	    if (tmp->name && wildcard_match(tmp->name, name))
	      break;

	  if (!tmp)
	    return;
	}
      else
	{
	  /* Do the same as DHCP */
	  if (!iface_check(listen->family, &addra, name, NULL))
	    {
	      if (!option_bool(OPT_CLEVERBIND))
		enumerate_interfaces(0); 
	      if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
		  !label_exception(if_index, listen->family, &addra))
		return;
	    }
	  
#ifdef HAVE_DHCP      
	  /* allowed interfaces are the same as for DHCP */
	  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
	    if (tmp->name && wildcard_match(tmp->name, name))
	      return;
#endif
	}

      safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
      if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
	{
	  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;

  if (name)
    {
      /* check for per-interface prefix */ 
      for (pref = daemon->if_prefix; pref; pref = pref->next)
	if (strcmp(pref->interface, name) == 0)
	  prefix = pref->prefix;  
    }

  if (listen->family == AF_INET)
    {
      addr.in.sin_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
      addr.in.sin_len = sizeof(addr.in);
#endif
    }
  else
    {
      addr.in6.sin6_port = htons(port);
      addr.in6.sin6_flowinfo = 0;
      addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
      addr.in6.sin6_len = sizeof(addr.in6);
#endif
    }

  if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
    return;
  
  if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
    {
      free(transfer);
      return;
    }
  
  transfer->peer = peer;
  transfer->timeout = now + 2;
  transfer->backoff = 1;
  transfer->block = 1;
  transfer->blocksize = 512;
  transfer->offset = 0;
  transfer->file = NULL;
  transfer->opt_blocksize = transfer->opt_transize = 0;
  transfer->netascii = transfer->carrylf = 0;
 
  prettyprint_addr(&peer, daemon->addrbuff);
  
  /* if we have a nailed-down range, iterate until we find a free one. */
  while (1)
    {
      if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
	  setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
	  !fix_fd(transfer->sockfd))
	{
	  if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
	    {
	      if (++port <= daemon->end_tftp_port)
		{ 
		  if (listen->family == AF_INET)
		    addr.in.sin_port = htons(port);
		  else
		    addr.in6.sin6_port = htons(port);
		  
		  continue;
		}
	      my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
	    }
	  free_transfer(transfer);
	  return;
	}
      break;
    }
  
  p = packet + 2;
  end = packet + len;

  if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
      !(filename = next(&p, end)) ||
      !(mode = next(&p, end)) ||
      (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
    {
      len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
      is_err = 1;
    }
  else
    {
      if (strcasecmp(mode, "netascii") == 0)
	transfer->netascii = 1;
      
      while ((opt = next(&p, end)))
	{
	  if (strcasecmp(opt, "blksize") == 0)
	    {
	      if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
		{
		  /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
		  int overhead = (listen->family == AF_INET) ? 32 : 52;
		  transfer->blocksize = atoi(opt);
		  if (transfer->blocksize < 1)
		    transfer->blocksize = 1;
		  if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
		    transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
		  if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
		    transfer->blocksize = (unsigned)mtu - overhead;
		  transfer->opt_blocksize = 1;
		  transfer->block = 0;
		}
	    }
	  else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
	    {
	      transfer->opt_transize = 1;
	      transfer->block = 0;
	    }
	}

      /* cope with backslashes from windows boxen. */
      for (p = filename; *p; p++)
	if (*p == '\\')
	  *p = '/';
	else if (option_bool(OPT_TFTP_LC))
	  *p = tolower(*p);
		
      strcpy(daemon->namebuff, "/");
      if (prefix)
	{
	  if (prefix[0] == '/')
	    daemon->namebuff[0] = 0;
	  strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
	  if (prefix[strlen(prefix)-1] != '/')
	    strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));

	  if (option_bool(OPT_TFTP_APREF_IP))
	    {
	      size_t oldlen = strlen(daemon->namebuff);
	      struct stat statbuf;
	      
	      strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
	      strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
	      
	      /* remove unique-directory if it doesn't exist */
	      if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
		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 */
	  if (filename[0] == '/')
	    {
	      if (strstr(filename, daemon->namebuff) == filename)
		daemon->namebuff[0] = 0;
	      else
		filename++;
	    }
	}
      else if (filename[0] == '/')
	daemon->namebuff[0] = 0;
      strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
      
      /* check permissions and open file */
      if ((transfer->file = check_tftp_fileperm(&len, prefix)))
	{
	  if ((len = get_block(packet, transfer)) == -1)
	    len = tftp_err_oops(packet, daemon->namebuff);
	  else
	    is_err = 0;
	}
    }
  
  while (sendto(transfer->sockfd, packet, len, 0, 
		(struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
  
  if (is_err)
    free_transfer(transfer);
  else
    {
      transfer->next = daemon->tftp_trans;
      daemon->tftp_trans = transfer;
    }
}
Exemple #19
0
void check_tftp_listeners(time_t now)
{
  struct tftp_transfer *transfer, *tmp, **up;
  ssize_t len;
  
  struct ack {
    unsigned short op, block;
  } *mess = (struct ack *)daemon->packet;
  
  /* 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))
	{
	  /* we overwrote the buffer... */
	  daemon->srv_save = NULL;
	  
	  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;
		}
	    }
	}
      
      if (difftime(now, transfer->timeout) >= 0.0)
	{
	  int endcon = 0;

	  /* timeout, retransmit */
	  transfer->timeout += 1 + (1<<transfer->backoff);
	  	  
	  /* we overwrote the buffer... */
	  daemon->srv_save = NULL;
	 
	  if ((len = get_block(daemon->packet, transfer)) == -1)
	    {
	      len = tftp_err_oops(daemon->packet, transfer->file->filename);
	      endcon = 1;
	    }
	  /* don't complain about timeout when we're awaiting the last
	     ACK, some clients never send it */
	  else if (++transfer->backoff > 7 && len != 0)
	    {
	      endcon = 1;
	      len = 0;
	    }

	  if (len != 0)
	    while(sendto(transfer->sockfd, daemon->packet, len, 0, 
			 (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
	  
	  if (endcon || len == 0)
	    {
	      strcpy(daemon->namebuff, transfer->file->filename);
	      sanitise(daemon->namebuff);
	      my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
	      /* unlink */
	      *up = tmp;
	      if (endcon)
		free_transfer(transfer);
	      else
		{
		  /* put on queue to be sent to script and deleted */
		  transfer->next = daemon->tftp_done_trans;
		  daemon->tftp_done_trans = transfer;
		}
	      continue;
	    }
	}

      up = &transfer->next;
    }    
}
Exemple #20
0
/* The daemon forks before calling this: it should deal with one connection,
   blocking as neccessary, and then return. Note, need to be a bit careful
   about resources for debug mode, when the fork is suppressed: that's
   done by the caller. */
unsigned char *tcp_request(int confd, time_t now,
			   struct in_addr local_addr, struct in_addr netmask)
{
  int size = 0;
  size_t m;
  unsigned short qtype, gotname;
  unsigned char c1, c2;
  /* Max TCP packet + slop */
  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
  HEADER *header;
  struct server *last_server;
  
  while (1)
    {
      if (!packet ||
	  !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
	  !(size = c1 << 8 | c2) ||
	  !read_write(confd, packet, size, 1))
       	return packet; 
  
      if (size < (int)sizeof(HEADER))
	continue;
      
      header = (HEADER *)packet;
      
      if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
	{
	  union mysockaddr peer_addr;
	  socklen_t peer_len = sizeof(union mysockaddr);
	  
	  if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
	    {
	      char types[20];

	      querystr(types, qtype);

	      if (peer_addr.sa.sa_family == AF_INET) 
		log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
			  (struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
	      else
		log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
			  (struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif
	    }
	}
      
      /* m > 0 if answered from cache */
      m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, 
			 local_addr, netmask, now);

      /* Do this by steam now we're not in the select() loop */
      check_log_writer(NULL); 
      
      if (m == 0)
	{
	  unsigned short flags = 0;
	  struct all_addr *addrp = NULL;
	  int type = 0;
	  char *domain = NULL;
	  
	  if (gotname)
	    flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
	  
	  if (type != 0  || (daemon->options & OPT_ORDER) || !daemon->last_server)
	    last_server = daemon->servers;
	  else
	    last_server = daemon->last_server;
      
	  if (!flags && last_server)
	    {
	      struct server *firstsendto = NULL;
	      unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);

	      /* Loop round available servers until we succeed in connecting to one.
	         Note that this code subtley ensures that consecutive queries on this connection
	         which can go to the same server, do so. */
	      while (1) 
		{
		  if (!firstsendto)
		    firstsendto = last_server;
		  else
		    {
		      if (!(last_server = last_server->next))
			last_server = daemon->servers;
		      
		      if (last_server == firstsendto)
			break;
		    }
	      
		  /* server for wrong domain */
		  if (type != (last_server->flags & SERV_TYPE) ||
		      (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
		    continue;
		  
		  if ((last_server->tcpfd == -1) &&
		      (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
		      (!local_bind(last_server->tcpfd, &last_server->source_addr,
				   last_server->interface, last_server->mark, 1) ||
		       connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
		    {
		      close(last_server->tcpfd);
		      last_server->tcpfd = -1;
		    }
		  
		  if (last_server->tcpfd == -1)	
		    continue;

		  c1 = size >> 8;
		  c2 = size;
		  
		  if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
		      !read_write(last_server->tcpfd, &c2, 1, 0) ||
		      !read_write(last_server->tcpfd, packet, size, 0) ||
		      !read_write(last_server->tcpfd, &c1, 1, 1) ||
		      !read_write(last_server->tcpfd, &c2, 1, 1))
		    {
		      close(last_server->tcpfd);
		      last_server->tcpfd = -1;
		      continue;
		    } 
		  
		  m = (c1 << 8) | c2;
		  if (!read_write(last_server->tcpfd, packet, m, 1))
		    return packet;
		  
		  if (!gotname)
		    strcpy(daemon->namebuff, "query");
		  if (last_server->addr.sa.sa_family == AF_INET)
		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
#ifdef HAVE_IPV6
		  else
		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
			      (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
#endif 
		  
		  /* There's no point in updating the cache, since this process will exit and
		     lose the information after a few queries. We make this call for the alias and 
		     bogus-nxdomain side-effects. */
		  /* If the crc of the question section doesn't match the crc we sent, then
		     someone might be attempting to insert bogus values into the cache by 
		     sending replies containing questions and bogus answers. */
		  if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
		    m = process_reply(header, now, last_server, (unsigned int)m);
		  
		  break;
		}
	    }
	  
	  /* In case of local answer or no connections made. */
	  if (m == 0)
	    m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
	}

      check_log_writer(NULL);
      
      c1 = m>>8;
      c2 = m;
      if (!read_write(confd, &c1, 1, 0) ||
	  !read_write(confd, &c2, 1, 0) || 
	  !read_write(confd, packet, m, 0))
	return packet;
    }
Exemple #21
0
/* Send a UDP packet with its source address set as "source" 
   unless nowild is true, when we just send it with the kernel default */
static void send_from(int fd, int nowild, char *packet, size_t len, 
		      union mysockaddr *to, struct all_addr *source,
		      unsigned int iface)
{
  struct msghdr msg;
  struct iovec iov[1]; 
  union {
    struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_SENDSRCADDR)
    char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
#ifdef HAVE_IPV6
    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
  } control_u;
  
  iov[0].iov_base = packet;
  iov[0].iov_len = len;

  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;
  msg.msg_name = to;
  msg.msg_namelen = sa_len(to);
  msg.msg_iov = iov;
  msg.msg_iovlen = 1;
  
  if (!nowild)
    {
      struct cmsghdr *cmptr;
      msg.msg_control = &control_u;
      msg.msg_controllen = sizeof(control_u);
      cmptr = CMSG_FIRSTHDR(&msg);

      if (to->sa.sa_family == AF_INET)
	{
#if defined(HAVE_LINUX_NETWORK)
	  struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
	  pkt->ipi_ifindex = 0;
	  pkt->ipi_spec_dst = source->addr.addr4;
	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
	  cmptr->cmsg_level = SOL_IP;
	  cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
	  struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
	  *a = source->addr.addr4;
	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
	  cmptr->cmsg_level = IPPROTO_IP;
	  cmptr->cmsg_type = IP_SENDSRCADDR;
#endif
	}
      else
#ifdef HAVE_IPV6
	{
	  struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
	  pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
	  pkt->ipi6_addr = source->addr.addr6;
	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
	  cmptr->cmsg_type = IPV6_PKTINFO;
	  cmptr->cmsg_level = IPV6_LEVEL;
	}
#else
      iface = 0; /* eliminate warning */
#endif
    }
  
 retry:
  if (sendmsg(fd, &msg, 0) == -1)
    {
      /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
	 by returning EINVAL from sendmsg. In that case, try again without setting the
	 source address, since it will nearly alway be correct anyway.  IPv6 stinks. */
      if (errno == EINVAL && msg.msg_controllen)
	{
	  msg.msg_controllen = 0;
	  goto retry;
	}
      if (retry_send())
	goto retry;
    }
}
void
do_udp_test(int s1, int type, struct sockaddr *slave,
            int nbufs, int buflen, int pause_time, int pause_threshold)
{
    int i, s, td_len, seq, seq_errors, total_packets;
    struct sockaddr_storage test_chan_master, test_chan_slave;
    struct timeval start_time, end_time;
    struct nc_request req;
    struct nc_reply reply;
    struct nc_test_results results;
    struct nc_test_data *tdp;
    fd_set fds;
    struct timeval timeout;
    int lost_packets = 0;
    int need_send, need_recv;
    const char *type_name;
    int pkt_ctr = 0;

    need_recv = true;  need_send = true;  type_name = "UDP echo";
    switch (type) {
    case NC_REQUEST_UDP_RECV:
        need_recv = false;
        need_send = true;
        type_name = "UDP recv";
        break;
    case NC_REQUEST_UDP_SEND:
        need_recv = true;
        need_send = false;
        type_name = "UDP send";
        break;
    case NC_REQUEST_UDP_ECHO:
        break;
    }

    new_test();
    req.type = htonl(type);
    req.nbufs = htonl(nbufs);
    req.buflen = htonl(buflen);
    req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
    req.master_port = htonl(NC_TESTING_MASTER_PORT);
    nc_message(s1, &req, &reply, slave);
    if (reply.response != ntohl(NC_REPLY_ACK)) {
        test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
        return;
    }

    s = socket(slave->sa_family, SOCK_DGRAM, 0);
    if (s < 0) {
        pexit("datagram socket");
    }

    memset(&test_chan_master, 0, sizeof(test_chan_master));
    ((struct sockaddr *)&test_chan_master)->sa_family = slave->sa_family;
    memcpy(&test_chan_slave, slave, sa_len(slave));
#ifndef __linux
    ((struct sockaddr *)&test_chan_master)->sa_len = slave->sa_len;
#endif
    switch (slave->sa_family) {
    case AF_INET:
        ((struct sockaddr_in *)&test_chan_master)->sin_addr.s_addr = htonl(INADDR_ANY);
        ((struct sockaddr_in *)&test_chan_master)->sin_port = htons(ntohl(req.master_port));
        ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req.slave_port));
        break;
    case AF_INET6:
        ((struct sockaddr_in6 *)&test_chan_master)->sin6_addr = in6addr_any;
        ((struct sockaddr_in6 *)&test_chan_master)->sin6_port = htons(ntohl(req.master_port));
        ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req.slave_port));
        break;
    default:
        pexit("strange TCP sockaddr");
    }

    if (bind(s, (struct sockaddr *)&test_chan_master, 
             sa_len((struct sockaddr *)&test_chan_master)) < 0) {
        perror("UDP bind <do_udp_test>");
        close(s);
        return;
    }
    test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
    if (pause_time) {
        test_printf(" - %dms delay after %d packet%s\n", pause_time*10, 
                    pause_threshold, pause_threshold > 1 ? "s" : "");
    } else {
        test_printf(" - no delays\n");
    }

    gettimeofday(&start_time, 0);
    seq = 0;  seq_errors = 0;  total_packets = 0;
    for (i = 0;  i < nbufs;  i++) {
        td_len = buflen + sizeof(struct nc_test_data);
        if (need_send) {
            tdp = (struct nc_test_data *)out_buf;
            tdp->key1 = htonl(NC_TEST_DATA_KEY1);
            tdp->key2 = htonl(NC_TEST_DATA_KEY2);
            tdp->seq = htonl(i);
            tdp->len = htonl(td_len);
            if (sendto(s, tdp, td_len, 0, 
                       (struct sockaddr *)&test_chan_slave, 
                       sa_len((struct sockaddr *)&test_chan_slave)) < 0) {
                perror("sendto");
                close(s);
                return;
            }
            total_packets++;
        }
        if (need_recv) {
            FD_ZERO(&fds);
            FD_SET(s, &fds);
            timeout.tv_sec = NC_TEST_TIMEOUT;
            timeout.tv_usec = 0;
            if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
                test_printf("Slave timed out after %d buffers\n", i);
                lost_packets++;
            } else {
                tdp = (struct nc_test_data *)in_buf;
                if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
                    perror("recvfrom");
                    close(s);
                    return;
                }
                if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
                    (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
                    if (ntohl(tdp->seq) != seq) {
                        test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
                                    ntohl(tdp->seq), seq);
                        seq_errors++;
                        if (!need_send) {
                            // Reset sequence to what the slave wants
                            seq = ntohl(tdp->seq);
                        }
                    }
                } else {
                    test_printf("Bad data packet - key: %x/%x, seq: %d\n",
                                ntohl(tdp->key1), ntohl(tdp->key2),
                                ntohl(tdp->seq));
                }
                total_packets++;
            }
            seq++;
            if (seq == nbufs) {
                break;
            }
            if (pause_time && (++pkt_ctr == pause_threshold)) {
                pkt_ctr = 0;
                test_delay(pause_time);
            }
        }
    }
    gettimeofday(&end_time, 0);
    show_results(type_name, &start_time, &end_time, total_packets, buflen, 
                 lost_packets, seq_errors);
    // Fetch results record
    FD_ZERO(&fds);
    FD_SET(s, &fds);
    timeout.tv_sec = NC_RESULTS_TIMEOUT;
    timeout.tv_usec = 0;
    if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
        test_printf("No results record sent\n");
    } else {
        if (recvfrom(s, &results, sizeof(results), 0, 0, 0) < 0) {
            perror("recvfrom");
        }
        show_test_results(&results);
    }
    close(s);
}
Exemple #23
0
int main (int argc, char **argv)
{
  char *int_err_string;
  int cachesize = CACHESIZ;
  int port = NAMESERVER_PORT;
  int query_port = 0;
  unsigned long local_ttl = 0;
  int logged_lease = 0, first_loop = 1;
  unsigned int options;
  char *runfile = RUNFILE;
  time_t resolv_changed = 0;
  time_t now, last = 0;
  time_t lease_file_change = 0;
  ino_t lease_file_inode = (ino_t)0;
  struct irec *iface, *interfaces = NULL;
  char *mxname = NULL;
  char *mxtarget = NULL;
  char *lease_file = NULL;
  char *addn_hosts = NULL;
  char *domain_suffix = NULL;
  char *username = CHUSER;
  char *groupname = CHGRP;
  struct iname *if_names = NULL;
  struct iname *if_addrs = NULL;
  struct iname *if_except = NULL;
  struct iname *if_tmp;
  struct server *serv_addrs = NULL;
  char *dnamebuff, *packet;
  struct server *servers, *last_server;
  struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
  struct resolvc *resolv = &default_resolv;
  struct bogus_addr *bogus_addr = NULL;
  struct serverfd *serverfdp, *sfds = NULL;

  sighup = 1; /* init cache the first time through */
  sigusr1 = 0; /* but don't dump */
  sigusr2 = 0; /* or rescan interfaces */
  signal(SIGUSR1, sig_handler);
  signal(SIGUSR2, sig_handler);
  signal(SIGHUP, sig_handler);

  /* These get allocated here to avoid overflowing the small stack
     on embedded systems. dnamebuff is big enough to hold one
     maximal sixed domain name and gets passed into all the processing
     code. We manage to get away with one buffer. */
  dnamebuff = safe_malloc(MAXDNAME);
  /* Size: we check after adding each record, so there must be 
     memory for the largest packet, and the largest record */
  packet = safe_malloc(PACKETSZ+MAXDNAME+RRFIXEDSZ);
  
  options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
		      &username, &groupname, &domain_suffix, &runfile, 
		      &if_names, &if_addrs, &if_except, &bogus_addr, 
		      &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts);
  
  int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, port);
  
  if (int_err_string)
    die(int_err_string, NULL);
  
  for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
    if (if_tmp->name && !if_tmp->found)
      die("unknown interface %s", if_tmp->name);
  
  for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
    if (!if_tmp->found)
      {
	char addrbuff[ADDRSTRLEN];
#ifdef HAVE_IPV6
	if (if_tmp->addr.sa.sa_family == AF_INET)
	  inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
		    addrbuff, ADDRSTRLEN);
	  else
	    inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
		      addrbuff, ADDRSTRLEN);
#else
	strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
#endif
	die("no interface with address %s", addrbuff);
      }
      
  forward_init(1);

  cache_init(cachesize, options & OPT_LOG);
  
  setbuf(stdout, NULL);

  if (!(options & OPT_DEBUG))
    {
      FILE *pidfile;
      struct passwd *ent_pw;
      int i;
        
      /* The following code "daemonizes" the process. 
	 See Stevens section 12.4 */

#ifndef NO_FORK
      if (fork() != 0 )
	exit(0);
      
      setsid();
      
      if (fork() != 0)
	exit(0);
#endif
      
      chdir("/");
      umask(022); /* make pidfile 0644 */
      
      /* write pidfile _after_ forking ! */
      if (runfile && (pidfile = fopen(runfile, "w")))
      	{
	  fprintf(pidfile, "%d\n", (int) getpid());
	  fclose(pidfile);
	}
      
      umask(0);

      for (i=0; i<64; i++)
	{
	  for (iface = interfaces; iface; iface = iface->next)
	    if (iface->fd == i)
	      break;
	  if (iface)
	    continue;
	  
	  close(i);
	}

      /* Change uid and gid for security */
      if (username && (ent_pw = getpwnam(username)))
	{
	  gid_t dummy;
	  struct group *gp;
	  /* remove all supplimentary groups */
	  setgroups(0, &dummy);
	  /* change group for /etc/ppp/resolv.conf 
	     otherwise get the group for "nobody" */
	  if ((groupname && (gp = getgrnam(groupname))) || 
	      (gp = getgrgid(ent_pw->pw_gid)))
	    setgid(gp->gr_gid); 
	  /* finally drop root */
	  setuid(ent_pw->pw_uid);
	}
    }

  openlog("dnsmasq", 
	  DNSMASQ_LOG_OPT(options & OPT_DEBUG), 
	  DNSMASQ_LOG_FAC(options & OPT_DEBUG));
  
  if (cachesize)
    syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
  else
    syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
  
  if (options & OPT_LOCALMX)
    syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
  else if (mxname)
    syslog(LOG_INFO, "serving MX record for mailhost %s target %s", 
	   mxname, mxtarget);
  
  if (getuid() == 0 || geteuid() == 0)
    syslog(LOG_WARNING, "failed to drop root privs");
  
  servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
  
  while (1)
    {
      int ready, maxfd = 0;
      fd_set rset;
      HEADER *header;
      struct stat statbuf;
   
      if (first_loop)
	/* do init stuff only first time round. */
	{
	  first_loop = 0;
	  ready = 0;
	}
      else
	{
	  FD_ZERO(&rset);

	  for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
	    {
	      FD_SET(serverfdp->fd, &rset);
	      if (serverfdp->fd > maxfd)
		maxfd = serverfdp->fd;
	    }

	  for (iface = interfaces; iface; iface = iface->next)
	    {
	      FD_SET(iface->fd, &rset);
	      if (iface->fd > maxfd)
		maxfd = iface->fd;
	    }
	  
	  ready = select(maxfd+1, &rset, NULL, NULL, NULL);
	  
	  if (ready == -1)
	    {
	      if (errno == EINTR)
		ready = 0; /* do signal handlers */
	      else
		continue;
	    }
	}
      
      if (sighup)
	{
	  signal(SIGHUP, SIG_IGN);
	  cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
	  if (resolv && (options & OPT_NO_POLL))
	    servers = last_server = 
	      check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port), 
			    interfaces, &sfds);
	  sighup = 0;
	  signal(SIGHUP, sig_handler);
	}
      
      if (sigusr1)
	{
	  signal(SIGUSR1, SIG_IGN);
	  dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
	  sigusr1 = 0;
	  signal(SIGUSR1, sig_handler);
	}
      
      if (sigusr2)
	{
	  signal(SIGUSR2, SIG_IGN);
	  if (getuid() != 0 && port <= 1024)
	    syslog(LOG_ERR, "cannot re-scan interfaces unless --user=root");
	  else
	   {
	     syslog(LOG_INFO, "rescanning network interfaces");
	     int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, port);
	     if (int_err_string)
	       syslog(LOG_ERR, int_err_string, strerror(errno));
	   }
	  sigusr2 = 0;
	  signal(SIGUSR2, sig_handler);
	  /* may be new file descriptors now, so redo select() */
	  ready = 0;
	}
      
      now = time(NULL);

      /* Check for changes to resolv files and DHCP leases file once per second max. */
      if (last == 0 || now > last)
	{
	  last = now;
	  if (!(options & OPT_NO_POLL))
	    {
	      struct resolvc *res = resolv, *latest = NULL;
	      time_t last_change = 0;
	      /* There may be more than one possible file. 
		 Go through and find the one which changed _last_.
		 Warn of any which can't be read. */
	      while (res)
		{
		  if (stat(res->name, &statbuf) == -1)
		    {
		      if (!res->logged)
			syslog(LOG_WARNING, "failed to access %s: %m", res->name);
		      res->logged = 1;
		    }
		  else
		    {
		      res->logged = 0;
		      if (statbuf.st_mtime > last_change)
			{
			  last_change = statbuf.st_mtime;
			  latest = res;
			}
		    }
		  res = res->next;
		}
	  
	      if (latest && last_change > resolv_changed)
		{
		  resolv_changed = last_change;
		  servers = last_server = 
		    check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
				  interfaces, &sfds);
		}
	    }

	  if (lease_file)
	    {
syslog(LOG_WARNING, "checking lease file %s", lease_file);
	      if (stat(lease_file, &statbuf) == -1)
		{
		  if (!logged_lease)
		    syslog(LOG_WARNING, "failed to access %s: %m", lease_file);
		  logged_lease = 1;
		}
	      else
		{ 
		  logged_lease = 0;
		  if ((statbuf.st_mtime != lease_file_change) ||
		      (statbuf.st_ino != lease_file_inode))
		    {
		      lease_file_change = statbuf.st_mtime;
		      lease_file_inode = statbuf.st_ino;
		      load_dhcp(lease_file, domain_suffix, now, dnamebuff);
		    }
		}
	    }
	}
		
      if (ready == 0)
	continue; /* no sockets ready */
      
       for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
	 if (FD_ISSET(serverfdp->fd, &rset))
	   last_server = reply_query(serverfdp->fd, options, packet, now, 
				     dnamebuff, last_server, bogus_addr);
      
      for (iface = interfaces; iface; iface = iface->next)
	{
	  if (FD_ISSET(iface->fd, &rset))
	    {
	      /* request packet, deal with query */
	      union mysockaddr udpaddr;
	      socklen_t udplen = sizeof(udpaddr);
	      int m, n = recvfrom(iface->fd, packet, PACKETSZ, 0, &udpaddr.sa, &udplen); 
	      udpaddr.sa.sa_family = iface->addr.sa.sa_family;
#ifdef HAVE_IPV6
	      if (udpaddr.sa.sa_family == AF_INET6)
		udpaddr.in6.sin6_flowinfo = htonl(0);
#endif	      
	      header = (HEADER *)packet;
	      if (n >= (int)sizeof(HEADER) && !header->qr)
		{
		  if (extract_request(header, (unsigned int)n, dnamebuff))
		    {
		      if (udpaddr.sa.sa_family == AF_INET) 
			log_query(F_QUERY | F_IPV4 | F_FORWARD, dnamebuff, 
				  (struct all_addr *)&udpaddr.in.sin_addr);
#ifdef HAVE_IPV6
		      else
			log_query(F_QUERY | F_IPV6 | F_FORWARD, dnamebuff, 
				  (struct all_addr *)&udpaddr.in6.sin6_addr);
#endif
		    }
		  
		  m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, 
				      mxname, mxtarget, options, now, local_ttl, dnamebuff);
		  if (m >= 1)
		    {
		      /* answered from cache, send reply */
		      sendto(iface->fd, (char *)header, m, 0, 
			     &udpaddr.sa, sa_len(&udpaddr));
		    }
		  else 
		    {
		      /* cannot answer from cache, send on to real nameserver */
		      last_server = forward_query(iface->fd, &udpaddr, header, n, 
						  options, dnamebuff, servers, 
						  last_server, now, local_ttl);
		    }
		}
	      
	    }
	}
    }
  
  return 0;
}
if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
	  setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
	  !fix_fd(transfer->sockfd))
	{
	  if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
	    {
	      if (++port <= daemon->end_tftp_port)
		{ 
		  if (listen->family == AF_INET)
		    addr.in.sin_port = htons(port);
#ifdef HAVE_IPV6
		  else
		     addr.in6.sin6_port = htons(port);
#endif
		  continue;
		}
	      my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
	    }
	  free_transfer(transfer);
	  return;
	}