Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libcasper/services/cap_dns/cap_dns.c
48260 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2012-2013 The FreeBSD Foundation
5
*
6
* This software was developed by Pawel Jakub Dawidek under sponsorship from
7
* the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*/
30
31
#include <sys/cdefs.h>
32
#include <sys/dnv.h>
33
#include <sys/nv.h>
34
#include <netinet/in.h>
35
36
#include <assert.h>
37
#include <errno.h>
38
#include <netdb.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
#include <libcasper.h>
44
#include <libcasper_service.h>
45
46
#include "cap_dns.h"
47
48
static struct hostent hent;
49
50
static void
51
hostent_free(struct hostent *hp)
52
{
53
unsigned int ii;
54
55
free(hp->h_name);
56
hp->h_name = NULL;
57
if (hp->h_aliases != NULL) {
58
for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
59
free(hp->h_aliases[ii]);
60
free(hp->h_aliases);
61
hp->h_aliases = NULL;
62
}
63
if (hp->h_addr_list != NULL) {
64
for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
65
free(hp->h_addr_list[ii]);
66
free(hp->h_addr_list);
67
hp->h_addr_list = NULL;
68
}
69
}
70
71
static struct hostent *
72
hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
73
{
74
unsigned int ii, nitems;
75
char nvlname[64];
76
int n;
77
78
hostent_free(hp);
79
80
hp->h_name = strdup(nvlist_get_string(nvl, "name"));
81
if (hp->h_name == NULL)
82
goto fail;
83
hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
84
hp->h_length = (int)nvlist_get_number(nvl, "length");
85
86
nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
87
hp->h_aliases = calloc(nitems + 1, sizeof(hp->h_aliases[0]));
88
if (hp->h_aliases == NULL)
89
goto fail;
90
for (ii = 0; ii < nitems; ii++) {
91
n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
92
assert(n > 0 && n < (int)sizeof(nvlname));
93
hp->h_aliases[ii] =
94
strdup(nvlist_get_string(nvl, nvlname));
95
if (hp->h_aliases[ii] == NULL)
96
goto fail;
97
}
98
hp->h_aliases[ii] = NULL;
99
100
nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
101
hp->h_addr_list = calloc(nitems + 1, sizeof(hp->h_addr_list[0]));
102
if (hp->h_addr_list == NULL)
103
goto fail;
104
for (ii = 0; ii < nitems; ii++) {
105
hp->h_addr_list[ii] = malloc(hp->h_length);
106
if (hp->h_addr_list[ii] == NULL)
107
goto fail;
108
n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
109
assert(n > 0 && n < (int)sizeof(nvlname));
110
bcopy(nvlist_get_binary(nvl, nvlname, NULL),
111
hp->h_addr_list[ii], hp->h_length);
112
}
113
hp->h_addr_list[ii] = NULL;
114
115
return (hp);
116
fail:
117
hostent_free(hp);
118
h_errno = NO_RECOVERY;
119
return (NULL);
120
}
121
122
struct hostent *
123
cap_gethostbyname(cap_channel_t *chan, const char *name)
124
{
125
126
return (cap_gethostbyname2(chan, name, AF_INET));
127
}
128
129
struct hostent *
130
cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
131
{
132
struct hostent *hp;
133
nvlist_t *nvl;
134
135
nvl = nvlist_create(0);
136
nvlist_add_string(nvl, "cmd", "gethostbyname");
137
nvlist_add_number(nvl, "family", (uint64_t)type);
138
nvlist_add_string(nvl, "name", name);
139
nvl = cap_xfer_nvlist(chan, nvl);
140
if (nvl == NULL) {
141
h_errno = NO_RECOVERY;
142
return (NULL);
143
}
144
if (nvlist_get_number(nvl, "error") != 0) {
145
h_errno = (int)nvlist_get_number(nvl, "error");
146
nvlist_destroy(nvl);
147
return (NULL);
148
}
149
150
hp = hostent_unpack(nvl, &hent);
151
nvlist_destroy(nvl);
152
return (hp);
153
}
154
155
struct hostent *
156
cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
157
int type)
158
{
159
struct hostent *hp;
160
nvlist_t *nvl;
161
162
nvl = nvlist_create(0);
163
nvlist_add_string(nvl, "cmd", "gethostbyaddr");
164
nvlist_add_binary(nvl, "addr", addr, (size_t)len);
165
nvlist_add_number(nvl, "family", (uint64_t)type);
166
nvl = cap_xfer_nvlist(chan, nvl);
167
if (nvl == NULL) {
168
h_errno = NO_RECOVERY;
169
return (NULL);
170
}
171
if (nvlist_get_number(nvl, "error") != 0) {
172
h_errno = (int)nvlist_get_number(nvl, "error");
173
nvlist_destroy(nvl);
174
return (NULL);
175
}
176
hp = hostent_unpack(nvl, &hent);
177
nvlist_destroy(nvl);
178
return (hp);
179
}
180
181
static struct addrinfo *
182
addrinfo_unpack(const nvlist_t *nvl)
183
{
184
struct addrinfo *ai;
185
const void *addr;
186
size_t addrlen;
187
const char *canonname;
188
189
addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
190
ai = malloc(sizeof(*ai) + addrlen);
191
if (ai == NULL)
192
return (NULL);
193
ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
194
ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
195
ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
196
ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
197
ai->ai_addrlen = (socklen_t)addrlen;
198
canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
199
if (canonname != NULL) {
200
ai->ai_canonname = strdup(canonname);
201
if (ai->ai_canonname == NULL) {
202
free(ai);
203
return (NULL);
204
}
205
} else {
206
ai->ai_canonname = NULL;
207
}
208
ai->ai_addr = (void *)(ai + 1);
209
bcopy(addr, ai->ai_addr, addrlen);
210
ai->ai_next = NULL;
211
212
return (ai);
213
}
214
215
int
216
cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
217
const struct addrinfo *hints, struct addrinfo **res)
218
{
219
struct addrinfo *firstai, *prevai, *curai;
220
unsigned int ii;
221
const nvlist_t *nvlai;
222
char nvlname[64];
223
nvlist_t *nvl;
224
int error, n;
225
226
nvl = nvlist_create(0);
227
nvlist_add_string(nvl, "cmd", "getaddrinfo");
228
if (hostname != NULL)
229
nvlist_add_string(nvl, "hostname", hostname);
230
if (servname != NULL)
231
nvlist_add_string(nvl, "servname", servname);
232
if (hints != NULL) {
233
nvlist_add_number(nvl, "hints.ai_flags",
234
(uint64_t)hints->ai_flags);
235
nvlist_add_number(nvl, "hints.ai_family",
236
(uint64_t)hints->ai_family);
237
nvlist_add_number(nvl, "hints.ai_socktype",
238
(uint64_t)hints->ai_socktype);
239
nvlist_add_number(nvl, "hints.ai_protocol",
240
(uint64_t)hints->ai_protocol);
241
}
242
nvl = cap_xfer_nvlist(chan, nvl);
243
if (nvl == NULL)
244
return (EAI_MEMORY);
245
if (nvlist_get_number(nvl, "error") != 0) {
246
error = (int)nvlist_get_number(nvl, "error");
247
nvlist_destroy(nvl);
248
return (error);
249
}
250
251
nvlai = NULL;
252
firstai = prevai = curai = NULL;
253
for (ii = 0; ; ii++) {
254
n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
255
assert(n > 0 && n < (int)sizeof(nvlname));
256
if (!nvlist_exists_nvlist(nvl, nvlname))
257
break;
258
nvlai = nvlist_get_nvlist(nvl, nvlname);
259
curai = addrinfo_unpack(nvlai);
260
if (curai == NULL)
261
break;
262
if (prevai != NULL)
263
prevai->ai_next = curai;
264
else if (firstai == NULL)
265
firstai = curai;
266
prevai = curai;
267
}
268
nvlist_destroy(nvl);
269
if (curai == NULL && nvlai != NULL) {
270
if (firstai == NULL)
271
freeaddrinfo(firstai);
272
return (EAI_MEMORY);
273
}
274
275
*res = firstai;
276
return (0);
277
}
278
279
int
280
cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
281
char *host, size_t hostlen, char *serv, size_t servlen, int flags)
282
{
283
nvlist_t *nvl;
284
int error;
285
286
nvl = nvlist_create(0);
287
nvlist_add_string(nvl, "cmd", "getnameinfo");
288
nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
289
nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
290
nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
291
nvlist_add_number(nvl, "flags", (uint64_t)flags);
292
nvl = cap_xfer_nvlist(chan, nvl);
293
if (nvl == NULL)
294
return (EAI_MEMORY);
295
if (nvlist_get_number(nvl, "error") != 0) {
296
error = (int)nvlist_get_number(nvl, "error");
297
nvlist_destroy(nvl);
298
return (error);
299
}
300
301
if (host != NULL && nvlist_exists_string(nvl, "host"))
302
strlcpy(host, nvlist_get_string(nvl, "host"), hostlen);
303
if (serv != NULL && nvlist_exists_string(nvl, "serv"))
304
strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen);
305
nvlist_destroy(nvl);
306
return (0);
307
}
308
309
static void
310
limit_remove(nvlist_t *limits, const char *prefix)
311
{
312
const char *name;
313
size_t prefixlen;
314
void *cookie;
315
316
prefixlen = strlen(prefix);
317
again:
318
cookie = NULL;
319
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
320
if (strncmp(name, prefix, prefixlen) == 0) {
321
nvlist_free(limits, name);
322
goto again;
323
}
324
}
325
}
326
327
int
328
cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
329
size_t ntypes)
330
{
331
nvlist_t *limits;
332
unsigned int i;
333
char nvlname[64];
334
int n;
335
336
if (cap_limit_get(chan, &limits) < 0)
337
return (-1);
338
if (limits == NULL)
339
limits = nvlist_create(0);
340
else
341
limit_remove(limits, "type");
342
for (i = 0; i < ntypes; i++) {
343
n = snprintf(nvlname, sizeof(nvlname), "type%u", i);
344
assert(n > 0 && n < (int)sizeof(nvlname));
345
nvlist_add_string(limits, nvlname, types[i]);
346
}
347
return (cap_limit_set(chan, limits));
348
}
349
350
int
351
cap_dns_family_limit(cap_channel_t *chan, const int *families,
352
size_t nfamilies)
353
{
354
nvlist_t *limits;
355
unsigned int i;
356
char nvlname[64];
357
int n;
358
359
if (cap_limit_get(chan, &limits) < 0)
360
return (-1);
361
if (limits == NULL)
362
limits = nvlist_create(0);
363
else
364
limit_remove(limits, "family");
365
for (i = 0; i < nfamilies; i++) {
366
n = snprintf(nvlname, sizeof(nvlname), "family%u", i);
367
assert(n > 0 && n < (int)sizeof(nvlname));
368
nvlist_add_number(limits, nvlname, (uint64_t)families[i]);
369
}
370
return (cap_limit_set(chan, limits));
371
}
372
373
/*
374
* Service functions.
375
*/
376
static bool
377
dns_allowed_type(const nvlist_t *limits, const char *type)
378
{
379
const char *name;
380
bool notypes;
381
void *cookie;
382
383
if (limits == NULL)
384
return (true);
385
386
notypes = true;
387
cookie = NULL;
388
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
389
if (strncmp(name, "type", sizeof("type") - 1) != 0)
390
continue;
391
notypes = false;
392
if (strcmp(nvlist_get_string(limits, name), type) == 0)
393
return (true);
394
}
395
396
/* If there are no types at all, allow any type. */
397
if (notypes)
398
return (true);
399
400
return (false);
401
}
402
403
static bool
404
dns_allowed_family(const nvlist_t *limits, int family)
405
{
406
const char *name;
407
bool nofamilies;
408
void *cookie;
409
410
if (limits == NULL)
411
return (true);
412
413
nofamilies = true;
414
cookie = NULL;
415
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
416
if (strncmp(name, "family", sizeof("family") - 1) != 0)
417
continue;
418
nofamilies = false;
419
if (family == AF_UNSPEC)
420
continue;
421
if (nvlist_get_number(limits, name) == (uint64_t)family)
422
return (true);
423
}
424
425
/* If there are no families at all, allow any family. */
426
if (nofamilies)
427
return (true);
428
429
return (false);
430
}
431
432
static void
433
hostent_pack(const struct hostent *hp, nvlist_t *nvl)
434
{
435
unsigned int ii;
436
char nvlname[64];
437
int n;
438
439
nvlist_add_string(nvl, "name", hp->h_name);
440
nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
441
nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
442
443
if (hp->h_aliases == NULL) {
444
nvlist_add_number(nvl, "naliases", 0);
445
} else {
446
for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
447
n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
448
assert(n > 0 && n < (int)sizeof(nvlname));
449
nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
450
}
451
nvlist_add_number(nvl, "naliases", (uint64_t)ii);
452
}
453
454
if (hp->h_addr_list == NULL) {
455
nvlist_add_number(nvl, "naddrs", 0);
456
} else {
457
for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
458
n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
459
assert(n > 0 && n < (int)sizeof(nvlname));
460
nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
461
(size_t)hp->h_length);
462
}
463
nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
464
}
465
}
466
467
static int
468
dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
469
nvlist_t *nvlout)
470
{
471
struct hostent *hp;
472
int family;
473
474
if (!dns_allowed_type(limits, "NAME2ADDR") &&
475
!dns_allowed_type(limits, "NAME"))
476
return (NO_RECOVERY);
477
478
family = (int)nvlist_get_number(nvlin, "family");
479
480
if (!dns_allowed_family(limits, family))
481
return (NO_RECOVERY);
482
483
hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
484
if (hp == NULL)
485
return (h_errno);
486
hostent_pack(hp, nvlout);
487
return (0);
488
}
489
490
static int
491
dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
492
nvlist_t *nvlout)
493
{
494
struct hostent *hp;
495
const void *addr;
496
size_t addrsize;
497
int family;
498
499
if (!dns_allowed_type(limits, "ADDR2NAME") &&
500
!dns_allowed_type(limits, "ADDR"))
501
return (NO_RECOVERY);
502
503
family = (int)nvlist_get_number(nvlin, "family");
504
505
if (!dns_allowed_family(limits, family))
506
return (NO_RECOVERY);
507
508
addr = nvlist_get_binary(nvlin, "addr", &addrsize);
509
hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
510
if (hp == NULL)
511
return (h_errno);
512
hostent_pack(hp, nvlout);
513
return (0);
514
}
515
516
static int
517
dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
518
{
519
struct sockaddr_storage sast;
520
const void *sabin;
521
char *host, *serv;
522
size_t sabinsize, hostlen, servlen;
523
socklen_t salen;
524
int error, flags;
525
526
if (!dns_allowed_type(limits, "ADDR2NAME") &&
527
!dns_allowed_type(limits, "ADDR"))
528
return (NO_RECOVERY);
529
530
error = 0;
531
host = serv = NULL;
532
memset(&sast, 0, sizeof(sast));
533
534
hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
535
servlen = (size_t)nvlist_get_number(nvlin, "servlen");
536
537
if (hostlen > 0) {
538
host = calloc(1, hostlen);
539
if (host == NULL) {
540
error = EAI_MEMORY;
541
goto out;
542
}
543
}
544
if (servlen > 0) {
545
serv = calloc(1, servlen);
546
if (serv == NULL) {
547
error = EAI_MEMORY;
548
goto out;
549
}
550
}
551
552
sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
553
if (sabinsize > sizeof(sast)) {
554
error = EAI_FAIL;
555
goto out;
556
}
557
558
memcpy(&sast, sabin, sabinsize);
559
salen = (socklen_t)sabinsize;
560
561
if ((sast.ss_family != AF_INET ||
562
salen != sizeof(struct sockaddr_in)) &&
563
(sast.ss_family != AF_INET6 ||
564
salen != sizeof(struct sockaddr_in6))) {
565
error = EAI_FAIL;
566
goto out;
567
}
568
569
if (!dns_allowed_family(limits, (int)sast.ss_family)) {
570
error = NO_RECOVERY;
571
goto out;
572
}
573
574
flags = (int)nvlist_get_number(nvlin, "flags");
575
576
error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
577
serv, servlen, flags);
578
if (error != 0)
579
goto out;
580
581
if (host != NULL)
582
nvlist_move_string(nvlout, "host", host);
583
if (serv != NULL)
584
nvlist_move_string(nvlout, "serv", serv);
585
out:
586
if (error != 0) {
587
free(host);
588
free(serv);
589
}
590
return (error);
591
}
592
593
static nvlist_t *
594
addrinfo_pack(const struct addrinfo *ai)
595
{
596
nvlist_t *nvl;
597
598
nvl = nvlist_create(0);
599
nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
600
nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
601
nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
602
nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
603
nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
604
if (ai->ai_canonname != NULL)
605
nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
606
607
return (nvl);
608
}
609
610
static int
611
dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
612
{
613
struct addrinfo hints, *hintsp, *res, *cur;
614
const char *hostname, *servname;
615
char nvlname[64];
616
nvlist_t *elem;
617
unsigned int ii;
618
int error, family, n;
619
620
if (!dns_allowed_type(limits, "NAME2ADDR") &&
621
!dns_allowed_type(limits, "NAME"))
622
return (NO_RECOVERY);
623
624
hostname = dnvlist_get_string(nvlin, "hostname", NULL);
625
servname = dnvlist_get_string(nvlin, "servname", NULL);
626
if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
627
hints.ai_flags = (int)nvlist_get_number(nvlin,
628
"hints.ai_flags");
629
hints.ai_family = (int)nvlist_get_number(nvlin,
630
"hints.ai_family");
631
hints.ai_socktype = (int)nvlist_get_number(nvlin,
632
"hints.ai_socktype");
633
hints.ai_protocol = (int)nvlist_get_number(nvlin,
634
"hints.ai_protocol");
635
hints.ai_addrlen = 0;
636
hints.ai_addr = NULL;
637
hints.ai_canonname = NULL;
638
hints.ai_next = NULL;
639
hintsp = &hints;
640
family = hints.ai_family;
641
} else {
642
hintsp = NULL;
643
family = AF_UNSPEC;
644
}
645
646
if (!dns_allowed_family(limits, family))
647
return (NO_RECOVERY);
648
649
error = getaddrinfo(hostname, servname, hintsp, &res);
650
if (error != 0)
651
goto out;
652
653
for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
654
elem = addrinfo_pack(cur);
655
n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
656
assert(n > 0 && n < (int)sizeof(nvlname));
657
nvlist_move_nvlist(nvlout, nvlname, elem);
658
}
659
660
freeaddrinfo(res);
661
error = 0;
662
out:
663
return (error);
664
}
665
666
static bool
667
limit_has_entry(const nvlist_t *limits, const char *prefix)
668
{
669
const char *name;
670
size_t prefixlen;
671
void *cookie;
672
673
if (limits == NULL)
674
return (false);
675
676
prefixlen = strlen(prefix);
677
678
cookie = NULL;
679
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
680
if (strncmp(name, prefix, prefixlen) == 0)
681
return (true);
682
}
683
684
return (false);
685
}
686
687
static int
688
dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
689
{
690
const char *name;
691
void *cookie;
692
int nvtype;
693
bool hastype, hasfamily;
694
695
hastype = false;
696
hasfamily = false;
697
698
cookie = NULL;
699
while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
700
if (nvtype == NV_TYPE_STRING) {
701
const char *type;
702
703
if (strncmp(name, "type", sizeof("type") - 1) != 0)
704
return (EINVAL);
705
type = nvlist_get_string(newlimits, name);
706
if (strcmp(type, "ADDR2NAME") != 0 &&
707
strcmp(type, "NAME2ADDR") != 0 &&
708
strcmp(type, "ADDR") != 0 &&
709
strcmp(type, "NAME") != 0) {
710
return (EINVAL);
711
}
712
if (!dns_allowed_type(oldlimits, type))
713
return (ENOTCAPABLE);
714
hastype = true;
715
} else if (nvtype == NV_TYPE_NUMBER) {
716
int family;
717
718
if (strncmp(name, "family", sizeof("family") - 1) != 0)
719
return (EINVAL);
720
family = (int)nvlist_get_number(newlimits, name);
721
if (!dns_allowed_family(oldlimits, family))
722
return (ENOTCAPABLE);
723
hasfamily = true;
724
} else {
725
return (EINVAL);
726
}
727
}
728
729
/*
730
* If the new limit doesn't mention type or family we have to
731
* check if the current limit does have those. Missing type or
732
* family in the limit means that all types or families are
733
* allowed.
734
*/
735
if (!hastype) {
736
if (limit_has_entry(oldlimits, "type"))
737
return (ENOTCAPABLE);
738
}
739
if (!hasfamily) {
740
if (limit_has_entry(oldlimits, "family"))
741
return (ENOTCAPABLE);
742
}
743
744
return (0);
745
}
746
747
static int
748
dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
749
nvlist_t *nvlout)
750
{
751
int error;
752
753
if (strcmp(cmd, "gethostbyname") == 0)
754
error = dns_gethostbyname(limits, nvlin, nvlout);
755
else if (strcmp(cmd, "gethostbyaddr") == 0)
756
error = dns_gethostbyaddr(limits, nvlin, nvlout);
757
else if (strcmp(cmd, "getnameinfo") == 0)
758
error = dns_getnameinfo(limits, nvlin, nvlout);
759
else if (strcmp(cmd, "getaddrinfo") == 0)
760
error = dns_getaddrinfo(limits, nvlin, nvlout);
761
else
762
error = NO_RECOVERY;
763
764
return (error);
765
}
766
767
CREATE_SERVICE("system.dns", dns_limit, dns_command, 0);
768
769