Netværksprogrammering 5 - Lokale IP adresser

Tags:    c++
Skrevet af Bruger #2695 @ 16.05.2004

Introduktion



Hej igen. Så fandt jeg alligevel et lille emne, som jeg ikke har dækket i de tidligere artikler, og det er et finde ud af de lokale IP adresser.
Jeg har set mange kreative måder at finde IP adresser på, men de fleste brugte kald til eksterne programmer. Én måde et at kalde ipconfig under Windows og ifconfig under Linux og pipe dens output til en fil, som derefter åbnes og parses. Andre sender lige outputtet gennem grep eller awk for at filtrere alt skidtet fra. Alle disse metoder er lige dårlige, for hvis de eksterne programmer ikke er installeret, eller path ikke sat rigtigt op, så virker det bare ikke. Og de programmer kan jo få fat i adressen selv, så hvorfor skulle vi ikke også ?
Det kan vi også, og i denne artikel udbygger vi IPAddress klassen, som vi udviklede i første artikel, så den kan skaffe os vores lokale adresser.
Når jeg siger lokale IP adresser, så mener jeg de adresser, som netkortene er tildelt. Ikke den adresse, som en web server på Internettet ser. De behøver nemlig ikke, at være de samme. Dine netkort har sikkert adresser i det lokale LAN, men når dine data pakker når til din ISP's router eller firewall, så kan adressen blive oversat. Dette kaldes NAT (Network Address Translation), og Google kan fortælle dig en hel masse om, hvordan det virker, hvis du er interesseret.
Derudover vil vi også have fat i maskinens host name.

En IP adresse lister



Vi vil lave et program, som skriver en liste over alle vores IP adresser og host name, og til det vil vi bruge følgende funktioner under Linux:
Fold kodeboks ind/udKode 


...og følgende under Windows:
Fold kodeboks ind/udKode 


ioclt/WSAIoctl funktionen bruges til at læse eller ændre en masse forskellige socket relaterede indstillinger i operativ systemet. Hvilken, det er, specificeres med den anden parameter. For at læse adresselisten bruger vi konstanten SIOCGIFCONF under Linux og SIO_ADDRESS_LIST_QUERY under Windows.
Den første parameter i begge versioner specificerer en socket, som man ønsker oplysninger om. Hvis man vil vide noget om Internet adresser, skal man skaffe listen over adresser, som Internet sockets kan binde til. Derfor skal man oprette en Internet socket først, som vi jo har set på før. De andre parametre specificerer buffere og bufferstørrelser, som informationerne skal lægges i.
Vi prøver et kort eksempel, som lister vores IP adresser.
Fold kodeboks ind/udKode 


Som det ses, er det ikke meget kode, der skal til, og så er det ikke en grim løsning, hvor der bruges eksterne programmer.
Vi prøver at køre det under Linux:
Fold kodeboks ind/udKode 

...og under Windows:
Fold kodeboks ind/udKode 

Under Linux vises loopback adressen 127.0.0.1, men det gør Windows versionen ikke. Min Linux maskine har to netkort (et fysisk og et virtuelt), hvorimod min Windows maskine kun har et enkelt. Alle oplysninger, som operativsystemet har, bliver givet videre til vores program.
Men vi er selvfølgelig ikke tilfredse endnu, for det skal selvfølgelig pakkes ind i IPAddress klassen.

Tilføjelsen til IPAddress



Vi vil tilføje en statisk metode til IPAddress klassen, som returnerer en instans af IPAddress klassen med vores lokale adresser. For at gøre det må vi også kunne oprette tomme adresser, og til det skal vi bruge en ny constructor. Den vil jeg placere i private området, så vi undgår, at tomme adresser gives til vores socket klasser.
IPAddress.h skal altså udbygges med:
Fold kodeboks ind/udKode 


Implementeringen er næsten magen til vores test kode fra før:
Fold kodeboks ind/udKode 


Nemt nok.
Som det ses lægger jeg manuelt loopback adressen 127.0.0.1 (hexadecimalt 0x0100007f) ind i Windows udgaven. Det er for at funktionen virker éns på begge platforme.

En revideret IP adresse lister



Dette bliver nok det korteste program, vi endnu har lavet:
Fold kodeboks ind/udKode 


Og skidtet virker:
Fold kodeboks ind/udKode 

..og også under Windows:
Fold kodeboks ind/udKode 


Programmet gik fra at fylde 111 linjer og indeholde grim arkitektur specifik kode til at fylde 26 linjer og være arkitektur uafhængig. Det kunne vist ikke være meget bedre.

Konklusion



Stol ALDRIG på at du har eksterne programmer at snylte på. Programmet bliver meget bedre, hvis det gøres "rigtigt".
Vi brugte et lidt grimt trick til at skabe ensartet output ved manuelt at indsætte 127.0.0.1 i Windows udgaven af koden, og det er ikke unormal praksis.
Held og lykke med din videre programmering.

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (9)

