enable -fno-strict-aliasing until the code base gets a hefty clean up to fix all...
[platform/upstream/net-tools.git] / lib / inet.c
1 /*
2  * lib/inet.c This file contains an implementation of the "INET"
3  *              support functions for the net-tools.
4  *              (NET-3 base distribution).
5  *
6  * Version:    $Id: inet.c,v 1.14 2003/10/19 11:57:37 pb Exp $
7  *
8  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
9  *              Copyright 1993 MicroWalt Corporation
10  *
11  * Modified:
12  *960113 {1.21} Bernd Eckenfels :       rresolve cache bug.
13  *960128 {1.22} Bernd Eckenfels :       endian bug in print
14  *960203 {1.23} Bernd Eckenfels :       net-features support
15  *960217 {1.24} Bernd Eckenfels :       get_sname
16  *960219 {1.25} Bernd Eckenfels :       extern int h_errno
17  *960329 {1.26} Bernd Eckenfels :       resolve 255.255.255.255 
18  *980101 {1.27} Bernd Eckenfels :       resolve raw sockets in /etc/protocols
19  *990302 {1.28} Phil Blundell   :       add netmask to INET_rresolve
20  *991007        Kurt Garloff    :       rresolve, resolve: may be hosts
21  *              <garloff@suse.de>       store type (host?) in cache 
22  *
23  *              This program is free software; you can redistribute it
24  *              and/or  modify it under  the terms of  the GNU General
25  *              Public  License as  published  by  the  Free  Software
26  *              Foundation;  either  version 2 of the License, or  (at
27  *              your option) any later version.
28  */
29 #include "config.h"
30
31 /* FIXME.  Split this file into inet4.c for the IPv4 specific parts
32    and inet.c for those shared between IPv4 and IPv6.  */
33
34 #if HAVE_AFINET || HAVE_AFINET6
35 #include <netinet/in.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <arpa/inet.h>
39 #include <arpa/nameser.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #include <resolv.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include "version.h"
49 #include "net-support.h"
50 #include "pathnames.h"
51 #include "intl.h"
52 #include "util.h"
53
54 extern int h_errno;             /* some netdb.h versions don't export this */
55
56 /* cache */
57 struct addr {
58     struct sockaddr_in addr;
59     char *name;
60     int host;
61     struct addr *next;
62 };
63
64 struct service {
65     int number;
66     char *name;
67     struct service *next;
68 };
69
70 static struct service *tcp_name = NULL, *udp_name = NULL, *raw_name = NULL;
71
72 #if HAVE_AFINET
73
74 static struct addr *INET_nn = NULL;     /* addr-to-name cache           */
75
76
77 static int INET_resolve(char *name, struct sockaddr_in *sin, int hostfirst)
78 {
79     struct hostent *hp;
80     struct netent *np;
81
82     /* Grmpf. -FvK */
83     sin->sin_family = AF_INET;
84     sin->sin_port = 0;
85
86     /* Default is special, meaning 0.0.0.0. */
87     if (!strcmp(name, "default")) {
88         sin->sin_addr.s_addr = INADDR_ANY;
89         return (1);
90     }
91     /* Look to see if it's a dotted quad. */
92     if (inet_aton(name, &sin->sin_addr)) {
93         return 0;
94     }
95     /* If we expect this to be a hostname, try hostname database first */
96 #ifdef DEBUG
97     if (hostfirst) fprintf (stderr, "gethostbyname (%s)\n", name);
98 #endif
99     if (hostfirst && 
100         (hp = gethostbyname(name)) != (struct hostent *) NULL) {
101         memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], 
102                 sizeof(struct in_addr));
103         return 0;
104     }
105     /* Try the NETWORKS database to see if this is a known network. */
106 #ifdef DEBUG
107     fprintf (stderr, "getnetbyname (%s)\n", name);
108 #endif
109     if ((np = getnetbyname(name)) != (struct netent *) NULL) {
110         sin->sin_addr.s_addr = htonl(np->n_net);
111         return 1;
112     }
113     if (hostfirst) {
114         /* Don't try again */
115         errno = h_errno;
116         return -1;
117     }
118 #ifdef DEBUG
119     res_init();
120     _res.options |= RES_DEBUG;
121 #endif
122
123 #ifdef DEBUG
124     fprintf (stderr, "gethostbyname (%s)\n", name);
125 #endif
126     if ((hp = gethostbyname(name)) == (struct hostent *) NULL) {
127         errno = h_errno;
128         return -1;
129     }
130     memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], 
131            sizeof(struct in_addr));
132
133     return 0;
134 }
135
136
137 /* numeric: & 0x8000: default instead of *, 
138  *          & 0x4000: host instead of net, 
139  *          & 0x0fff: don't resolve
140  */
141 static int INET_rresolve(char *name, size_t len, struct sockaddr_in *sin, 
142                          int numeric, unsigned int netmask)
143 {
144     struct hostent *ent;
145     struct netent *np;
146     struct addr *pn;
147     u_int32_t ad, host_ad;
148     int host = 0;
149
150     /* Grmpf. -FvK */
151     if (sin->sin_family != AF_INET) {
152 #ifdef DEBUG
153         fprintf(stderr, _("rresolve: unsupport address family %d !\n"), sin->sin_family);
154 #endif
155         errno = EAFNOSUPPORT;
156         return (-1);
157     }
158     ad = sin->sin_addr.s_addr;
159 #ifdef DEBUG
160     fprintf (stderr, "rresolve: %08lx, mask %08x, num %08x \n", ad, netmask, numeric);
161 #endif
162     if (ad == INADDR_ANY) {
163         if ((numeric & 0x0FFF) == 0) {
164             if (numeric & 0x8000)
165                 safe_strncpy(name, "default", len);
166             else
167                 safe_strncpy(name, "*", len);
168             return (0);
169         }
170     }
171     if (numeric & 0x0FFF) {
172         safe_strncpy(name, inet_ntoa(sin->sin_addr), len);
173         return (0);
174     }
175
176     if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
177         host = 1;
178 #if 0
179     INET_nn = NULL;
180 #endif
181     pn = INET_nn;
182     while (pn != NULL) {
183         if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
184             safe_strncpy(name, pn->name, len);
185 #ifdef DEBUG
186             fprintf (stderr, "rresolve: found %s %08lx in cache\n", (host? "host": "net"), ad);
187 #endif
188             return (0);
189         }
190         pn = pn->next;
191     }
192
193     host_ad = ntohl(ad);
194     np = NULL;
195     ent = NULL;
196     if (host) {
197 #ifdef DEBUG
198         fprintf (stderr, "gethostbyaddr (%08lx)\n", ad);
199 #endif
200         ent = gethostbyaddr((char *) &ad, 4, AF_INET);
201         if (ent != NULL)
202             safe_strncpy(name, ent->h_name, len);
203     } else {
204 #ifdef DEBUG
205         fprintf (stderr, "getnetbyaddr (%08lx)\n", host_ad);
206 #endif
207         np = getnetbyaddr(host_ad, AF_INET);
208         if (np != NULL)
209             safe_strncpy(name, np->n_name, len);
210     }
211     if ((ent == NULL) && (np == NULL))
212         safe_strncpy(name, inet_ntoa(sin->sin_addr), len);
213     pn = (struct addr *) malloc(sizeof(struct addr));
214     pn->addr = *sin;
215     pn->next = INET_nn;
216     pn->host = host;
217     pn->name = (char *) malloc(strlen(name) + 1);
218     strcpy(pn->name, name);
219     INET_nn = pn;
220
221     return (0);
222 }
223
224
225 static void INET_reserror(char *text)
226 {
227     herror(text);
228 }
229
230
231 /* Display an Internet socket address. */
232 static char *INET_print(unsigned char *ptr)
233 {
234     return (inet_ntoa((*(struct in_addr *) ptr)));
235 }
236
237
238 /* Display an Internet socket address. */
239 static char *INET_sprint(struct sockaddr *sap, int numeric)
240 {
241     static char buff[128];
242
243     if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
244         return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
245
246     if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, 
247                       numeric, 0xffffff00) != 0)
248         return (NULL);
249
250     return (buff);
251 }
252
253 char *INET_sprintmask(struct sockaddr *sap, int numeric, 
254                       unsigned int netmask)
255 {
256     static char buff[128];
257
258     if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
259         return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
260     if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, 
261                       numeric, netmask) != 0)
262         return (NULL);
263     return (buff);
264 }
265
266
267 static int INET_getsock(char *bufp, struct sockaddr *sap)
268 {
269     char *sp = bufp, *bp;
270     unsigned int i;
271     unsigned val;
272     struct sockaddr_in *sin;
273
274     sin = (struct sockaddr_in *) sap;
275     sin->sin_family = AF_INET;
276     sin->sin_port = 0;
277
278     val = 0;
279     bp = (char *) &val;
280     for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) {
281         *sp = toupper(*sp);
282
283         if ((*sp >= 'A') && (*sp <= 'F'))
284             bp[i] |= (int) (*sp - 'A') + 10;
285         else if ((*sp >= '0') && (*sp <= '9'))
286             bp[i] |= (int) (*sp - '0');
287         else
288             return (-1);
289
290         bp[i] <<= 4;
291         sp++;
292         *sp = toupper(*sp);
293
294         if ((*sp >= 'A') && (*sp <= 'F'))
295             bp[i] |= (int) (*sp - 'A') + 10;
296         else if ((*sp >= '0') && (*sp <= '9'))
297             bp[i] |= (int) (*sp - '0');
298         else
299             return (-1);
300
301         sp++;
302     }
303     sin->sin_addr.s_addr = htonl(val);
304
305     return (sp - bufp);
306 }
307
308 static int INET_input(int type, char *bufp, struct sockaddr *sap)
309 {
310     switch (type) {
311     case 1:
312         return (INET_getsock(bufp, sap));
313     case 256:
314         return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
315     default:
316         return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
317     }
318 }
319
320 static int INET_getnetmask(char *adr, struct sockaddr *m, char *name)
321 {
322     struct sockaddr_in *mask = (struct sockaddr_in *) m;
323     char *slash, *end;
324     int prefix;
325
326     if ((slash = strchr(adr, '/')) == NULL)
327         return 0;
328
329     *slash++ = '\0';
330     prefix = strtoul(slash, &end, 0);
331     if (*end != '\0')
332         return -1;
333
334     if (name) {
335         sprintf(name, "/%d", prefix);
336     }
337     mask->sin_family = AF_INET;
338     mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix));
339     return 1;
340 }
341
342
343 struct aftype inet_aftype =
344 {
345     "inet", NULL, /*"DARPA Internet", */ AF_INET, sizeof(unsigned long),
346     INET_print, INET_sprint, INET_input, INET_reserror,
347     NULL /*INET_rprint */ , NULL /*INET_rinput */ ,
348     INET_getnetmask,
349     -1,
350     NULL
351 };
352
353 #endif                          /* HAVE_AFINET */
354
355 static void add2list(struct service **namebase, struct service *item)
356 {
357     if (*namebase == NULL) {
358         *namebase = item;
359         item->next = NULL;
360     } else {
361         item->next = *namebase;
362         *namebase = item;
363     }
364 }
365
366
367 static struct service *searchlist(struct service *servicebase, int number)
368 {
369     struct service *item;
370
371     for (item = servicebase; item != NULL; item = item->next) {
372         if (item->number == number)
373             return (item);
374     }
375     return (NULL);
376 }
377
378
379 static int read_services(void)
380 {
381     struct servent *se;
382     struct protoent *pe;
383     struct service *item;
384
385     setservent(1);
386     while ((se = getservent())) {
387         /* Allocate a service entry. */
388         item = (struct service *) malloc(sizeof(struct service));
389         if (item == NULL)
390             perror("netstat");
391         item->name = strdup(se->s_name);
392         item->number = se->s_port;
393
394         /* Fill it in. */
395         if (!strcmp(se->s_proto, "tcp")) {
396             add2list(&tcp_name, item);
397         } else if (!strcmp(se->s_proto, "udp")) {
398             add2list(&udp_name, item);
399         } else if (!strcmp(se->s_proto, "raw")) {
400             add2list(&raw_name, item);
401         }
402     }
403     endservent();
404     setprotoent(1);
405     while ((pe = getprotoent())) {
406         /* Allocate a service entry. */
407         item = (struct service *) malloc(sizeof(struct service));
408         if (item == NULL)
409             perror("netstat");
410         item->name = strdup(pe->p_name);
411         item->number = htons(pe->p_proto);
412         add2list(&raw_name, item);
413     }
414     endprotoent();
415     return (0);
416 }
417
418
419 char *get_sname(int socknumber, char *proto, int numeric)
420 {
421     static char buffer[64], init = 0;
422     struct service *item;
423
424     if (socknumber == 0)
425         return ("*");
426     if (numeric) {
427         sprintf(buffer, "%d", ntohs(socknumber));
428         return (buffer);
429     }
430     if (!init) {
431         (void) read_services();
432         init = 1;
433     }
434     buffer[0] = '\0';
435     if (!strcmp(proto, "tcp")) {
436         if ((item = searchlist(tcp_name, socknumber)) != NULL)
437             sprintf(buffer, "%s", item->name);
438     } else if (!strcmp(proto, "udp")) {
439         if ((item = searchlist(udp_name, socknumber)) != NULL)
440             sprintf(buffer, "%s", item->name);
441     } else if (!strcmp(proto, "raw")) {
442         if ((item = searchlist(raw_name, socknumber)) != NULL)
443             sprintf(buffer, "%s", item->name);
444
445     }
446     if (!buffer[0])
447         sprintf(buffer, "%d", ntohs(socknumber));
448     return (buffer);
449 }
450
451 #endif                          /* HAVE_AFINET || HAVE_AFINET6 */