remove duplicate includes from dynlink.c, strfmon.c and getaddrinfo.c
[musl] / src / network / getaddrinfo.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include "__dns.h"
10 #include "stdio_impl.h"
11
12 static int is_valid(const char *host)
13 {
14         const unsigned char *s;
15         if (strlen(host)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0;
16         for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++);
17         return !*s;
18 }
19
20 #if 0
21 static int have_af(int family)
22 {
23         struct sockaddr_in6 sin6 = { .sin6_family = family };
24         socklen_t sl = family == AF_INET
25                 ? sizeof(struct sockaddr_in)
26                 : sizeof(struct sockaddr_in6);
27         int sock = socket(family, SOCK_STREAM, 0);
28         int have = !bind(sock, (void *)&sin6, sl);
29         close(sock);
30         return have;
31 }
32 #endif
33
34 union sa {
35         struct sockaddr_in sin;
36         struct sockaddr_in6 sin6;
37 };
38
39 struct aibuf {
40         struct addrinfo ai;
41         union sa sa;
42 };
43
44 /* Extra slots needed for storing canonical name */
45 #define EXTRA ((256+sizeof(struct aibuf)-1)/sizeof(struct aibuf))
46
47 int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
48 {
49         int flags = hint ? hint->ai_flags : 0;
50         int family = hint ? hint->ai_family : AF_UNSPEC;
51         int type = hint ? hint->ai_socktype : 0;
52         int proto = hint ? hint->ai_protocol : 0;
53         unsigned long port = 0;
54         struct aibuf *buf;
55         union sa sa = {{0}};
56         unsigned char reply[1024];
57         int i, j;
58         char line[512];
59         FILE *f, _f;
60         unsigned char _buf[1024];
61         char *z;
62         int result;
63         int cnt;
64
65         if (host && strlen(host)>255) return EAI_NONAME;
66         if (serv && strlen(serv)>32) return EAI_SERVICE;
67
68         if (type && !proto)
69                 proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
70         if (!type && proto)
71                 type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
72
73         if (serv) {
74                 if (!*serv) return EAI_SERVICE;
75                 port = strtoul(serv, &z, 10);
76                 if (*z) {
77                         size_t servlen = strlen(serv);
78                         char *end = line;
79
80                         if (flags & AI_NUMERICSERV) return EAI_SERVICE;
81
82                         f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
83                         if (!f) return EAI_SERVICE;
84                         while (fgets(line, sizeof line, f)) {
85                                 if (strncmp(line, serv, servlen) || !isspace(line[servlen]))
86                                         continue;
87                                 port = strtoul(line+servlen, &end, 10);
88                                 if (strncmp(end, proto==IPPROTO_UDP ? "/udp" : "/tcp", 4))
89                                         continue;
90                                 break;
91                         }
92                         __fclose_ca(f);
93                         if (feof(f)) return EAI_SERVICE;
94                 }
95                 if (port > 65535) return EAI_SERVICE;
96                 port = htons(port);
97         }
98
99         if (!host) {
100                 if (family == AF_UNSPEC) {
101                         cnt = 2; family = AF_INET;
102                 } else {
103                         cnt = 1;
104                 }
105                 buf = calloc(sizeof *buf, cnt);
106                 if (!buf) return EAI_MEMORY;
107                 for (i=0; i<cnt; i++) {
108                         if (i) family = AF_INET6;
109                         buf[i].ai.ai_protocol = proto;
110                         buf[i].ai.ai_socktype = type;
111                         buf[i].ai.ai_addr = (void *)&buf[i].sa;
112                         buf[i].ai.ai_addrlen = family==AF_INET6
113                                 ? sizeof sa.sin6 : sizeof sa.sin;
114                         buf[i].ai.ai_family = family;
115                         buf[i].sa.sin.sin_family = family;
116                         buf[i].sa.sin.sin_port = port;
117                         if (i+1<cnt) buf[i].ai.ai_next = &buf[i+1].ai;
118                         if (!(flags & AI_PASSIVE)) {
119                                 if (family == AF_INET) {
120                                         0[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=127;
121                                         3[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=1;
122                                 } else buf[i].sa.sin6.sin6_addr.s6_addr[15] = 1;
123                         }
124                 }
125                 *res = &buf->ai;
126                 return 0;
127         }
128
129         if (!*host) return EAI_NONAME;
130
131         /* Try as a numeric address */
132         if (__ipparse(&sa, family, host) >= 0) {
133                 buf = calloc(sizeof *buf, 1+EXTRA);
134                 if (!buf) return EAI_MEMORY;
135                 family = sa.sin.sin_family;
136                 buf->ai.ai_protocol = proto;
137                 buf->ai.ai_socktype = type;
138                 buf->ai.ai_addr = (void *)&buf->sa;
139                 buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
140                 buf->ai.ai_family = family;
141                 buf->ai.ai_canonname = (char *)host;
142                 buf->sa = sa;
143                 buf->sa.sin.sin_port = port;
144                 *res = &buf->ai;
145                 return 0;
146         }
147
148         if (flags & AI_NUMERICHOST) return EAI_NONAME;
149
150         f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
151         if (f) while (fgets(line, sizeof line, f)) {
152                 char *p;
153                 size_t l = strlen(host);
154
155                 if ((p=strchr(line, '#'))) *p++='\n', *p=0;
156                 for(p=line+1; (p=strstr(p, host)) &&
157                         (!isspace(p[-1]) || !isspace(p[l])); p++);
158                 if (!p) continue;
159                 __fclose_ca(f);
160
161                 /* Isolate IP address to parse */
162                 for (p=line; *p && !isspace(*p); p++);
163                 *p++ = 0;
164                 if (__ipparse(&sa, family, line) < 0) return EAI_NONAME;
165
166                 /* Allocate and fill result buffer */
167                 buf = calloc(sizeof *buf, 1+EXTRA);
168                 if (!buf) return EAI_MEMORY;
169                 family = sa.sin.sin_family;
170                 buf->ai.ai_protocol = proto;
171                 buf->ai.ai_socktype = type;
172                 buf->ai.ai_addr = (void *)&buf->sa;
173                 buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
174                 buf->ai.ai_family = family;
175                 buf->sa = sa;
176                 buf->sa.sin.sin_port = port;
177
178                 /* Extract first name as canonical name */
179                 for (; *p && isspace(*p); p++);
180                 buf->ai.ai_canonname = (void *)(buf+1);
181                 snprintf(buf->ai.ai_canonname, 256, "%s", p);
182                 for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++);
183                 *p = 0;
184                 if (!is_valid(buf->ai.ai_canonname))
185                         buf->ai.ai_canonname = 0;
186
187                 *res = &buf->ai;
188                 return 0;
189         }
190         if (f) __fclose_ca(f);
191
192 #if 0
193         f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
194         if (f) while (fgets(line, sizeof line, f)) {
195                 if (!isspace(line[10]) || (strncmp(line, "search", 6)
196                         && strncmp(line, "domain", 6))) continue;
197         }
198         if (f) __fclose_ca(f);
199 #endif
200
201         /* Perform one or more DNS queries for host */
202         memset(reply, 0, sizeof reply);
203         result = __dns_query(reply, host, family, 0);
204         if (result < 0) return result;
205
206         cnt = __dns_count_addrs(reply, result);
207         if (cnt <= 0) return EAI_NONAME;
208
209         buf = calloc(sizeof *buf, cnt+EXTRA);
210         if (!buf) return EAI_MEMORY;
211
212         i = 0;
213         if (family != AF_INET6) {
214                 j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0);
215                 while (j--) buf[i++].sa.sin.sin_family = AF_INET;
216         }
217         if (family != AF_INET) {
218                 j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0);
219                 while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
220         }
221         if (result>1) {
222                 j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0);
223                 while (j--) buf[i++].sa.sin.sin_family = AF_INET;
224                 j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0);
225                 while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
226         }
227
228         if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) <= 0)
229                 strcpy((void *)&buf[cnt], host);
230
231         for (i=0; i<cnt; i++) {
232                 buf[i].ai.ai_protocol = proto;
233                 buf[i].ai.ai_socktype = type;
234                 buf[i].ai.ai_addr = (void *)&buf[i].sa;
235                 buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6
236                         ? sizeof sa.sin6 : sizeof sa.sin;
237                 buf[i].ai.ai_family = buf[i].sa.sin.sin_family;
238                 buf[i].sa.sin.sin_port = port;
239                 buf[i].ai.ai_next = &buf[i+1].ai;
240                 buf[i].ai.ai_canonname = (void *)&buf[cnt];
241         }
242         buf[cnt-1].ai.ai_next = 0;
243         *res = &buf->ai;
244
245         return 0;
246 }