1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
| | #include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include "lookup.h"
int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
{
struct service ports[MAXSERVS];
struct address addrs[MAXADDRS];
char canon[256], *outcanon;
int nservs, naddrs, nais, canon_len, i, j, k;
int family = AF_UNSPEC, flags = 0, proto = 0, socktype = 0;
struct aibuf {
struct addrinfo ai;
union sa {
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} sa;
} *out;
if (!host && !serv) return EAI_NONAME;
if (hint) {
family = hint->ai_family;
flags = hint->ai_flags;
proto = hint->ai_protocol;
socktype = hint->ai_socktype;
const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV;
if ((flags & mask) != flags)
return EAI_BADFLAGS;
switch (family) {
case AF_INET:
case AF_INET6:
case AF_UNSPEC:
break;
default:
return EAI_FAMILY;
}
}
if (flags & AI_ADDRCONFIG) {
int cs;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
if (family != AF_INET6) {
struct sockaddr_in sa4 = {
.sin_family = AF_INET,
.sin_port = 65535,
.sin_addr.s_addr = INADDR_LOOPBACK
};
int s = socket(AF_INET, SOCK_CLOEXEC|SOCK_DGRAM,
IPPROTO_UDP);
if (s>=0) {
int r = connect(s, (void *)&sa4, sizeof sa4);
close(s);
if (r) {
if (family == AF_INET) return EAI_NONAME;
family = AF_INET6;
}
}
}
if (family != AF_INET) {
struct sockaddr_in6 sa6 = {
.sin6_family = AF_INET6,
.sin6_port = 65535,
.sin6_addr = IN6ADDR_LOOPBACK_INIT
};
int s = socket(AF_INET6, SOCK_CLOEXEC|SOCK_DGRAM,
IPPROTO_UDP);
if (s>=0) {
int r = connect(s, (void *)&sa6, sizeof sa6);
close(s);
if (r) {
if (family == AF_INET6) return EAI_NONAME;
family = AF_INET;
}
}
}
pthread_setcancelstate(cs, 0);
}
nservs = __lookup_serv(ports, serv, proto, socktype, flags);
if (nservs < 0) return nservs;
naddrs = __lookup_name(addrs, canon, host, family, flags);
if (naddrs < 0) return naddrs;
nais = nservs * naddrs;
canon_len = strlen(canon);
out = calloc(1, nais * sizeof(*out) + canon_len + 1);
if (!out) return EAI_MEMORY;
if (canon_len) {
outcanon = (void *)&out[nais];
memcpy(outcanon, canon, canon_len+1);
} else {
outcanon = 0;
}
for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) {
out[k].ai = (struct addrinfo){
.ai_family = addrs[i].family,
.ai_socktype = ports[j].socktype,
.ai_protocol = ports[j].proto,
.ai_addrlen = addrs[i].family == AF_INET
? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6),
.ai_addr = (void *)&out[k].sa,
.ai_canonname = outcanon,
.ai_next = &out[k+1].ai };
switch (addrs[i].family) {
case AF_INET:
out[k].sa.sin.sin_family = AF_INET;
out[k].sa.sin.sin_port = htons(ports[j].port);
memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4);
break;
case AF_INET6:
out[k].sa.sin6.sin6_family = AF_INET6;
out[k].sa.sin6.sin6_port = htons(ports[j].port);
out[k].sa.sin6.sin6_scope_id = addrs[i].scopeid;
memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16);
break;
}
}
out[nais-1].ai.ai_next = 0;
*res = &out->ai;
return 0;
}
|