CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2
  1. #1
    Join Date
    Feb 2010
    Posts
    2

    Strange UDP problem - BSD sockets

    This is a problem with BSD sockets on Ubuntu. I hope this is not the wrong place to ask this.

    I have a piece of code that should listen on DNS port 53 of the local machine, and handle DNS queries (responses and replies). If the code gets back a response with 0 answer sections, the code re-issues a query for a different DNS RR type. The code re-issues a query asking for a custom RR type. This packet is seen on the issuing machine's tcpdump, but not on the tcpdump of the machine running the resolver. All packets are sent using UDP.

    Two observations:

    1. If I let my piece of code sleep a second before sending the packet, all's successful! (I've commented out sleep in the code below).
    2. If the application requesting a DNS query requests for the custom RR type (what should have been re-issued by my piece of code), the response is retrieved.

    Please let me know if additional information would be useful

    Thank you for your time,
    Arun Madhavan

    Code:
    Socket creation:
    
      m_udp_fd = socket(PF_INET, SOCK_DGRAM, 0);
      if (m_udp_fd >= 0)
      {
        struct sockaddr_in sin;
    
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = INADDR_ANY;
        sin.sin_port = htons(DNS_PORT);
    
        // allow the socket to re-bind to the same port
        do {
          int ignore = 0;
          if (setsockopt(m_udp_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&ignore,
                sizeof(ignore)) < 0)
          {
            fprintf(stderr, "setsockopt with SO_REUSEADDR failed with error %d\n", GetLastError());
            return false;
          }
        }
        while(0);
    
        // Windows non-blocking is done at create
    #ifndef _MSC_VER
        set_nonblocking(m_udp_fd);
    #endif /* _MSC_VER */
    
        // bind to the port
        if (bind(m_udp_fd, (struct sockaddr *)&sin, sizeof(sin)) == 0)
        {
          eprintf("PANZER: port %d\n", sin.sin_port);  
          select_loop();
          // this can fall through
        }
      }
    
    void Resolver::set_nonblocking(int fd) {
      long flags;
      
      if ((flags = fcntl(fd, F_GETFL)) < 0)
        err(1, "fcntl(F_GETFL)");
      if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
        err(1, "fcntl(F_SETFL)");
    }
    
    
    Entry point of code: 
    
        if ((r = recvfrom(m_udp_fd, (char*)buf, sizeof(buf), 0,
                (struct sockaddr *)&sin, &sz)) > 0)
        {
          DnsPacket *p ;
          p = new DnsPacket(r);
          p->add_bytes(buf, r);
          if (p->parse())
          {
            read_packet(sin, *p);
          }
          else
          {
            delete p;
          }
        }
      }
    
    void Resolver::read_packet(struct sockaddr_in &from, DnsPacket &p)
    {
      DnsHeader& h = p.header();
      // response packet
      if (h.response())
      {
        int id = h.id();
        DnsQuery *q = m_queries.get(id);
        if (q == NULL)
        {
          delete &p;
        }
        else
        {
          q->add_response(p);
          // sleep(1);
          // forward packet
          write_packet(q->dest(), q->packet_to_forward());
    
          if (q->done())
          {
            int id = h.id();
            m_queries.remove(id);
          }
        }
      }
    
      // packet is a question
      else
      {
        // silently drop packets that aren't from localhost
        if (from.sin_addr.s_addr == ntohl(INADDR_LOOPBACK))
        {
          DnsQuery *q = new DnsQuery(from, p);
    
          // add the query to the LRU
          int id = p.header().id();
          m_queries.add(id, *q);
    
          size_t len;
          u_char *b = p.get_bytes(len);
          write_packet(m_tRes, b, len);
        }
        else
        {
          delete &p;
        }
      }
    }
    
    void Resolver::write_packet(struct sockaddr_in &dst, DnsPacket &p)
    {
      size_t size;
      u_char *bytes = p.get_bytes(size);
      write_packet(dst, bytes, size);
    }
    
    void Resolver::write_packet(struct sockaddr_in &dst, u_char *b, size_t len)
    {
      bool sent = false;
      //printf("trying to send to %x:%d\n", dst.sin_addr.s_addr, ntohs(dst.sin_port));
    
      // attempt to write the packet if the queue is empty
      if (m_send_q.empty())
      {
        sent = try_send(dst, b, len);
      }
    
      if (!sent)
      {
        enqueue(dst, b, len);
      }
    }
    
    bool Resolver::try_send(struct sockaddr_in &dst, u_char *b, size_t len)
    {
      SSIZE_T w;
      do
      {
        w = sendto(m_udp_fd, (char*)b, len, 0, (struct sockaddr *)&dst, sizeof(dst));
        if (w > 0)
        {
          eprintf("sent to %x:%d\n", dst.sin_addr.s_addr, ntohs(dst.sin_port));
          delete [] b;
        }
      }
      while (w < 0 && errno == EINTR);
    
      if (w == 0)
      {
        eprintf("socket closed while writing\n");
        exit(1);
      }
      if (w < 0 && errno != EAGAIN)
      {
        int errorNum = GetLastError();
        eprintf("error on socket while writing: %s/%d\n", strerror(errorNum), errorNum);
        exit(1);
      }
    
      // return true only if we wrote the packet
      if (w > 0)
      {
        return true;
      }
      return false;
    }

  2. #2
    Join Date
    Feb 2010
    Posts
    2

    Re: Strange UDP problem - BSD sockets

    I forgot to add: The first time the DNS query is issued, the piece of code forwards it across. The piece of code re-issuing with custom record is where it fails. However, if the first DNS query is for the custom RR type, it works.

    One more thing to note: The DNS packet for the re-issue by the piece of code has been verified with a hex dump, and it seems alright.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured