/* * lib/inet.c This file contains an implementation of the "INET" * support functions for the net-tools. * (NET-3 base distribution). * * Version: $Id: inet.c,v 1.14 2003/10/19 11:57:37 pb Exp $ * * Author: Fred N. van Kempen, * Copyright 1993 MicroWalt Corporation * * Modified: *960113 {1.21} Bernd Eckenfels : rresolve cache bug. *960128 {1.22} Bernd Eckenfels : endian bug in print *960203 {1.23} Bernd Eckenfels : net-features support *960217 {1.24} Bernd Eckenfels : get_sname *960219 {1.25} Bernd Eckenfels : extern int h_errno *960329 {1.26} Bernd Eckenfels : resolve 255.255.255.255 *980101 {1.27} Bernd Eckenfels : resolve raw sockets in /etc/protocols *990302 {1.28} Phil Blundell : add netmask to INET_rresolve *991007 Kurt Garloff : rresolve, resolve: may be hosts * store type (host?) in cache * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or (at * your option) any later version. */ #include "config.h" /* FIXME. Split this file into inet4.c for the IPv4 specific parts and inet.c for those shared between IPv4 and IPv6. */ #if HAVE_AFINET || HAVE_AFINET6 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #include "net-support.h" #include "pathnames.h" #include "intl.h" #include "util.h" extern int h_errno; /* some netdb.h versions don't export this */ /* cache */ struct addr { struct sockaddr_in addr; char *name; int host; struct addr *next; }; struct service { int number; char *name; struct service *next; }; static struct service *tcp_name = NULL, *udp_name = NULL, *raw_name = NULL; #if HAVE_AFINET static struct addr *INET_nn = NULL; /* addr-to-name cache */ static int INET_resolve(char *name, struct sockaddr_storage *sasp, int hostfirst) { struct sockaddr_in *sin = (struct sockaddr_in *)sasp; struct hostent *hp; struct netent *np; /* Grmpf. -FvK */ sin->sin_family = AF_INET; sin->sin_port = 0; /* Default is special, meaning 0.0.0.0. */ if (!strcmp(name, "default")) { sin->sin_addr.s_addr = INADDR_ANY; return (1); } /* Look to see if it's a dotted quad. */ if (inet_aton(name, &sin->sin_addr)) { return 0; } /* If we expect this to be a hostname, try hostname database first */ #ifdef DEBUG if (hostfirst) fprintf (stderr, "gethostbyname (%s)\n", name); #endif if (hostfirst && (hp = gethostbyname(name)) != (struct hostent *) NULL) { memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], sizeof(struct in_addr)); return 0; } /* Try the NETWORKS database to see if this is a known network. */ #ifdef DEBUG fprintf (stderr, "getnetbyname (%s)\n", name); #endif if ((np = getnetbyname(name)) != (struct netent *) NULL) { sin->sin_addr.s_addr = htonl(np->n_net); return 1; } if (hostfirst) { /* Don't try again */ errno = h_errno; return -1; } #ifdef DEBUG res_init(); _res.options |= RES_DEBUG; #endif #ifdef DEBUG fprintf (stderr, "gethostbyname (%s)\n", name); #endif if ((hp = gethostbyname(name)) == (struct hostent *) NULL) { errno = h_errno; return -1; } memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], sizeof(struct in_addr)); return 0; } /* numeric: & 0x8000: default instead of *, * & 0x4000: host instead of net, * & 0x0fff: don't resolve */ static int INET_rresolve(char *name, size_t len, const struct sockaddr_storage *sasp, int numeric, unsigned int netmask) { const struct sockaddr_in *sin = (const struct sockaddr_in *)sasp; struct hostent *ent; struct netent *np; struct addr *pn; u_int32_t ad, host_ad; int host = 0; /* Grmpf. -FvK */ if (sin->sin_family != AF_INET) { #ifdef DEBUG fprintf(stderr, _("rresolve: unsupport address family %d !\n"), sin->sin_family); #endif errno = EAFNOSUPPORT; return (-1); } ad = sin->sin_addr.s_addr; #ifdef DEBUG fprintf (stderr, "rresolve: %08lx, mask %08x, num %08x, len %d\n", ad, netmask, numeric, len); #endif // if no symbolic names are requested we shortcut with ntoa if (numeric & 0x0FFF) { safe_strncpy(name, inet_ntoa(sin->sin_addr), len); return (0); } // we skip getnetbyaddr for 0.0.0.0/0 and 0.0.0.0/~0 if (ad == INADDR_ANY) { if (netmask == INADDR_ANY) { // for 0.0.0.0/0 we hardcode symbolic name if (numeric & 0x8000) safe_strncpy(name, "default", len); else safe_strncpy(name, "*", len); return (0); } else { // for 0.0.0.0/1 we skip getnetbyname() safe_strncpy(name, "0.0.0.0", len); return (0); } } // it is a host address if flagged or any host bits set if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) host = 1; #if 0 INET_nn = NULL; #endif pn = INET_nn; while (pn != NULL) { if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { safe_strncpy(name, pn->name, len); #ifdef DEBUG fprintf (stderr, "rresolve: found %s %08lx in cache (name=%s, len=%d)\n", (host? "host": "net"), ad, name, len); #endif return (0); } pn = pn->next; } host_ad = ntohl(ad); np = NULL; ent = NULL; if (host) { #ifdef DEBUG fprintf (stderr, "gethostbyaddr (%08lx)\n", ad); #endif ent = gethostbyaddr((char *) &ad, 4, AF_INET); if (ent != NULL) safe_strncpy(name, ent->h_name, len); } else { #ifdef DEBUG fprintf (stderr, "getnetbyaddr (%08lx)\n", host_ad); #endif np = getnetbyaddr(host_ad, AF_INET); if (np != NULL) safe_strncpy(name, np->n_name, len); } if ((ent == NULL) && (np == NULL)) safe_strncpy(name, inet_ntoa(sin->sin_addr), len); pn = (struct addr *) xmalloc(sizeof(struct addr)); pn->addr = *sin; pn->next = INET_nn; pn->host = host; pn->name = xstrdup(name); INET_nn = pn; return (0); } static void INET_reserror(const char *text) { herror(text); } /* Display an Internet socket address. */ static const char *INET_print(const char *ptr) { static char name[INET_ADDRSTRLEN + 1]; socklen_t len = sizeof(name) - 1; name[len] = '\0'; inet_ntop(AF_INET, ptr, name, len); return name; } /* Display an Internet socket address. */ static const char *INET_sprint(const struct sockaddr_storage *sasp, int numeric) { static char buff[128]; if (sasp->ss_family == 0xFFFF || sasp->ss_family == 0) return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff)); if (INET_rresolve(buff, sizeof(buff), sasp, numeric, 0xffffff00) != 0) return (NULL); return (buff); } char *INET_sprintmask(const struct sockaddr_storage *sasp, int numeric, unsigned int netmask) { static char buff[128]; if (sasp->ss_family == 0xFFFF || sasp->ss_family == 0) return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff)); if (INET_rresolve(buff, sizeof(buff), sasp, numeric, netmask) != 0) return (NULL); return (buff); } static int INET_getsock(char *bufp, struct sockaddr_storage *sasp) { char *sp = bufp, *bp; unsigned int i; unsigned val; struct sockaddr_in *sin = (struct sockaddr_in *)sasp; sin->sin_family = AF_INET; sin->sin_port = 0; val = 0; bp = (char *) &val; for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) { *sp = toupper(*sp); if ((*sp >= 'A') && (*sp <= 'F')) bp[i] |= (int) (*sp - 'A') + 10; else if ((*sp >= '0') && (*sp <= '9')) bp[i] |= (int) (*sp - '0'); else return (-1); bp[i] <<= 4; sp++; *sp = toupper(*sp); if ((*sp >= 'A') && (*sp <= 'F')) bp[i] |= (int) (*sp - 'A') + 10; else if ((*sp >= '0') && (*sp <= '9')) bp[i] |= (int) (*sp - '0'); else return (-1); sp++; } sin->sin_addr.s_addr = htonl(val); return (sp - bufp); } static int INET_input(int type, char *bufp, struct sockaddr_storage *sasp) { switch (type) { case 1: return INET_getsock(bufp, sasp); case 256: return INET_resolve(bufp, sasp, 1); default: return INET_resolve(bufp, sasp, 0); } } static int INET_getnetmask(char *adr, struct sockaddr_storage *m, char *name) { struct sockaddr_in *mask = (struct sockaddr_in *) m; char *slash, *end; int prefix; if ((slash = strchr(adr, '/')) == NULL) return 0; *slash++ = '\0'; prefix = strtoul(slash, &end, 0); if (*end != '\0') return -1; if (name) { sprintf(name, "/%d", prefix); } mask->sin_family = AF_INET; mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix)); return 1; } struct aftype inet_aftype = { "inet", NULL, /*"DARPA Internet", */ AF_INET, sizeof(unsigned long), INET_print, INET_sprint, INET_input, INET_reserror, NULL /*INET_rprint */ , NULL /*INET_rinput */ , INET_getnetmask, -1, NULL }; #endif /* HAVE_AFINET */ static void add2list(struct service **namebase, struct service *item) { if (*namebase == NULL) { *namebase = item; item->next = NULL; } else { item->next = *namebase; *namebase = item; } } static struct service *searchlist(struct service *servicebase, int number) { struct service *item; for (item = servicebase; item != NULL; item = item->next) { if (item->number == number) return (item); } return (NULL); } static int read_services(void) { struct servent *se; struct protoent *pe; struct service *item; setservent(1); while ((se = getservent())) { /* Allocate a service entry. */ item = (struct service *) xmalloc(sizeof(struct service)); item->name = xstrdup(se->s_name); item->number = se->s_port; /* Fill it in. */ if (!strcmp(se->s_proto, "tcp")) { add2list(&tcp_name, item); } else if (!strcmp(se->s_proto, "udp")) { add2list(&udp_name, item); } else if (!strcmp(se->s_proto, "raw")) { add2list(&raw_name, item); } else { /* sctp, ddp, dccp */ free(item->name); free(item); } } endservent(); setprotoent(1); while ((pe = getprotoent())) { /* Allocate a service entry. */ item = (struct service *) xmalloc(sizeof(struct service)); item->name = xstrdup(pe->p_name); item->number = htons(pe->p_proto); add2list(&raw_name, item); } endprotoent(); return (0); } const char *get_sname(int socknumber, const char *proto, int numeric) { static char buffer[64], init = 0; struct service *item; if (socknumber == 0) return ("*"); if (numeric) goto do_ntohs; if (!init) { (void) read_services(); init = 1; } buffer[0] = '\0'; if (!strcmp(proto, "tcp")) item = searchlist(tcp_name, socknumber); else if (!strcmp(proto, "udp")) item = searchlist(udp_name, socknumber); else if (!strcmp(proto, "raw")) item = searchlist(raw_name, socknumber); else item = NULL; if (item) { strncpy(buffer, item->name, sizeof(buffer)); buffer[sizeof(buffer) - 1] = '\0'; } if (!buffer[0]) { do_ntohs: sprintf(buffer, "%d", ntohs(socknumber)); } return (buffer); } #endif /* HAVE_AFINET || HAVE_AFINET6 */