806 lines
19 KiB
C
806 lines
19 KiB
C
/*
|
|
* |-----------------------------------------------------------|
|
|
* | Copyright (c) 1990 MIPS Computer Systems, Inc. |
|
|
* | All Rights Reserved |
|
|
* |-----------------------------------------------------------|
|
|
* | Restricted Rights Legend |
|
|
* | Use, duplication, or disclosure by the Government is |
|
|
* | subject to restrictions as set forth in |
|
|
* | subparagraph (c)(1)(ii) of the Rights in Technical |
|
|
* | Data and Computer Software Clause of DFARS 52.227-7013. |
|
|
* | MIPS Computer Systems, Inc. |
|
|
* | 950 DeGuigne Drive |
|
|
* | Sunnyvale, CA 94086 |
|
|
* |-----------------------------------------------------------|
|
|
*/
|
|
#ident "$Revision: 1.4 $"
|
|
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
/*
|
|
* VIS generic implementation of Internet name-to-address mapping.
|
|
*/
|
|
#include <libc_synonyms.h>
|
|
#include <libnsl_synonyms.h>
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/xti.h>
|
|
#include <netconfig.h>
|
|
#include <netdir.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/utsname.h>
|
|
#include <net/if.h>
|
|
#include <stropts.h>
|
|
#include <sys/ioctl.h>
|
|
#ifdef SYSLOG
|
|
#include <sys/syslog.h>
|
|
#else
|
|
#define LOG_ERR 3
|
|
#endif /* SYSLOG */
|
|
|
|
#ifdef _BUILDING_SVR4_LIBC
|
|
#define t_errno T_ERRNO
|
|
#endif
|
|
|
|
#define MAXALIASES 35
|
|
|
|
static int __ifioctl( int, int , char *, int *);
|
|
static char * _netdir_mergeaddr( struct netconfig *, char *, char *);
|
|
static int __checkresvport( struct netbuf *);
|
|
static int __getbroadcastnets( struct netconfig *, struct in_addr *);
|
|
static int __bindresvport( int , struct netbuf *);
|
|
|
|
extern char *malloc(), *calloc();
|
|
char *_inet_ntoa();
|
|
extern struct in_addr _inet_makeaddr();
|
|
extern int _nderror;
|
|
|
|
static char *localaddr[] = {"\000\000\000\000", NULL};
|
|
|
|
static struct hostent localent = {
|
|
"Localhost",
|
|
NULL,
|
|
AF_INET,
|
|
4,
|
|
localaddr
|
|
};
|
|
#define MAXBCAST 10
|
|
|
|
/*
|
|
* This routine is the "internal" TCP/IP routine that will build a
|
|
* host/service pair into one or more netbufs depending on how many
|
|
* addresses the host has in the host table.
|
|
* If the hostname is HOST_SELF, we return 0.0.0.0 so that the binding
|
|
* can be contacted through all interfaces.
|
|
* If the hostname is HOST_ANY, we return no addresses because IP doesn't
|
|
* know how to specify a service without a host.
|
|
* And finally if we specify HOST_BROADCAST then we ask a tli fd to tell
|
|
* us what the broadcast addresses are for any udp interfaces on this
|
|
* machine.
|
|
*/
|
|
int _netdir_getbyname_count = 0;
|
|
|
|
struct nd_addrlist *
|
|
_netdir_getbyname(tp, serv)
|
|
struct netconfig *tp;
|
|
struct nd_hostserv *serv;
|
|
{
|
|
struct hostent *he;
|
|
struct hostent h_broadcast;
|
|
struct servent *se;
|
|
struct nd_addrlist *result;
|
|
struct netbuf *na;
|
|
char **t;
|
|
struct sockaddr_in *sa;
|
|
int num;
|
|
int server_port;
|
|
char *baddrlist[MAXBCAST + 1];
|
|
struct in_addr inaddrs[MAXBCAST];
|
|
|
|
_netdir_getbyname_count++;
|
|
|
|
if (!serv || !tp) {
|
|
_nderror = ND_BADARG;
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
_nderror = ND_OK; /* assume success */
|
|
|
|
/* NULL is not allowed, that returns no answer */
|
|
if (! (serv->h_host)) {
|
|
_nderror = ND_NOHOST;
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Find the port number for the service. If the service is
|
|
* not found, check for some special cases. These are :
|
|
* rpcbind - The portmapper's address
|
|
* A number - We don't have a name just a number so use it
|
|
*/
|
|
if (_netdir_getbyname_count > 1) {
|
|
se = NULL;
|
|
} else {
|
|
se = (struct servent *)_getservbyname(serv->h_serv,
|
|
(strcmp(tp->nc_proto, NC_TCP) == 0) ? "tcp" : "udp");
|
|
}
|
|
|
|
if (!se) {
|
|
if (strcmp(serv->h_serv, "rpcbind") == 0) {
|
|
server_port = 111; /* Hard coded */
|
|
} else if (strspn(serv->h_serv, "0123456789")
|
|
== strlen(serv->h_serv)) {
|
|
/* Its a port number */
|
|
server_port = atoi(serv->h_serv);
|
|
} else {
|
|
_nderror = ND_NOSERV;
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
} else {
|
|
server_port = se->s_port;
|
|
}
|
|
|
|
if ((strcmp(serv->h_host, HOST_SELF) == 0)) {
|
|
he = &localent;
|
|
} else if ((strcmp(serv->h_host, HOST_BROADCAST) == 0)) {
|
|
int bnets, i;
|
|
|
|
memset((char *)inaddrs, 0, sizeof(struct in_addr) * MAXBCAST);
|
|
bnets = __getbroadcastnets(tp, inaddrs);
|
|
if (bnets == 0) {
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
he = &h_broadcast;
|
|
he->h_name = "broadcast";
|
|
he->h_aliases = NULL;
|
|
he->h_addrtype = AF_INET;
|
|
he->h_length = 4;
|
|
for (i = 0; i < bnets; i++)
|
|
baddrlist[i] = (char *)&inaddrs[i];
|
|
baddrlist[i] = NULL;
|
|
he->h_addr_list = baddrlist;
|
|
} else if (_netdir_getbyname_count > 1) {
|
|
he = NULL;
|
|
} else {
|
|
he = (struct hostent *)_gethostbyname(serv->h_host);
|
|
}
|
|
|
|
if (!he) {
|
|
_nderror = ND_NOHOST;
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
result = (struct nd_addrlist *)(malloc(sizeof(struct nd_addrlist)));
|
|
if (!result) {
|
|
_nderror = ND_NOMEM;
|
|
_netdir_getbyname_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
/* Count the number of addresses we have */
|
|
for (num = 0, t = he->h_addr_list; *t; t++, num++)
|
|
;
|
|
|
|
result->n_cnt = num;
|
|
result->n_addrs = (struct netbuf *)
|
|
(calloc(num, sizeof(struct netbuf)));
|
|
if (!result->n_addrs) {
|
|
_netdir_getbyname_count--;
|
|
_nderror = ND_NOMEM;
|
|
return (NULL);
|
|
}
|
|
|
|
/* build up netbuf structs for all addresses */
|
|
for (na = result->n_addrs, t = he->h_addr_list; *t; t++, na++) {
|
|
sa = (struct sockaddr_in *)calloc(1, sizeof(*sa));
|
|
if (!sa) {
|
|
_netdir_getbyname_count--;
|
|
_nderror = ND_NOMEM;
|
|
return (NULL);
|
|
}
|
|
/* Vendor specific, that is why it's here and hard coded */
|
|
na->maxlen = sizeof(struct sockaddr_in);
|
|
na->len = sizeof(struct sockaddr_in);
|
|
na->buf = (char *)sa;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_port = htons(server_port);
|
|
sa->sin_addr = *((struct in_addr *)(*t));
|
|
}
|
|
|
|
_netdir_getbyname_count--;
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* This routine is the "internal" TCP/IP routine that will build a
|
|
* host/service pair from the netbuf passed. Currently it only
|
|
* allows one answer, it should, in fact allow several.
|
|
*/
|
|
int _netdir_getbyaddr_count = 0;
|
|
|
|
struct nd_hostservlist *
|
|
_netdir_getbyaddr(tp, addr)
|
|
struct netconfig *tp;
|
|
struct netbuf *addr;
|
|
{
|
|
struct sockaddr_in *sa; /* TCP/IP temporaries */
|
|
struct servent *se;
|
|
struct hostent *he;
|
|
struct nd_hostservlist *result; /* Final result */
|
|
struct nd_hostserv *hs; /* Pointer to the array */
|
|
int servs, hosts; /* # of hosts, services */
|
|
char **hn, **sn; /* host, service names */
|
|
int i, j; /* some counters */
|
|
|
|
_netdir_getbyaddr_count++;
|
|
|
|
if (!addr || !tp) {
|
|
_nderror = ND_BADARG;
|
|
_netdir_getbyaddr_count--;
|
|
return (NULL);
|
|
}
|
|
_nderror = ND_OK; /* assume success */
|
|
|
|
/* XXX how much should we trust this ? */
|
|
sa = (struct sockaddr_in *)(addr->buf);
|
|
|
|
/* first determine the host */
|
|
if (_netdir_getbyaddr_count > 1) {
|
|
he = NULL;
|
|
} else {
|
|
he = (struct hostent *)_gethostbyaddr(&(sa->sin_addr.s_addr),
|
|
4, sa->sin_family);
|
|
}
|
|
if (!he) {
|
|
_nderror = ND_NOHOST;
|
|
_netdir_getbyaddr_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
/* Now determine the service */
|
|
if (_netdir_getbyaddr_count > 1) {
|
|
se = NULL;
|
|
} else {
|
|
se = (struct servent *)_getservbyport(sa->sin_port,
|
|
(strcmp(tp->nc_proto, NC_TCP) == 0) ? "tcp" : "udp");
|
|
}
|
|
if (!se) {
|
|
/* It is not a well known service */
|
|
servs = 1;
|
|
}
|
|
|
|
/* now build the result for the client */
|
|
result = (struct nd_hostservlist *)malloc(sizeof(struct nd_hostservlist));
|
|
if (!result) {
|
|
_nderror = ND_NOMEM;
|
|
_netdir_getbyaddr_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* We initialize the counters to 1 rather than zero because
|
|
* we have to count the "official" name as well as the aliases.
|
|
*/
|
|
for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++)
|
|
;
|
|
|
|
if (se)
|
|
for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++)
|
|
;
|
|
|
|
hs = (struct nd_hostserv *)calloc(hosts*servs, sizeof(struct nd_hostserv));
|
|
if (!hs) {
|
|
_nderror = ND_NOMEM;
|
|
free((char *)result);
|
|
_netdir_getbyaddr_count--;
|
|
return (NULL);
|
|
}
|
|
|
|
result->h_cnt = servs * hosts;
|
|
result->h_hostservs = hs;
|
|
|
|
/* Now build the list of answers */
|
|
|
|
for (i = 0, hn = he->h_aliases; i < hosts; i++) {
|
|
sn = se ? se->s_aliases : NULL;
|
|
|
|
for (j = 0; j < servs; j++) {
|
|
if (! i)
|
|
hs->h_host = strdup(he->h_name);
|
|
else
|
|
hs->h_host = strdup(*hn);
|
|
if (! j) {
|
|
if (se) {
|
|
hs->h_serv = strdup(se->s_name);
|
|
} else {
|
|
/* Convert to a number string */
|
|
char stmp[16];
|
|
|
|
(void) sprintf(stmp, "%d", sa->sin_port);
|
|
hs->h_serv = strdup(stmp);
|
|
}
|
|
} else {
|
|
hs->h_serv = strdup(*sn++);
|
|
}
|
|
|
|
if (!(hs->h_host) || !(hs->h_serv)) {
|
|
_nderror = ND_NOMEM;
|
|
free(result->h_hostservs); /* Free memory */
|
|
free((char *)result);
|
|
_netdir_getbyaddr_count--;
|
|
return (NULL);
|
|
}
|
|
hs ++;
|
|
}
|
|
if (i)
|
|
hn++;
|
|
}
|
|
|
|
_netdir_getbyaddr_count--;
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* This internal routine will merge one of those "universal" addresses
|
|
* to the one which will make sense to the remote caller.
|
|
*/
|
|
static char *
|
|
_netdir_mergeaddr(
|
|
struct netconfig *tp, /* the transport provider */
|
|
char *ruaddr,/* remote uaddr of the caller */
|
|
char *uaddr) /* the address */
|
|
{
|
|
static char **addrlist;
|
|
char tmp[32], nettmp[32];
|
|
char *hptr, *netptr, *portptr;
|
|
int i, j;
|
|
struct in_addr inaddr;
|
|
int index = 0, level = 0;
|
|
|
|
if (!uaddr || !ruaddr || !tp) {
|
|
_nderror = ND_BADARG;
|
|
return ((char *)NULL);
|
|
}
|
|
if (strncmp(ruaddr, "0.0.0.0.", strlen("0.0.0.0.")) == 0)
|
|
/* thats me: return the way it is */
|
|
return (strdup(uaddr));
|
|
|
|
if (addrlist == (char **)NULL) {
|
|
struct utsname u;
|
|
struct hostent *he;
|
|
|
|
if (uname(&u) < 0) {
|
|
_nderror = ND_NOHOST;
|
|
return ((char *)NULL);
|
|
}
|
|
he = (struct hostent *)_gethostbyname(u.nodename);
|
|
if (he == NULL) {
|
|
_nderror = ND_NOHOST;
|
|
return ((char *)NULL);
|
|
}
|
|
/* make a list of all the legal host addrs */
|
|
for (i = 0; he->h_addr_list[i]; i++)
|
|
;
|
|
if (i == 0) {
|
|
_nderror = ND_NOHOST;
|
|
return ((char *)NULL);
|
|
}
|
|
addrlist = (char **)malloc(sizeof (char *) * (i + 1));
|
|
if (addrlist == NULL) {
|
|
_nderror = ND_NOMEM;
|
|
return ((char *)NULL);
|
|
}
|
|
addrlist[i] = NULL;
|
|
for (i = 0; he->h_addr_list[i]; i++) {
|
|
inaddr = *((struct in_addr *)he->h_addr_list[i]);
|
|
addrlist[i] = strdup(_inet_ntoa(inaddr));
|
|
}
|
|
}
|
|
|
|
if (addrlist[1] == NULL) {
|
|
/* Only one address. So, dont compare */
|
|
i = 0;
|
|
goto reply;
|
|
}
|
|
/* Get the host part of the remote uaddress assuming h.h.h.h.p.p */
|
|
/* XXX: May be there is a better way of doing all this */
|
|
(void) strcpy(tmp, ruaddr);
|
|
for (hptr = tmp, j = 0; j < 4; j++) {
|
|
hptr = strchr(hptr, '.');
|
|
hptr++;
|
|
}
|
|
*(hptr - sizeof (char)) = NULL;
|
|
|
|
/* class C networks - for gateways the common address is h.h.h */
|
|
/* class B networks - for gateways the common address is h.h */
|
|
/* class A networks - for gateways the common address is h */
|
|
(void) strcpy(nettmp, tmp);
|
|
netptr = strrchr(nettmp, '.');
|
|
*netptr = NULL;
|
|
netptr = nettmp;
|
|
|
|
/* Compare and get the right one */
|
|
for (i = 0; addrlist[i]; i++) {
|
|
char *t1, *t2;
|
|
|
|
if (strcmp(tmp, addrlist[i]) == 0)
|
|
return (strdup(uaddr)); /* the same one */
|
|
if (strncmp(netptr, addrlist[i], strlen(netptr)) == 0) {
|
|
index = i;
|
|
level = 3;
|
|
break; /* A hit */
|
|
}
|
|
t1 = strrchr(netptr, '.');
|
|
*t1 = NULL;
|
|
if (strncmp(netptr, addrlist[i], strlen(netptr)) == 0) {
|
|
index = i;
|
|
level = 2;
|
|
*t1 = '.';
|
|
continue; /* A partial hit */
|
|
}
|
|
t2 = strrchr(netptr, '.');
|
|
*t2 = NULL;
|
|
if (strncmp(netptr, addrlist[i], strlen(netptr)) == 0) {
|
|
if (level < 1) {
|
|
index = i;
|
|
level = 1;
|
|
}
|
|
*t1 = '.';
|
|
*t2 = '.';
|
|
continue; /* A partial hit */
|
|
}
|
|
*t1 = '.';
|
|
*t2 = '.';
|
|
}
|
|
reply:
|
|
/* Get the port number */
|
|
for (portptr = uaddr, j = 0; j < 4; j++) {
|
|
portptr = strchr(portptr, '.');
|
|
portptr++;
|
|
}
|
|
(void) sprintf(tmp, "%s.%s", addrlist[index], portptr);
|
|
return (strdup(tmp));
|
|
}
|
|
|
|
int
|
|
_netdir_options(tp, opts, fd, par)
|
|
struct netconfig *tp;
|
|
int opts;
|
|
int fd;
|
|
char *par;
|
|
{
|
|
struct t_optmgmt *options;
|
|
struct t_optmgmt *optionsret;
|
|
struct nd_mergearg *ma;
|
|
|
|
#ifndef SUNOS
|
|
struct sochdr {
|
|
struct opthdr opthdr;
|
|
long value;
|
|
} sochdr;
|
|
#endif
|
|
|
|
switch (opts) {
|
|
case ND_SET_BROADCAST:
|
|
/* enable for broadcasting */
|
|
#ifndef SUNOS
|
|
options = (struct t_optmgmt *)t_alloc(fd, T_OPTMGMT, 0);
|
|
optionsret = (struct t_optmgmt *)t_alloc(fd, T_OPTMGMT, T_OPT);
|
|
if ((options == NULL) || (optionsret == NULL))
|
|
return (ND_NOMEM);
|
|
sochdr.opthdr.level = SOL_SOCKET;
|
|
sochdr.opthdr.name = SO_BROADCAST;
|
|
sochdr.opthdr.len = 4;
|
|
sochdr.value = 1; /* ok to broadcast */
|
|
options->opt.maxlen = sizeof(sochdr);
|
|
options->opt.len = sizeof(sochdr);
|
|
options->opt.buf = (char *) &sochdr;
|
|
options->flags = T_NEGOTIATE;
|
|
if (t_optmgmt(fd, options, optionsret) == -1) {
|
|
/*
|
|
* maybe shouldn't quit if some this just
|
|
* lets you broadcast
|
|
*/
|
|
}
|
|
options->opt.buf = 0;
|
|
(void) t_free((char *)options, T_OPTMGMT);
|
|
(void) t_free((char *)optionsret, T_OPTMGMT);
|
|
#endif
|
|
return (ND_OK);
|
|
case ND_SET_RESERVEDPORT: /* bind to a resered port */
|
|
return (__bindresvport(fd, (struct netbuf *)par));
|
|
case ND_CHECK_RESERVEDPORT: /* check if reserved prot */
|
|
return (__checkresvport((struct netbuf *)par));
|
|
case ND_MERGEADDR: /* Merge two addresses */
|
|
ma = (struct nd_mergearg *)(par);
|
|
ma->m_uaddr = _netdir_mergeaddr(tp, ma->c_uaddr, ma->s_uaddr);
|
|
return(_nderror);
|
|
default:
|
|
return (ND_NOCTRL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This internal routine will convert a TCP/IP internal format address
|
|
* into a "universal" format address. In our case it prints out the
|
|
* decimal dot equivalent. h1.h2.h3.h4.p1.p2 where h1-h4 are the host
|
|
* address and p1-p2 are the port number.
|
|
*/
|
|
char *
|
|
_taddr2uaddr(tp, addr)
|
|
struct netconfig *tp; /* the transport provider */
|
|
struct netbuf *addr; /* the netbuf struct */
|
|
{
|
|
struct sockaddr_in *sa; /* our internal format */
|
|
char tmp[32];
|
|
unsigned short myport;
|
|
|
|
if (!addr || !tp) {
|
|
_nderror = ND_BADARG;
|
|
return (NULL);
|
|
}
|
|
sa = (struct sockaddr_in *)(addr->buf);
|
|
myport = _ntohs(sa->sin_port);
|
|
(void) sprintf(tmp,"%s.%d.%d", _inet_ntoa(sa->sin_addr),
|
|
myport >> 8, myport & 255);
|
|
return (strdup(tmp)); /* Doesn't return static data ! */
|
|
}
|
|
|
|
/*
|
|
* This internal routine will convert one of those "universal" addresses
|
|
* to the internal format used by the Sun TLI TCP/IP provider.
|
|
*/
|
|
|
|
struct netbuf *
|
|
_uaddr2taddr(tp, addr)
|
|
struct netconfig *tp; /* the transport provider */
|
|
char *addr; /* the address */
|
|
{
|
|
struct sockaddr_in *sa;
|
|
unsigned long inaddr;
|
|
unsigned short inport;
|
|
int h1, h2, h3, h4, p1, p2;
|
|
struct netbuf *result;
|
|
|
|
if (!addr || !tp) {
|
|
_nderror = ND_BADARG;
|
|
return (0);
|
|
}
|
|
result = (struct netbuf *) malloc(sizeof(struct netbuf));
|
|
if (!result) {
|
|
_nderror = ND_NOMEM;
|
|
return (0);
|
|
}
|
|
|
|
sa = (struct sockaddr_in *)calloc(1, sizeof(*sa));
|
|
if (!sa) {
|
|
free((char *)result); /* free previous result */
|
|
_nderror = ND_NOMEM;
|
|
return (0);
|
|
}
|
|
result->buf = (char *)(sa);
|
|
result->maxlen = sizeof(struct sockaddr_in);
|
|
result->len = sizeof(struct sockaddr_in);
|
|
|
|
/* XXX there is probably a better way to do this. */
|
|
sscanf(addr,"%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4, &p1, &p2);
|
|
|
|
/* convert the host address first */
|
|
inaddr = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
|
|
sa->sin_addr.s_addr = _htonl(inaddr);
|
|
|
|
/* convert the port */
|
|
inport = (p1 << 8) + p2;
|
|
sa->sin_port = _htons(inport);
|
|
|
|
sa->sin_family = AF_INET;
|
|
|
|
return (result);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
__ifioctl(
|
|
int s,
|
|
int cmd,
|
|
char *arg,
|
|
int *lenp)
|
|
{
|
|
struct strioctl ioc;
|
|
int error;
|
|
|
|
ioc.ic_cmd = cmd;
|
|
ioc.ic_timout = 0;
|
|
if (lenp)
|
|
ioc.ic_len = *lenp;
|
|
else
|
|
ioc.ic_len = sizeof(struct ifreq);
|
|
ioc.ic_dp = arg;
|
|
error = _ioctl(s, I_STR, (char *) &ioc);
|
|
if (error != -1 &&
|
|
lenp)
|
|
*lenp = ioc.ic_len;
|
|
return(error);
|
|
}
|
|
|
|
|
|
static int
|
|
__checkresvport( struct netbuf *addr)
|
|
{
|
|
struct sockaddr_in *sin;
|
|
|
|
if (addr == NULL) {
|
|
_nderror = ND_FAILCTRL;
|
|
return (-1);
|
|
}
|
|
sin = (struct sockaddr_in *)addr->buf;
|
|
if (sin->sin_port < IPPORT_RESERVED)
|
|
return (0);
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
__getbroadcastnets(
|
|
struct netconfig *tp,
|
|
struct in_addr *addrs)
|
|
{
|
|
struct ifconf ifc;
|
|
struct ifreq ifreq, *ifr;
|
|
struct sockaddr_in *sin;
|
|
int fd;
|
|
int n, i;
|
|
char buf[8800];
|
|
|
|
_nderror = ND_SYSTEM;
|
|
fd = open(tp->nc_device, O_RDONLY);
|
|
if (fd < 0) {
|
|
(void) syslog(LOG_ERR, "broadcast: ioctl (get interface configuration): %m");
|
|
return (0);
|
|
}
|
|
ifc.ifc_len = 8800;
|
|
ifc.ifc_buf = buf;
|
|
/*
|
|
* Ideally, this ioctl should also tell me, how many bytes were
|
|
* finally allocated, but it doesnt.
|
|
*/
|
|
if (__ifioctl(fd, SIOCGIFCONF, ifc.ifc_buf, &ifc.ifc_len) < 0) {
|
|
(void) syslog(LOG_ERR, "broadcast: ioctl (get interface configuration): %m");
|
|
close(fd);
|
|
return (0);
|
|
}
|
|
ifr = (struct ifreq *)buf;
|
|
for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq);
|
|
n > 0 && i < MAXBCAST; n--, ifr++) {
|
|
ifreq = *ifr;
|
|
if (__ifioctl(fd, SIOCGIFFLAGS, (char *)&ifreq, 0) < 0) {
|
|
(void) syslog(LOG_ERR, "broadcast: ioctl (get interface flags): %m");
|
|
continue;
|
|
}
|
|
if ((ifreq.ifr_flags & IFF_BROADCAST) &&
|
|
(ifreq.ifr_flags & IFF_UP) &&
|
|
(ifr->ifr_addr.sa_family == AF_INET)) {
|
|
sin = (struct sockaddr_in *)&ifr->ifr_addr;
|
|
if (__ifioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq, 0) < 0) {
|
|
/* May not work with other implementation */
|
|
addrs[i++] = _inet_makeaddr(_inet_netof(
|
|
sin->sin_addr.s_addr), INADDR_ANY);
|
|
} else {
|
|
addrs[i++] = ((struct sockaddr_in*)
|
|
&ifreq.ifr_addr)->sin_addr;
|
|
}
|
|
}
|
|
}
|
|
close(fd);
|
|
if (i)
|
|
_nderror = ND_OK;
|
|
return (i);
|
|
}
|
|
|
|
static int
|
|
__bindresvport(
|
|
int fd,
|
|
struct netbuf *addr)
|
|
{
|
|
int res;
|
|
static short port;
|
|
struct sockaddr_in myaddr;
|
|
struct sockaddr_in *sin;
|
|
extern int errno;
|
|
#ifndef _BUILDING_SVR4_LIBC
|
|
extern int t_errno;
|
|
#endif
|
|
int i;
|
|
struct t_bind *tbind, *tres;
|
|
|
|
#define STARTPORT 600
|
|
#define ENDPORT (IPPORT_RESERVED - 1)
|
|
#define NPORTS (ENDPORT - STARTPORT + 1)
|
|
|
|
_nderror = ND_SYSTEM;
|
|
if (geteuid()) {
|
|
errno = EACCES;
|
|
return (-1);
|
|
}
|
|
if ((i = t_getstate(fd)) != T_UNBND) {
|
|
if (t_errno == TBADF)
|
|
errno = EBADF;
|
|
if (i != -1)
|
|
errno = EISCONN;
|
|
return (-1);
|
|
}
|
|
if (addr == NULL) {
|
|
sin = &myaddr;
|
|
(void)memset((char *)sin, 0, sizeof (*sin));
|
|
sin->sin_family = AF_INET;
|
|
} else {
|
|
sin = (struct sockaddr_in *)addr->buf;
|
|
if (sin->sin_family != AF_INET) {
|
|
errno = EPFNOSUPPORT;
|
|
return (-1);
|
|
}
|
|
}
|
|
if (port == 0)
|
|
port = (getpid() % NPORTS) + STARTPORT;
|
|
res = -1;
|
|
errno = EADDRINUSE;
|
|
/* Transform sockaddr_in to netbuf */
|
|
tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
|
|
if (tbind == NULL) {
|
|
if (t_errno == TBADF)
|
|
errno = EBADF;
|
|
_nderror = ND_NOMEM;
|
|
return (-1);
|
|
}
|
|
tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
|
|
if (tres == NULL) {
|
|
(void) t_free((char *)tbind, T_BIND);
|
|
_nderror = ND_NOMEM;
|
|
return (-1);
|
|
}
|
|
|
|
tbind->qlen = 0;/* Always 0; user should change if he wants to */
|
|
(void) memcpy(tbind->addr.buf, (char *)sin, (int)tbind->addr.maxlen);
|
|
tbind->addr.len = tbind->addr.maxlen;
|
|
sin = (struct sockaddr_in *)tbind->addr.buf;
|
|
|
|
for (i = 0; i < NPORTS && errno == EADDRINUSE; i++) {
|
|
sin->sin_port = htons(port++);
|
|
if (port > ENDPORT)
|
|
port = STARTPORT;
|
|
res = t_bind(fd, tbind, tres);
|
|
if ((res == 0) && (memcmp(tbind->addr.buf, tres->addr.buf,
|
|
(int)tres->addr.len) == 0))
|
|
break;
|
|
}
|
|
|
|
(void) t_free((char *)tbind, T_BIND);
|
|
(void) t_free((char *)tres, T_BIND);
|
|
if (i != NPORTS) {
|
|
_nderror = ND_OK;
|
|
} else {
|
|
_nderror = ND_FAILCTRL;
|
|
res = 1;
|
|
}
|
|
return (res);
|
|
}
|
|
|