mirror of
https://github.com/ecki/net-tools.git
synced 2025-04-12 02:49:32 +00:00
Not all sockaddr structs have the same alignment. Instead, it depends on the fields contained in it. The way net-tools has written things though, it accepts sockaddr* everywhere which has 16bit alignment, even though it will cast it to other sockaddr types that have higher alignment. For example, `route` can crash on alpha because it declares sockaddr on the stack, but then casts it up to sockaddr_in6 (which has 32bits). It's also bad storage wise as we might try to cast the sockaddr to a type that is larger than sockaddr which means clobbering the stack. Instead, lets rewrite all the APIs to take a sockaddr_storage. This is guaranteed to have both the maximum alignment and size requirements for all other sockaddr types. Now we can safely cast that pointer to any other sockaddr type and not worry about it. It also has the nice effect of deleting a lot of casts in a lot of places when we only need the type of family. The vast majority of changes here are mechanical. There are a few places where we have to memcpy between a dedicated sockaddr_storage and a smaller struct because we're using an external embedded type (like arpreq). URL: https://bugs.gentoo.org/558436
466 lines
11 KiB
C
466 lines
11 KiB
C
/*
|
|
* 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, <waltje@uwalt.nl.mugnet.org>
|
|
* 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
|
|
* <garloff@suse.de> 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 <netinet/in.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <arpa/nameser.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <resolv.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#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 */
|