User
Bruger #2330 @ 17.05.04 00:06
Nå, hvis der er så mange der giver den meget få points, så ville jeg enelig godt høre nogle argumenter. Jeg personligt syntes det er en smaddergod artikel, så jeg syntes ikke den fortjener at få så lav en rating. Anyways, et 5 tal fra mig. Thumbs up, og keep up the good work :D
User
Bruger #2695 @ 17.05.04 14:08
Tak tak for de venlige ord og topkarakteren :-)
Ja et par kommentarer (selv negative) ville da være rart, hvis de er konstruktive. Jeg vil gerne skrive gode artikler og vil gerne vide, hvad jeg gør forkert nu.
Og selvfølgelig gerne idéer til nye artikler :-)
User
Bruger #714 @ 23.05.04 20:13
Op med den, elsker dine artikler Robert, altid så dejlig godt forklaret, hvorfor rater folk den så lavt? Keep up the good work!
User
Bruger #5192 @ 02.06.04 09:37
virkelig god artikel, men du burde stærkt overveje at komme med noget forklaring i dine kode, da det ellers bliver meget tungt og til sin vis uforståeligt for nogle folk, hvilket også kan forkare den lave rating din ellers rigtig gode artikel har fået.
User
Bruger #2695 @ 03.06.04 18:15
Nåda...ok...jeg synes ellers selv, at jeg skriver en hel del kommentarer i koden. Jeg skal prøve at gøre det bedre næste gang :-)
User
Bruger #7900 @ 24.07.05 23:49
Der er en lille bug i sourcen:

int num, i, sock = socket(AF_INET,SOCK_DGRAM,0);
if(socket == 0)
{
throw NetException(strerror(errno));
}

skal være:

int num, i, sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock == -1)
{
throw NetException(strerror(errno));
}

User
Bruger #7900 @ 25.07.05 00:30
Iøvrigt tusind tak for en yderst anvendelig og meget god artikel. Jeg har taget mig den frihed at skrice en ren C udgave. Jeg har testet på Gentoo 2005.0 AMD64, og FC.

######## Ren C ######
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netdb.h>

#define HOSTNAME 0
#define FQDN 1
#define IPADDR 2


char *get_netid(int which)
{
char *hostname = NULL;

if (which == IPADDR) {
struct ifreq my_ifreqs[2];
struct ifconf my_ifconf;
my_ifconf.ifc_len = sizeof(my_ifreqs);
my_ifconf.ifc_req = my_ifreqs;
int sock = socket(AF_INET,SOCK_DGRAM,0);
if (-1 == sock)
goto out;;

if (ioctl(sock,SIOCGIFCONF,&my_ifconf) < 0) {
close(sock);
goto out;;
}
close(sock);

int num = my_ifconf.ifc_len / sizeof(struct ifreq);
if (!num)
goto out;

int i;
struct sockaddr_in *adr = NULL;
for (i = 0; i < num; i++) {
adr = (struct sockaddr_in *)&my_ifreqs.ifr_ifru.ifru_addr;
if (strcmp("127.0.0.1", inet_ntoa((struct in_addr)adr->sin_addr)))
break;
}
adr = (struct sockaddr_in *)&my_ifreqs.ifr_ifru.ifru_addr;
return strdup(inet_ntoa((struct in_addr)adr->sin_addr));
}

if ((which == HOSTNAME) || (which == FQDN)) {
hostname = (char*)malloc(sizeof(char)*HOST_NAME_MAX);
if (!hostname)
goto out;
if (gethostname(hostname, HOST_NAME_MAX))
goto out;
if (errno == EINVAL)
goto out;

if (which == HOSTNAME) {
size_t count = strcspn((const char*)hostname, ".'\\0'");
char *retv = (char*)malloc(sizeof(char)*(count+1));
if (!retv)
goto out;

strncpy(retv, (const char*)hostname, count);
retv[count] = '\\0';
free(hostname);
return retv;
}
}

if (which == FQDN) {
char *fqdn = NULL;
struct addrinfo *result, hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_CANONNAME;
if (!getaddrinfo(hostname, NULL, &hints, &result)) {
fqdn = strdup(result->ai_canonname);
freeaddrinfo(result);
} else
goto out;

return fqdn;
}

out:
free(hostname);
return NULL;
}

int main(int argc, char ** argv)
{
printf("Hostname is: %s\\n", get_netid(HOSTNAME));
printf("FQDN is: %s\\n", get_netid(FQDN));
printf("IP address is: %s\\n", get_netid(IPADDR));

return 0;
}

User
Bruger #7870 @ 30.01.06 19:54
Jeg bruger Dev-c++ og jeg kan ikke få nogen af dine scripts til at virke :(?
User
Bruger #2695 @ 05.05.06 20:11
Hvilke fejl får du Christian ?
Du skal være logget ind for at skrive en kommentar.
t