minor fix to return E_USAGE on -V instead of exit(0);
[oss-qm-packages.git] / lib / inet.c
blobae906649e75ec04f25ececa12afa48615066e0bc
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).
6 * Version: $Id: inet.c,v 1.13 1999/12/11 13:35:56 freitag Exp $
8 * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
9 * Copyright 1993 MicroWalt Corporation
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
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.
29 #include "config.h"
31 /* FIXME. Split this file into inet4.c for the IPv4 specific parts
32 and inet.c for those shared between IPv4 and IPv6. */
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"
54 extern int h_errno; /* some netdb.h versions don't export this */
56 /* cache */
57 struct addr {
58 struct sockaddr_in addr;
59 char *name;
60 int host;
61 struct addr *next;
64 struct service {
65 int number;
66 char *name;
67 struct service *next;
70 static struct service *tcp_name = NULL, *udp_name = NULL, *raw_name = NULL;
72 #if HAVE_AFINET
74 static struct addr *INET_nn = NULL; /* addr-to-name cache */
77 static int INET_resolve(char *name, struct sockaddr_in *sin, int hostfirst)
79 struct hostent *hp;
80 struct netent *np;
82 /* Grmpf. -FvK */
83 sin->sin_family = AF_INET;
84 sin->sin_port = 0;
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);
91 /* Look to see if it's a dotted quad. */
92 if (inet_aton(name, &sin->sin_addr)) {
93 return 0;
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;
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;
113 if (hostfirst) {
114 /* Don't try again */
115 errno = h_errno;
116 return -1;
118 #ifdef DEBUG
119 res_init();
120 _res.options |= RES_DEBUG;
121 #endif
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;
130 memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0],
131 sizeof(struct in_addr));
133 return 0;
137 /* numeric: & 0x8000: default instead of *,
138 * & 0x4000: host instead of net,
139 * & 0x0fff: don't resolve
141 static int INET_rresolve(char *name, size_t len, struct sockaddr_in *sin,
142 int numeric, unsigned int netmask)
144 struct hostent *ent;
145 struct netent *np;
146 struct addr *pn;
147 unsigned long ad, host_ad;
148 int host = 0;
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);
158 ad = (unsigned long) 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);
171 if (numeric & 0x0FFF) {
172 safe_strncpy(name, inet_ntoa(sin->sin_addr), len);
173 return (0);
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);
190 pn = pn->next;
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);
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;
221 return (0);
225 static void INET_reserror(char *text)
227 herror(text);
231 /* Display an Internet socket address. */
232 static char *INET_print(unsigned char *ptr)
234 return (inet_ntoa((*(struct in_addr *) ptr)));
238 /* Display an Internet socket address. */
239 static char *INET_sprint(struct sockaddr *sap, int numeric)
241 static char buff[128];
243 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
244 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
246 if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
247 numeric, 0xffffff00) != 0)
248 return (NULL);
250 return (buff);
253 char *INET_sprintmask(struct sockaddr *sap, int numeric,
254 unsigned int netmask)
256 static char buff[128];
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);
267 static int INET_getsock(char *bufp, struct sockaddr *sap)
269 char *sp = bufp, *bp;
270 unsigned int i;
271 unsigned val;
272 struct sockaddr_in *sin;
274 sin = (struct sockaddr_in *) sap;
275 sin->sin_family = AF_INET;
276 sin->sin_port = 0;
278 val = 0;
279 bp = (char *) &val;
280 for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) {
281 *sp = toupper(*sp);
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);
290 bp[i] <<= 4;
291 sp++;
292 *sp = toupper(*sp);
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);
301 sp++;
303 sin->sin_addr.s_addr = htonl(val);
305 return (sp - bufp);
308 static int INET_input(int type, char *bufp, struct sockaddr *sap)
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));
320 static int INET_getnetmask(char *adr, struct sockaddr *m, char *name)
322 struct sockaddr_in *mask = (struct sockaddr_in *) m;
323 char *slash, *end;
324 int prefix;
326 if ((slash = strchr(adr, '/')) == NULL)
327 return 0;
329 *slash++ = '\0';
330 prefix = strtoul(slash, &end, 0);
331 if (*end != '\0')
332 return -1;
334 if (name) {
335 sprintf(name, "/%d", prefix);
337 mask->sin_family = AF_INET;
338 mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix));
339 return 1;
343 struct aftype inet_aftype =
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,
350 NULL
353 #endif /* HAVE_AFINET */
355 static void add2list(struct service **namebase, struct service *item)
357 if (*namebase == NULL) {
358 *namebase = item;
359 item->next = NULL;
360 } else {
361 item->next = *namebase;
362 *namebase = item;
367 static struct service *searchlist(struct service *servicebase, int number)
369 struct service *item;
371 for (item = servicebase; item != NULL; item = item->next) {
372 if (item->number == number)
373 return (item);
375 return (NULL);
379 static int read_services(void)
381 struct servent *se;
382 struct protoent *pe;
383 struct service *item;
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;
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);
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);
414 endprotoent();
415 return (0);
419 char *get_sname(int socknumber, char *proto, int numeric)
421 static char buffer[64], init = 0;
422 struct service *item;
424 if (socknumber == 0)
425 return ("*");
426 if (numeric) {
427 sprintf(buffer, "%d", ntohs(socknumber));
428 return (buffer);
430 if (!init) {
431 (void) read_services();
432 init = 1;
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);
446 if (!buffer[0])
447 sprintf(buffer, "%d", ntohs(socknumber));
448 return (buffer);
451 #endif /* HAVE_AFINET || HAVE_AFINET6 */