2015-04-17 13 views
6

Sto imparando C scrivendo una piccola applicazione che invia una query DNS a un server specificato. Ecco un esempio di codice di rete:Porta di origine e di destinazione per il socket UDP?

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)); 
} 

Questo funziona bene come la query viene inviata con successo, e una risposta è ricevuto. Tuttavia, annusando il traffico con Wireshark posso vedere il messaggio: Destination unreachable (Port unreachable).

ho scoperto che posso evitare questo chiamando bind() prima 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)); 
    } 

Ora il messaggio Destination unreachable (Port unreachable) è andato, ma l'applicazione deve essere eseguito con i privilegi di root in quanto si legherà alla porta 53.

È possibile modificare il codice in modo da utilizzare una porta di origine casuale non privilegiata?

Problema risolto

Il problema si è verificato a causa di un errore stupido. Ho semplicemente commentato recvfrom(). Mentre stavo annusando il traffico durante il test dell'applicazione, ho potuto vedere la risposta e l'errore verificatosi sul mio computer e ho confuso erroneamente questo dato che l'applicazione stava recuperando. Perché non so cosa diavolo sto facendo, ho iniziato a scherzare con bind() ecc. E questa valanga di insuccessi è iniziata.

Per brevità non ho pubblicato tutto il codice, ma il problema era stato probabilmente risolto immediatamente se lo avessi fatto.

+0

Sei sicuro che una porta di origine casuale non sia già utilizzata? – Xaqq

+0

Bene, potresti eseguire Math in modo casuale per i numeri superiori a 1024 e al di sotto del limite, ma potresti accidentalmente ottenere una porta che è già in uso, quindi è necessario implementare la logica per gestirla. –

+1

@MatthewHerbst Si potrebbe fare un ciclo mentre 'bind()' fallisce. Comunque non mi preoccuperei. Credo che questo messaggio "Destinazione irraggiungibile (Port non raggiungibile)" sia innocuo. – Xaqq

risposta

2

È possibile eseguire il binding sulla porta 0 per consentire al sistema operativo di selezionarne a caso uno disponibile (proprio come INADDR_ANY è 0). Vedi https://stackoverflow.com/a/1077305/3543692

Inoltre, il binding alla porta 53 non ha senso. La porta 53 è la porta del server DNS, non il client DNS. Pensa se tutti i client DNS sul tuo computer utilizzano 53 per la porta client DNS, quindi è possibile procedere simultaneamente a una sola richiesta DNS su un server. In genere, tutti i client (entrambi TCP/UDP) utilizzano porte inutilizzate casuali assegnate dal sistema operativo.

+0

Quindi, come posso impostare sia la porta 0 che la porta 53? 'bind()' vuole un puntatore a 'struct sockaddr', ricordi? Il parametro 'sin_port' deve essere impostato su 53, altrimenti la query verrà inviata a una porta casuale sul server DNS, ma' bind() 'tenterà anche di collegarsi a questa porta, ecco di cosa tratta l'intero problema. – user4793972

+2

@ user4793972: È necessario passare un sockaddr_in con INADDR_ANY e la porta 0 a bind e passare un sockaddr_in con 8.8.8.8 e la porta 53 a sendto. – cybcaoyibo

+0

Aha, in qualche modo avevo bloccato la mia mente sul fatto che sarebbe stato possibile con una sola istanza di sockaddr_in, ma l'unico modo è usare due? – user4793972