#include <sys_defs.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#ifdef __APPLE_OS_X_SERVER__
#include <arpa/nameser_compat.h>
#endif
#ifdef RESOLVE_H_NEEDS_STDIO_H
#include <stdio.h>
#endif
#ifndef __APPLE_OS_X_SERVER__
#include <resolv.h>
#endif
#include <stdarg.h>
#include <unistd.h>
#include <mymalloc.h>
#include <msg.h>
#include <myaddrinfo.h>
#include <name_mask.h>
#include <inet_proto.h>
INET_PROTO_INFO *inet_proto_table = 0;
#define INET_PROTO_MASK_IPV4 (1<<0)
#define INET_PROTO_MASK_IPV6 (1<<1)
static const NAME_MASK proto_table[] = {
#ifdef HAS_IPV6
INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6,
INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6,
#else
INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4,
#endif
INET_PROTO_NAME_IPV4, INET_PROTO_MASK_IPV4,
0,
};
static unsigned char *make_uchar_vector(int len,...)
{
const char *myname = "make_uchar_vector";
va_list ap;
int count;
unsigned char *vp;
va_start(ap, len);
if (len <= 0)
msg_panic("%s: bad vector length: %d", myname, len);
vp = (unsigned char *) mymalloc(sizeof(*vp) * len);
for (count = 0; count < len; count++)
vp[count] = va_arg(ap, unsigned);
va_end(ap);
return (vp);
}
static unsigned *make_unsigned_vector(int len,...)
{
const char *myname = "make_unsigned_vector";
va_list ap;
int count;
unsigned *vp;
va_start(ap, len);
if (len <= 0)
msg_panic("%s: bad vector length: %d", myname, len);
vp = (unsigned *) mymalloc(sizeof(*vp) * len);
for (count = 0; count < len; count++)
vp[count] = va_arg(ap, unsigned);
va_end(ap);
return (vp);
}
static void inet_proto_free(INET_PROTO_INFO *pf)
{
myfree((char *) pf->ai_family_list);
myfree((char *) pf->dns_atype_list);
myfree((char *) pf->sa_family_list);
myfree((char *) pf);
}
INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
{
const char *myname = "inet_proto";
INET_PROTO_INFO *pf;
int inet_proto_mask;
int sock;
inet_proto_mask = name_mask(context, proto_table, protocols);
#ifdef HAS_IPV6
if (inet_proto_mask & INET_PROTO_MASK_IPV6) {
if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
close(sock);
} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
msg_warn("%s: disabling IPv6 name/address support: %m", context);
inet_proto_mask &= ~INET_PROTO_MASK_IPV6;
} else {
msg_fatal("socket: %m");
}
}
#endif
if (inet_proto_mask & INET_PROTO_MASK_IPV4) {
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) {
close(sock);
} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
msg_warn("%s: disabling IPv4 name/address support: %m", context);
inet_proto_mask &= ~INET_PROTO_MASK_IPV4;
} else {
msg_fatal("socket: %m");
}
}
switch (inet_proto_mask) {
#ifdef HAS_IPV6
case INET_PROTO_MASK_IPV6:
pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
pf->ai_family = PF_INET6;
pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0);
pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0);
pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0);
break;
case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4):
pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
pf->ai_family = PF_UNSPEC;
pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0);
pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0);
pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0);
break;
#endif
case INET_PROTO_MASK_IPV4:
pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
pf->ai_family = PF_INET;
pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0);
pf->dns_atype_list = make_unsigned_vector(2, T_A, 0);
pf->sa_family_list = make_uchar_vector(2, AF_INET, 0);
break;
case 0:
pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
pf->ai_family = PF_UNSPEC;
pf->ai_family_list = make_unsigned_vector(1, 0);
pf->dns_atype_list = make_unsigned_vector(1, 0);
pf->sa_family_list = make_uchar_vector(1, 0);
break;
default:
msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask);
}
if (inet_proto_table)
inet_proto_free(inet_proto_table);
return (inet_proto_table = pf);
}
#ifdef TEST
static char *print_unsigned_vector(VSTRING *buf, unsigned *vector)
{
unsigned *p;
VSTRING_RESET(buf);
for (p = vector; *p; p++) {
vstring_sprintf_append(buf, "%u", *p);
if (p[1])
VSTRING_ADDCH(buf, ' ');
}
VSTRING_TERMINATE(buf);
return (vstring_str(buf));
}
static char *print_uchar_vector(VSTRING *buf, unsigned char *vector)
{
unsigned char *p;
VSTRING_RESET(buf);
for (p = vector; *p; p++) {
vstring_sprintf_append(buf, "%u", *p);
if (p[1])
VSTRING_ADDCH(buf, ' ');
}
VSTRING_TERMINATE(buf);
return (vstring_str(buf));
}
int main(int argc, char **argv)
{
const char *myname = argv[0];
INET_PROTO_INFO *pf;
VSTRING *buf;
if (argc < 2)
msg_fatal("usage: %s protocol(s)...", myname);
buf = vstring_alloc(10);
while (*++argv) {
msg_info("=== %s ===", *argv);
inet_proto_init(myname, *argv);
pf = inet_proto_table;
msg_info("ai_family = %u", pf->ai_family);
msg_info("ai_family_list = %s",
print_unsigned_vector(buf, pf->ai_family_list));
msg_info("dns_atype_list = %s",
print_unsigned_vector(buf, pf->dns_atype_list));
msg_info("sa_family_list = %s",
print_uchar_vector(buf, pf->sa_family_list));
}
vstring_free(buf);
return (0);
}
#endif