Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/getaddrinfo.c
1532 views
1
/*
2
* Replacement for a missing getaddrinfo.
3
*
4
* This is an implementation of getaddrinfo for systems that don't have one so
5
* that networking code can use a consistent interface without #ifdef. It is
6
* a fairly minimal implementation, with the following limitations:
7
*
8
* - IPv4 support only. IPv6 is not supported.
9
* - AI_ADDRCONFIG is ignored.
10
* - Not thread-safe due to gethostbyname and getservbyname.
11
* - SOCK_DGRAM and SOCK_STREAM only.
12
* - Multiple possible socket types only generate one addrinfo struct.
13
* - Protocol hints aren't used correctly.
14
*
15
* The last four issues could probably be easily remedied, but haven't been
16
* needed to date. Adding IPv6 support isn't worth it; systems with IPv6
17
* support should already have getaddrinfo.
18
*
19
* The canonical version of this file is maintained in the rra-c-util package,
20
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
21
*
22
* Written by Russ Allbery <[email protected]>
23
*
24
* The authors hereby relinquish any claim to any copyright that they may have
25
* in this work, whether granted under contract or by operation of law or
26
* international treaty, and hereby commit to the public, at large, that they
27
* shall not, at any time in the future, seek to enforce any copyright in this
28
* work against any person or entity, or prevent any person or entity from
29
* copying, publishing, distributing or creating derivative works of this
30
* work.
31
*/
32
33
#include <config.h>
34
35
#ifndef HAVE_GETADDRINFO
36
37
#include <sys/types.h>
38
#include <sys/socket.h>
39
40
#include <stdlib.h>
41
#include <string.h>
42
#include <limits.h>
43
#include <netdb.h>
44
#include <errno.h>
45
46
#include <arpa/inet.h>
47
#include <netinet/in.h>
48
#ifdef NEED_RESOLV_H
49
# include <arpa/nameser.h>
50
# include <resolv.h>
51
#endif /* NEED_RESOLV_H */
52
53
#include <sudo_compat.h>
54
#include <compat/getaddrinfo.h>
55
56
/* We need access to h_errno to map errors from gethostbyname. */
57
#ifndef HAVE_DECL_H_ERRNO
58
extern int h_errno;
59
#endif
60
61
/*
62
* The netdb constants, which aren't always defined (particularly if h_errno
63
* isn't declared). We also make sure that a few of the less-used ones are
64
* defined so that we can deal with them in case statements.
65
*/
66
#ifndef HOST_NOT_FOUND
67
# define HOST_NOT_FOUND 1
68
# define TRY_AGAIN 2
69
# define NO_RECOVERY 3
70
# define NO_DATA 4
71
#endif
72
#ifndef NETDB_INTERNAL
73
# define NETDB_INTERNAL -1
74
#endif
75
76
/*
77
* If we're running the test suite, rename the functions to avoid conflicts
78
* with the system version. Note that we don't rename the structures and
79
* constants, but that should be okay (except possibly for gai_strerror).
80
*/
81
#ifdef TESTING
82
# define gai_strerror test_gai_strerror
83
# define freeaddrinfo test_freeaddrinfo
84
# define getaddrinfo test_getaddrinfo
85
const char *test_gai_strerror(int);
86
void test_freeaddrinfo(struct addrinfo *);
87
int test_getaddrinfo(const char *, const char *, const struct addrinfo *,
88
struct addrinfo **);
89
#endif
90
91
/*
92
* If the platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST,
93
* pick some other values for them.
94
*/
95
#ifdef TESTING
96
# if AI_NUMERICSERV == 0
97
# undef AI_NUMERICSERV
98
# define AI_NUMERICSERV 0x0080
99
# endif
100
# if AI_NUMERICHOST == 0
101
# undef AI_NUMERICHOST
102
# define AI_NUMERICHOST 0x0100
103
# endif
104
#endif
105
106
/*
107
* Value representing all of the hint flags set. Linux uses flags up to
108
* 0x0400, so be sure not to break when testing on that platform.
109
*/
110
#ifdef TESTING
111
# ifdef HAVE_GETADDRINFO
112
# define AI_INTERNAL_ALL 0x04ff
113
# else
114
# define AI_INTERNAL_ALL 0x01ff
115
# endif
116
#else
117
# define AI_INTERNAL_ALL 0x007f
118
#endif
119
120
/* Table of strings corresponding to the EAI_* error codes. */
121
static const char * const gai_errors[] = {
122
"Host name lookup failure", /* 1 EAI_AGAIN */
123
"Invalid flag value", /* 2 EAI_BADFLAGS */
124
"Unknown server error", /* 3 EAI_FAIL */
125
"Unsupported address family", /* 4 EAI_FAMILY */
126
"Memory allocation failure", /* 5 EAI_MEMORY */
127
"Host unknown or not given", /* 6 EAI_NONAME */
128
"Service not supported for socket", /* 7 EAI_SERVICE */
129
"Unsupported socket type", /* 8 EAI_SOCKTYPE */
130
"System error", /* 9 EAI_SYSTEM */
131
"Supplied buffer too small", /* 10 EAI_OVERFLOW */
132
};
133
134
/* Macro to set the len attribute of sockaddr_in. */
135
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
136
# define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in))
137
#else
138
# define sin_set_length(s) /* empty */
139
#endif
140
141
/*
142
* Return a constant string for a given EAI_* error code or a string
143
* indicating an unknown error.
144
*/
145
const char *
146
sudo_gai_strerror(int ecode)
147
{
148
if (ecode < 1 || (size_t) ecode > nitems(gai_errors))
149
return "Unknown error";
150
else
151
return gai_errors[ecode - 1];
152
}
153
154
155
/*
156
* Free a linked list of addrinfo structs.
157
*/
158
void
159
sudo_freeaddrinfo(struct addrinfo *ai)
160
{
161
struct addrinfo *next;
162
163
while (ai != NULL) {
164
next = ai->ai_next;
165
if (ai->ai_addr != NULL)
166
free(ai->ai_addr);
167
if (ai->ai_canonname != NULL)
168
free(ai->ai_canonname);
169
free(ai);
170
ai = next;
171
}
172
}
173
174
175
/*
176
* Allocate a new addrinfo struct, setting some defaults given that this
177
* implementation is IPv4 only. Also allocates an attached sockaddr_in and
178
* zeroes it, per the requirement for getaddrinfo. Takes the socktype,
179
* canonical name (which is copied if not NULL), address, and port. Returns
180
* NULL on a memory allocation failure.
181
*/
182
static struct addrinfo *
183
gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr,
184
unsigned short port)
185
{
186
struct addrinfo *ai;
187
188
ai = malloc(sizeof(*ai));
189
if (ai == NULL)
190
return NULL;
191
ai->ai_addr = malloc(sizeof(struct sockaddr_in));
192
if (ai->ai_addr == NULL) {
193
free(ai);
194
return NULL;
195
}
196
ai->ai_next = NULL;
197
if (canonical == NULL)
198
ai->ai_canonname = NULL;
199
else {
200
ai->ai_canonname = strdup(canonical);
201
if (ai->ai_canonname == NULL) {
202
freeaddrinfo(ai);
203
return NULL;
204
}
205
}
206
memset(ai->ai_addr, 0, sizeof(struct sockaddr_in));
207
ai->ai_flags = 0;
208
ai->ai_family = AF_INET;
209
ai->ai_socktype = socktype;
210
ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
211
ai->ai_addrlen = sizeof(struct sockaddr_in);
212
((struct sockaddr_in *) ai->ai_addr)->sin_family = AF_INET;
213
((struct sockaddr_in *) ai->ai_addr)->sin_addr = addr;
214
((struct sockaddr_in *) ai->ai_addr)->sin_port = htons(port);
215
sin_set_length((struct sockaddr_in *) ai->ai_addr);
216
return ai;
217
}
218
219
220
/*
221
* Look up a service. Takes the service name (which may be numeric), the hint
222
* flags, a pointer to the socket type (used to determine whether TCP or UDP
223
* services are of interest and, if 0, is filled in with the result of
224
* getservbyname if the service was not numeric), and a pointer to the
225
* addrinfo struct to fill in. Returns 0 on success or an EAI_* error on
226
* failure.
227
*/
228
static int
229
gai_service(const char *servname, int flags, int *type, unsigned short *port)
230
{
231
struct servent *servent;
232
const char *protocol;
233
const char *errstr;
234
unsigned short value;
235
236
value = sudo_strtonum(servname, 0, USHRT_MAX, &errstr);
237
if (errstr == NULL) {
238
*port = value;
239
} else if (errno == ERANGE) {
240
return EAI_SERVICE;
241
} else {
242
if (flags & AI_NUMERICSERV)
243
return EAI_NONAME;
244
if (*type != 0)
245
protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp";
246
else
247
protocol = NULL;
248
249
/*
250
* We really technically should be generating an addrinfo struct for
251
* each possible protocol unless type is set, but this works well
252
* enough for what I need this for.
253
*/
254
servent = getservbyname(servname, protocol);
255
if (servent == NULL)
256
return EAI_NONAME;
257
if (strcmp(servent->s_proto, "udp") == 0)
258
*type = SOCK_DGRAM;
259
else if (strcmp(servent->s_proto, "tcp") == 0)
260
*type = SOCK_STREAM;
261
else
262
return EAI_SERVICE;
263
*port = htons(servent->s_port);
264
}
265
return 0;
266
}
267
268
269
/*
270
* Look up a host and fill in a linked list of addrinfo structs with the
271
* results, one per IP address of the returned host. Takes the name or IP
272
* address of the host as a string, the lookup flags, the type of socket (to
273
* fill into the addrinfo structs), the port (likewise), and a pointer to
274
* where the head of the linked list should be put. Returns 0 on success or
275
* the appropriate EAI_* error.
276
*/
277
static int
278
gai_lookup(const char *nodename, int flags, int socktype, unsigned short port,
279
struct addrinfo **res)
280
{
281
struct addrinfo *ai, *first, *prev;
282
struct in_addr addr;
283
struct hostent *host;
284
const char *canonical;
285
size_t i;
286
287
if (inet_pton(AF_INET, nodename, &addr)) {
288
canonical = (flags & AI_CANONNAME) ? nodename : NULL;
289
ai = gai_addrinfo_new(socktype, canonical, addr, port);
290
if (ai == NULL)
291
return EAI_MEMORY;
292
*res = ai;
293
return 0;
294
} else {
295
if (flags & AI_NUMERICHOST)
296
return EAI_NONAME;
297
host = gethostbyname(nodename);
298
if (host == NULL)
299
switch (h_errno) {
300
case HOST_NOT_FOUND:
301
return EAI_NONAME;
302
case TRY_AGAIN:
303
case NO_DATA:
304
return EAI_AGAIN;
305
case NO_RECOVERY:
306
return EAI_FAIL;
307
case NETDB_INTERNAL:
308
default:
309
return EAI_SYSTEM;
310
}
311
if (host->h_addr_list[0] == NULL)
312
return EAI_FAIL;
313
canonical = (flags & AI_CANONNAME)
314
? ((host->h_name != NULL) ? host->h_name : nodename)
315
: NULL;
316
first = NULL;
317
prev = NULL;
318
for (i = 0; host->h_addr_list[i] != NULL; i++) {
319
if (host->h_length != sizeof(addr)) {
320
freeaddrinfo(first);
321
return EAI_FAIL;
322
}
323
memcpy(&addr, host->h_addr_list[i], sizeof(addr));
324
ai = gai_addrinfo_new(socktype, canonical, addr, port);
325
if (ai == NULL) {
326
freeaddrinfo(first);
327
return EAI_MEMORY;
328
}
329
if (first == NULL) {
330
first = ai;
331
prev = ai;
332
} else {
333
prev->ai_next = ai;
334
prev = ai;
335
}
336
}
337
*res = first;
338
return 0;
339
}
340
}
341
342
343
/*
344
* The actual getaddrinfo implementation.
345
*/
346
int
347
sudo_getaddrinfo(const char *nodename, const char *servname,
348
const struct addrinfo *hints, struct addrinfo **res)
349
{
350
struct addrinfo *ai;
351
struct in_addr addr;
352
int flags, socktype, status;
353
unsigned short port;
354
355
/* Take the hints into account and check them for validity. */
356
if (hints != NULL) {
357
flags = hints->ai_flags;
358
socktype = hints->ai_socktype;
359
if ((flags & AI_INTERNAL_ALL) != flags)
360
return EAI_BADFLAGS;
361
if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
362
return EAI_FAMILY;
363
if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
364
return EAI_SOCKTYPE;
365
366
/* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */
367
if (hints->ai_protocol != 0) {
368
int protocol = hints->ai_protocol;
369
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
370
return EAI_SOCKTYPE;
371
}
372
} else {
373
flags = 0;
374
socktype = 0;
375
}
376
377
/*
378
* See what we're doing. If nodename is null, either AI_PASSIVE is set or
379
* we're getting information for connecting to a service on the loopback
380
* address. Otherwise, we're getting information for connecting to a
381
* remote system.
382
*/
383
if (servname == NULL)
384
port = 0;
385
else {
386
status = gai_service(servname, flags, &socktype, &port);
387
if (status != 0)
388
return status;
389
}
390
if (nodename != NULL)
391
return gai_lookup(nodename, flags, socktype, port, res);
392
else {
393
if (servname == NULL)
394
return EAI_NONAME;
395
if ((flags & AI_PASSIVE) == AI_PASSIVE)
396
addr.s_addr = INADDR_ANY;
397
else
398
addr.s_addr = htonl(0x7f000001UL);
399
ai = gai_addrinfo_new(socktype, NULL, addr, port);
400
if (ai == NULL)
401
return EAI_MEMORY;
402
*res = ai;
403
return 0;
404
}
405
}
406
#endif /* HAVE_GETADDRINFO */
407
408