6

I'm learning C by writing a small application that sends a DNS query to a specified server. Here is an example of the network code:

int send_query()
{
    int sockfd;
    struct sockaddr_in server;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            perror("cannot create socket\n");
    }

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(53);
    inet_pton(AF_INET, "8.8.8.8", &(server.sin_addr));

    sendto(sockfd, const void *buffer, size_t length, 0, (struct sockaddr *) &server, sizeof(server));
}

This works fine as the query is succesfully sent, and a reply is recieved. However, by sniffing the traffic with Wireshark I can see the message: Destination unreachable (Port unreachable).

I found out that I can avoid this by calling bind() before sendto():

int send_query()
{
    int sockfd;
    struct sockaddr_in server;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            perror("cannot create socket\n");
    }

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(53);
    inet_pton(AF_INET, "8.8.8.8", &(server.sin_addr));

    if(bind(sockfd, (struct sockaddr *) &server, sizeof(server)) < 0) {
            perror("bind failed\n");
    }

    sendto(sockfd, const void *buffer, size_t length, 0, (struct sockaddr *) &server, sizeof(server));
   }

Now the Destination unreachable (Port unreachable) message is gone, but the application has to be run with root privileges as it will bind to port 53.

Is it possible to modify the code so a random non-privileged source port is used instead?

Problem solved

The problem occured because of a stupid mistake. I had simply commented out recvfrom(). As I was sniffing the traffic while testing the application, I could see the response and error arrving at my computer, and mistakenly confused this as the application was receving. Because I don't know what the hell I'm doing, I started to mess around with bind() etc. and this avalanche of failure started.

For brevity I did not post all the code, but the issue had probably been solved instantly if had did that instead.

user4793972
  • 125
  • 1
  • 2
  • 8
  • Are you sure a random source port is not already used? – Xaqq Apr 17 '15 at 23:55
  • Well, you could do Math random for numbers above 1024 and below the cap, but then you might accidentally get a port that is being used already, so you need to implement logic to deal with that. – Matthew Herbst Apr 17 '15 at 23:56
  • 1
    @MatthewHerbst You could loop while `bind()` fails. However I wouldn't bother. I believe this `Destination unreachable (Port unreachable)` message is harmless. – Xaqq Apr 17 '15 at 23:58
  • Xaqq: If I don't call `bind()` a random source port is used, but then I get the `Destination unreachable (Port unreachable)` message that I would like to avoid. It's like an option for source port is missing in the `sockaddr_in` structure. – user4793972 Apr 18 '15 at 00:00
  • Why? It's part of the normal operation of IP and, in your case, of a successful operation. Don't worry about it. If you don't bind, a bind to port zero occurs on the first `connect(), send()`, or `recv()`. – user207421 Apr 18 '15 at 01:20
  • Is the DNS query response being received by your application? The ICMP error Destination Unreachable means there is no application listening on the destination port of the packet. From what you've mentioned, the response is being sent to port 53 and not the original source port. In the first example, a response sent to 53 would not be received by your application because it is not listening on port 53 – Joel Cunningham Apr 20 '15 at 21:00
  • @EJP If I sniff the traffic while using `dig` or `drill` no errors are seen. Also, it seems wrong to me to see an error message, when my application successfully recieves an answer. – user4793972 Apr 22 '15 at 13:48
  • @Joel Cunningham The DNS query response is successfully received. – user4793972 Apr 22 '15 at 13:48
  • @user4793972 could you please post the receiver code. We need to see if your receive socket is binding to 53 or if it is using a dynamic port – Joel Cunningham Apr 22 '15 at 20:49
  • @Joel Cunningham Thank you for helping, but I have found the solution now, and have updated the post. – user4793972 Apr 26 '15 at 21:24

1 Answers1

2

You can bind to port 0 to let the OS randomly pick one available for you (just like INADDR_ANY is 0). See https://stackoverflow.com/a/1077305/3543692

Also, binding to port 53 makes no sense. The 53 port is the port of the DNS server, not the DNS client. Think if all the DNS clients on your computer use 53 for DNS client port, then only one DNS request to a server can be proceeded concurrently. Typically, all clients (both TCP/UDP) use random unused ports assigned by OS.

Community
  • 1
  • 1
cybcaoyibo
  • 68
  • 6
  • So, how do I set both port 0 and port 53? `bind()` wants a pointer to `struct sockaddr`, remember? The `sin_port` has to be set to 53 or else the query will be sent to a random port on the DNS server, but `bind()` will also try to bind to this port, that's what the whole issue is about. – user4793972 Apr 22 '15 at 13:47
  • 2
    @user4793972: You should pass a sockaddr_in with INADDR_ANY and port 0 to bind, and pass a sockaddr_in with 8.8.8.8 and port 53 to sendto. – cybcaoyibo Apr 22 '15 at 14:00
  • Aha, I had somehow locked my mind on that it would be possible with only one instance of sockaddr_in, but the only way is to use two? – user4793972 Apr 22 '15 at 14:07
  • @user4793972 You can reuse the same one if you must, but they are usually local variables in the function. – user207421 Apr 26 '15 at 21:36