, 1 April 1990
499 | Moe Ghoul, President of Vice
500 |
501 | That's all there is to it!
502 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | # This file is part of nss-mdns.
2 | #
3 | # nss-mdns is free software; you can redistribute it and/or
4 | # modify it under the terms of the GNU Lesser General Public
5 | # License as published by the Free Software Foundation; either
6 | # version 2.1 of the License, or (at your option) any later version.
7 | #
8 | # This library is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 | # Lesser General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU Lesser General Public
14 | # License along with this library; if not, see .
15 | #
16 | # SPDX-License-Identifier: LGPL-2.1-or-later
17 |
18 | EXTRA_DIST=bootstrap.sh README.md ACKNOWLEDGEMENTS.md NEWS.md LICENSE
19 | ACLOCAL_AMFLAGS=-I m4
20 |
21 | # src
22 | EXTRA_DIST += src/map-file
23 |
24 | AM_CFLAGS = \
25 | -DMDNS_ALLOW_FILE=\"$(MDNS_ALLOW_FILE)\" \
26 | -DAVAHI_SOCKET=\"$(AVAHI_SOCKET)\"
27 |
28 | AM_LDFLAGS=-avoid-version -module -export-dynamic
29 |
30 | if FREEBSD_NSS
31 | lib_LTLIBRARIES = \
32 | nss_mdns.la \
33 | nss_mdns4.la \
34 | nss_mdns6.la \
35 | nss_mdns_minimal.la \
36 | nss_mdns4_minimal.la \
37 | nss_mdns6_minimal.la
38 | else
39 | lib_LTLIBRARIES = \
40 | libnss_mdns.la \
41 | libnss_mdns4.la \
42 | libnss_mdns6.la \
43 | libnss_mdns_minimal.la \
44 | libnss_mdns4_minimal.la \
45 | libnss_mdns6_minimal.la
46 | endif
47 |
48 |
49 | check_PROGRAMS = nss-test avahi-test
50 |
51 | libnss_mdns_la_SOURCES=src/util.c src/util.h src/avahi.c src/avahi.h src/nss.c src/nss.h
52 | libnss_mdns_la_CFLAGS=$(AM_CFLAGS)
53 | libnss_mdns_la_LDFLAGS=$(AM_LDFLAGS) -shrext .so.2 -Wl,-version-script=$(srcdir)/src/map-file
54 |
55 | libnss_mdns_minimal_la_SOURCES=$(libnss_mdns_la_SOURCES)
56 | libnss_mdns_minimal_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DMDNS_MINIMAL
57 | libnss_mdns_minimal_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
58 |
59 | libnss_mdns4_la_SOURCES=$(libnss_mdns_la_SOURCES)
60 | libnss_mdns4_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1
61 | libnss_mdns4_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
62 |
63 | libnss_mdns4_minimal_la_SOURCES=$(libnss_mdns_la_SOURCES)
64 | libnss_mdns4_minimal_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1 -DMDNS_MINIMAL
65 | libnss_mdns4_minimal_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
66 |
67 | libnss_mdns6_la_SOURCES=$(libnss_mdns_la_SOURCES)
68 | libnss_mdns6_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1
69 | libnss_mdns6_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
70 |
71 | libnss_mdns6_minimal_la_SOURCES=$(libnss_mdns_la_SOURCES)
72 | libnss_mdns6_minimal_la_CFLAGS=$(libnss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1 -DMDNS_MINIMAL
73 | libnss_mdns6_minimal_la_LDFLAGS=$(libnss_mdns_la_LDFLAGS)
74 |
75 | nss_mdns_la_SOURCES=$(libnss_mdns_la_SOURCES) src/bsdnss.c
76 | nss_mdns_la_CFLAGS=$(AM_CFLAGS)
77 | nss_mdns_la_LDFLAGS=$(AM_LDFLAGS) -shrext .so.1
78 |
79 | nss_mdns_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
80 | nss_mdns_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DMDNS_MINIMAL
81 | nss_mdns_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
82 |
83 | nss_mdns4_la_SOURCES=$(nss_mdns_la_SOURCES)
84 | nss_mdns4_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1
85 | nss_mdns4_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
86 |
87 | nss_mdns4_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
88 | nss_mdns4_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV4_ONLY=1 -DMDNS_MINIMAL
89 | nss_mdns4_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
90 |
91 | nss_mdns6_la_SOURCES=$(nss_mdns_la_SOURCES)
92 | nss_mdns6_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1
93 | nss_mdns6_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
94 |
95 | nss_mdns6_minimal_la_SOURCES=$(nss_mdns_la_SOURCES)
96 | nss_mdns6_minimal_la_CFLAGS=$(nss_mdns_la_CFLAGS) -DNSS_IPV6_ONLY=1 -DMDNS_MINIMAL
97 | nss_mdns6_minimal_la_LDFLAGS=$(nss_mdns_la_LDFLAGS)
98 |
99 | avahi_test_SOURCES = \
100 | src/avahi.c src/avahi.h \
101 | src/util.c src/util.h \
102 | src/avahi-test.c
103 |
104 | nss_test_SOURCES = \
105 | src/nss-test.c
106 |
107 | install-exec-hook:
108 | rm -f $(DESTDIR)$(libdir)/libnss_mdns.la
109 | rm -f $(DESTDIR)$(libdir)/libnss_mdns_minimal.la
110 | rm -f $(DESTDIR)$(libdir)/libnss_mdns4.la
111 | rm -f $(DESTDIR)$(libdir)/libnss_mdns4_minimal.la
112 | rm -f $(DESTDIR)$(libdir)/libnss_mdns6.la
113 | rm -f $(DESTDIR)$(libdir)/libnss_mdns6_minimal.la
114 | rm -f $(DESTDIR)$(libdir)/nss_mdns.la
115 | rm -f $(DESTDIR)$(libdir)/nss_mdns_minimal.la
116 |
117 | uninstall-hook:
118 | rm -f $(DESTDIR)$(libdir)/libnss_mdns.so.2
119 | rm -f $(DESTDIR)$(libdir)/libnss_mdns_minimal.so.2
120 | rm -f $(DESTDIR)$(libdir)/libnss_mdns4.so.2
121 | rm -f $(DESTDIR)$(libdir)/libnss_mdns4_minimal.so.2
122 | rm -f $(DESTDIR)$(libdir)/libnss_mdns6.so.2
123 | rm -f $(DESTDIR)$(libdir)/libnss_mdns6_minimal.so.2
124 | rm -f $(DESTDIR)$(libdir)/nss_mdns.so.2
125 | rm -f $(DESTDIR)$(libdir)/nss_mdns_minimal.so.2
126 |
127 |
128 | # tests
129 | if ENABLE_TESTS
130 | TESTS = check_util
131 | check_PROGRAMS += check_util
132 | check_util_SOURCES = tests/check_util.c src/util.h
133 | check_util_CFLAGS = @CHECK_CFLAGS@
134 | check_util_LDADD = src/util.o @CHECK_LIBS@
135 | endif
136 |
137 | EXTRA_DIST += tests/check_util.c
138 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # News
2 |
3 | ## Sat Jun 12 2021:
4 |
5 | [Version 0.15.1](https://github.com/lathiat/nss-mdns/releases/tag/v0.15.1)
6 | released. Highlights:
7 |
8 | * This fixes the broken previous release by restoring the missing
9 | `src/nss.h` file. If you are using 0.15, you must upgrade to this
10 | version, or downgrade to a previous one.
11 |
12 | ## Mon May 10 2021:
13 |
14 | [Version 0.15](https://github.com/lathiat/nss-mdns/releases/tag/v0.15)
15 | released. Highlights:
16 |
17 | * Updated README.md for clarity
18 | * The return of BSD support!
19 | * Support for `AVAHI_SOCKET` in `/run` (instead of legacy `/var/run`)
20 |
21 | ## Sun Mar 18 2018:
22 |
23 | [Version 0.14.1](https://github.com/lathiat/nss-mdns/releases/tag/v0.14.1)
24 | released. Highlights:
25 |
26 | * No code changes
27 | * Fix unit tests to properly work on s390x
28 |
29 | ## Sun Mar 18 2018:
30 |
31 | [Version 0.14](https://github.com/lathiat/nss-mdns/releases/tag/v0.14)
32 | released. Highlights:
33 |
34 | * Fix -Wformat-truncation problem during reading of the allow file
35 |
36 | ## Tue Feb 20 2018:
37 |
38 | [Version 0.13.2](https://github.com/lathiat/nss-mdns/releases/tag/v0.13.2)
39 | released. Highlights:
40 |
41 | * No code changes
42 | * Change how `./configure --enable/disable-tests` works:
43 | * `--enable-tests`: tests are enabled and will fail if dependencies are
44 | not found
45 | * `--disable-tests`: tests are not enabled and will not be built even
46 | if dependencies are found
47 | * no flag given: tests are conditionally enabled if dependencies are
48 | found
49 |
50 | ## Sun Feb 18 2018:
51 |
52 | [Version 0.13.1](https://github.com/lathiat/nss-mdns/releases/tag/v0.13.1)
53 | released. Highlights:
54 |
55 | * Very minor code changes (should result in no binary changes)
56 | * Reformat source to 80 columns
57 | * Improve configure options to allow disabling tests even if
58 | the testing libraries are present
59 | * Automake is now non-recursive
60 | * Hardcoded paths are now exposed as configure variables
61 |
62 | ## Mon Feb 12 2018:
63 |
64 | [Version 0.13](https://github.com/lathiat/nss-mdns/releases/tag/v0.13)
65 | released. Highlights:
66 |
67 | * Fix an old memory leak in reverse lookup
68 | * Fix the broken workaround for nscd segfaults (not all clients
69 | would see all results)
70 | * Simplify buffer management
71 | * More unit tests, more cleanups, and fewer gotos
72 |
73 | ## Sat Feb 10 2018:
74 |
75 | [Version 0.12](https://github.com/lathiat/nss-mdns/releases/tag/v0.12)
76 | released. Highlights:
77 |
78 | * Fix segfault when using nscd
79 | * Remove untested, unmaintained BSD support (please help out if you
80 | would like BSD support to return!)
81 |
82 | ## Mon Jan 22 2018:
83 |
84 | [Version 0.11](https://github.com/lathiat/nss-mdns/releases/tag/v0.11)
85 | released. The first release in some time! Highlights:
86 |
87 | * Moved to new GitHub location, docs migrated to markdown
88 | * The long-deprecated `LEGACY` mode is removed
89 | * The long-deprecated `HONOUR_SEARCH_DOMAINS` option is removed
90 | * Unit tests are now included, with `make check`
91 | * nss-mdns now implements [standard
92 | heuristics](https://support.apple.com/en-us/HT201275) for
93 | detecting `.local` unicast resolution and will automatically
94 | disable resolution when a local server responds to `.local` requests
95 | * `_nss_mdns_gethostbyname3_r` and `_nss_mdns_gethostbyname4_r`
96 | are now implemented
97 | * Full dual-stack IPv4/IPv6 support is implemented
98 |
99 | ## Sat May 12 2007:
100 |
101 | [Version 0.10](https://github.com/lathiat/nss-mdns/releases/tag/v0.10)
102 | released. Changes include: Ported to FreeBSD; alignment fixes for SPARC.
103 |
104 | ## Mon Jan 1 2007:
105 |
106 | [Version 0.9](https://github.com/lathiat/nss-mdns/releases/tag/v0.9)
107 | released. Changes include: Make most shared library symbols private to
108 | not conflict with any symbols of the program we're loaded into. Fix a
109 | potential endless loop in the mDNS packet parsing code.
110 |
111 | **Please note that due to security reasons from this release on the
112 | minimal mDNS stack included in `nss-mdns` (dubbed "legacy") is no
113 | longer built by default. Thus, `nss-mdns` will not work unless
114 | [Avahi](http://avahi.org/) is running! That makes Avahi essentially a
115 | hard dependency of `nss-mdns`. Pass `--enable-legacy` to reenable the
116 | mini mDNS stack again. Please note as well that this release does not
117 | honour `/etc/resolv.conf` domain search lists by default anymore. It
118 | created a lot of problems and was never recommended anyway. You may
119 | reenable this functionality by passing `--enable-search-domains`.**
120 |
121 | ## Sat Apr 29 2006:
122 |
123 | [Version 0.8](https://github.com/lathiat/nss-mdns/releases/tag/v0.8)
124 | released. Changes include: Build time option to disable "legacy unicast" mDNS
125 | requests, i.e. resolve exclusively with Avahi; build a special
126 | `_minimal` flavour of the shared objects to minimize
127 | unnecessary name lookup timeouts; fix IPv6 resolving when using
128 | Avahi.
129 |
130 | **Please note that starting with nss-mdns 0.8 we encourage you to use
131 | a different `/etc/nsswitch.conf` configuration line. See below
132 | for more information!**
133 |
134 | ## Sat Nov 19 2005:
135 |
136 | [Version
137 | 0.7](https://github.com/lathiat/nss-mdns/releases/tag/v0.7)
138 | released. Changes include: Portability patch for ARM from Philipp
139 | Zabel; make sure not to print any messages to STDERR; deal with OOM
140 | situations properly; if multiple addresses are assigned to the same
141 | interface make sure to send a query packet only once; other cleanups
142 |
143 | ## Sun Aug 21 2005:
144 |
145 | [Version 0.6](https://github.com/lathiat/nss-mdns/releases/tag/v0.6)
146 | released. Changes include: honour search list in
147 | `/etc/resolv.conf`; try to contact [Avahi](http://avahi.org/) for
148 | resolving.
149 |
150 | ## Sat Jun 4 2005:
151 |
152 | [Version 0.5](https://github.com/lathiat/nss-mdns/releases/tag/v0.5)
153 | released. Changes include: only lookup hostnames ending in
154 | `.local`; add support for a configuration file
155 | (`/etc/mdns.allow`) to allow lookups for other names.
156 |
157 | ## Sun May 15 2005:
158 |
159 | [Version 0.4](https://github.com/lathiat/nss-mdns/releases/tag/v0.4)
160 | released. Changes include: small portability fix for big endian
161 | architectures; send "legacy unicast" packets instead of normal mDNS
162 | packets (this should reduce traffic and improve response time)
163 |
164 | ## Jan Sun 16 2005:
165 |
166 | [Version
167 | 0.3](https://github.com/lathiat/nss-mdns/releases/tag/v0.3)
168 | released. Changes include: add Debianization; use `ip6.arpa` instead
169 | of `ip6.int` for reverse IPv6 lookups.
170 |
171 | ## Fri Dec 17 2004:
172 |
173 | [Version 0.2](https://github.com/lathiat/nss-mdns/releases/tag/v0.2)
174 | released. Changes include: send mDNS queries on every interface that
175 | supports multicasts, instead of only the one with the default route,
176 | making `nss-mdns` more robust on multi-homed hosts; gcc 2.95
177 | compatiblity.
178 |
179 | ## Mon Dec 6 2004:
180 |
181 | [Version 0.1](https://github.com/lathiat/nss-mdns/releases/tag/v0.1)
182 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nss-mdns
2 |
3 | *Copyright 2004-2007 Lennart Poettering <mzaffzqaf (at) 0pointer
4 | (dot) de>*
5 |
6 | - [License](#license)
7 | - [Overview](#overview)
8 | - [Current Status](#current-status)
9 | - [Documentation](#documentation)
10 | - [Requirements](#requirements)
11 | - [Installation](#installation)
12 |
13 | ## License
14 |
15 | This program is free software; you can redistribute it and/or
16 | modify it under the terms of the GNU Lesser General Public License as
17 | published by the Free Software Foundation; either version 2 of the
18 | License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful, but
21 | WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 | Lesser General Public License for more details.
24 |
25 | You should have received a copy of the GNU Lesser General Public License
26 | along with this program; if not, write to the Free Software
27 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 |
29 | ## Overview
30 |
31 | `nss-mdns` is a plugin for the GNU Name Service Switch (NSS)
32 | functionality of the GNU C Library (`glibc`) providing host name
33 | resolution via [Multicast DNS](http://www.multicastdns.org/) (aka
34 | *Zeroconf*, aka *Apple Rendezvous*, aka *Apple Bonjour*), effectively
35 | allowing name resolution by common Unix/Linux programs in the ad-hoc
36 | mDNS domain `.local`.
37 |
38 | `nss-mdns` provides client functionality only, which
39 | means that you have to run a mDNS responder daemon seperately
40 | from `nss-mdns` if you want to register the local host name via
41 | mDNS. I recommend [Avahi](http://avahi.org/).
42 |
43 | `nss-mdns` is very lightweight (9 KByte stripped binary
44 | `.so` compiled with `-DNDEBUG=1 -Os` on i386, `gcc`
45 | 4.0), has no dependencies besides the `glibc` and requires only
46 | minimal configuration.
47 |
48 | `nss-mdns` tries to contact a running
49 | [avahi-daemon](http://avahi.org/) for resolving host names and
50 | addresses and making use of its superior record cacheing. If
51 | Avahi is not available at lookup time, the lookups will fail.
52 |
53 | ## Current Status
54 |
55 | It works!
56 |
57 | ## Documentation
58 |
59 | ### Libraries
60 |
61 | After compiling and installing `nss-mdns` you'll find six
62 | new NSS modules in `/lib`:
63 |
64 | - `libnss_mdns.so.2`
65 | - `libnss_mdns4.so.2`
66 | - `libnss_mdns6.so.2`
67 | - `libnss_mdns_minimal.so.2`
68 | - `libnss_mdns4_minimal.so.2`
69 | - `libnss_mdns6_minimal.so.2`
70 |
71 |
72 | `libnss_mdns.so.2`
73 | resolves both IPv6 and IPv4 addresses, `libnss_mdns4.so.2` only
74 | IPv4 addresses and `libnss_mdns6.so.2` only IPv6 addresses. Due
75 | to the fact that most mDNS responders only register local IPv4
76 | addresses via mDNS, most people will want to use
77 | `libnss_mdns4.so.2` exclusively. Using
78 | `libnss_mdns.so.2` or `libnss_mdns6.so.2` in such a
79 | situation causes long timeouts when resolving hosts since most modern
80 | Unix/Linux applications check for IPv6 addresses first, followed by a
81 | lookup for IPv4.
82 |
83 | `libnss_mdns{4,6,}_minimal.so` (new in version 0.8) is mostly
84 | identical to the versions without `_minimal`. However, they differ in
85 | one way. The minimal versions will always deny to resolve host names
86 | that don't end in `.local` or addresses that aren't in the range
87 | `169.254.x.x` (the range used by
88 | [IPV4LL/APIPA/RFC3927](http://files.zeroconf.org/rfc3927.txt).)
89 | Combining the `_minimal` and the normal NSS modules allows us to make
90 | mDNS authoritative for Zeroconf host names and addresses (and thus
91 | creating no extra burden on DNS servers with always failing requests)
92 | and use it as fallback for everything else.
93 |
94 | ### Activation
95 |
96 | To activate one of the NSS modules you have to edit
97 | `/etc/nsswitch.conf` and add `mdns4` and
98 | `mdns4_minimal` (resp. `mdns`, `mdns6`) to the
99 | line starting with "`hosts:`". On Debian this looks like
100 | this:
101 |
102 | # /etc/nsswitch.conf
103 |
104 | passwd: compat
105 | group: compat
106 | shadow: compat
107 |
108 | hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
109 | networks: files
110 |
111 | protocols: db files
112 | services: db files
113 | ethers: db files
114 | rpc: db files
115 |
116 | netgroup: nis
117 |
118 | That's it. You should now be able to resolve hosts from the
119 | `.local` domain with all your applications. For a quick check
120 | use `glibc`'s `getent` tool:
121 |
122 | $ getent hosts foo.local
123 | 192.168.50.4 foo.local
124 |
125 | Replace *foo* whith a host name that has been registered with
126 | an mDNS responder. (Don't try to use the tools `host` or
127 | `nslookup` for these tests! They bypass the NSS and thus
128 | `nss-mdns` and issue their DNS queries directly.)
129 |
130 | If you run a firewall, don't forget to allow UDP traffic to the the
131 | mDNS multicast address `224.0.0.251` on port 5353.
132 |
133 | **Please note:** The line above makes `nss-mdns` authoritative for the
134 | `.local` domain, unless your unicast DNS server responds to `SOA`
135 | queries for the top level `local` name, or if the request has more
136 | than two labels. (`X.local` might be resolved with `nss-mdns` but
137 | `X.Y.local` will not be.) `nss-mdns` will check `SOA` before every
138 | request to resolve `.local` names, meaning that neither `nss-mdns` nor
139 | `Avahi` need to be disabled to allow `.local` queries to be served
140 | from unicast DNS. (These two checks are only enabled in minimal mode
141 | or if there is no `/etc/mdns.allow` file. Any domain, with any number
142 | of labels, (including `.local`) will still be served authoritatively
143 | from `nss-mdns` if specified in `/etc/mdns.allow`.)
144 |
145 | ### `/etc/mdns.allow`
146 |
147 | `nss-mdns` has a simple configuration file `/etc/mdns.allow` for
148 | enabling name lookups via mDNS in other domains than `.local`.
149 |
150 | > Note: The "minimal" version of `nss-mdns` does not read `/etc/mdns.allow`
151 | > under any circumstances. It behaves as if the file does not exist.
152 |
153 | In the recommended configuration, no `/etc/mdns.allow` file is
154 | present. In this case:
155 |
156 | * If the request does not end with `.local` or `.local.`, it is rejected.
157 | Example: `example.test` is rejected.
158 |
159 | * If the request has more than two labels, it is rejected. Example:
160 | `foo.bar.local` is rejected. **This is the two-label limit heuristic.**
161 |
162 | * If, during a request, the system-configured unicast DNS (specified
163 | in `/etc/resolv.conf`) reports an `SOA` record for the top-level
164 | `local` name, the request is rejected. Example: `host -t SOA local`
165 | returns something other than `Host local not found:
166 | 3(NXDOMAIN)`. **This is the unicast SOA heuristic.**
167 |
168 | * Otherwise, the request is processed.
169 |
170 | If present, the file should contain valid domain suffixes, seperated
171 | by newlines. Empty lines are ignored as are comments starting with
172 | `#`.
173 |
174 | To disable the two heuristics described above, and force all `.local`
175 | domains to be resolved regardless of label count or unicast SOA
176 | records, use this configuration file:
177 |
178 | ```
179 | # /etc/mdns.allow
180 | .local.
181 | .local
182 | ```
183 |
184 | To enable mDNS lookups of all names regardless of the domain suffix
185 | and disabling the two heuristics, add a line consisting of `*` only:
186 |
187 | ```
188 | # /etc/mdns.allow
189 | *
190 | ```
191 |
192 | To complete disable mDNS name lookups, use an empty file:
193 | ```
194 | # /etc/mdns.allow
195 | ```
196 |
197 | Again, remember that changing this file has no effect on the "minimal"
198 | version of `nss-mdns`.
199 |
200 | ## Requirements
201 |
202 | Currently, `nss-mdns` is tested on Linux only. A fairly modern `glibc`
203 | installation with development headers (2.0 or newer) is required. Not
204 | suprisingly `nss-mdns` requires a kernel compiled with IPv4
205 | multicasting support enabled. [Avahi](http://avahi.org/) is a hard
206 | dependency when `nss-mdns` is used, however it is not a build-time
207 | requirement.
208 |
209 | `nss-mdns` was developed and tested on Debian GNU/Linux
210 | "testing" from December 2004, it should work on most other Linux
211 | distributions (and maybe Unix versions) since it uses GNU autoconf and
212 | GNU libtool for source code configuration and shared library
213 | management.
214 |
215 | ## Installation
216 |
217 | As this package is made with the GNU autotools you should run
218 | `./configure` inside the distribution directory for configuring
219 | the source tree. After that you should run `make` for
220 | compilation and `make install` (as root) for installation of
221 | `nss-mdns`.
222 |
--------------------------------------------------------------------------------
/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This file is part of nss-mdns.
4 | #
5 | # nss-mdns is free software; you can redistribute it and/or modify it
6 | # under the terms of the GNU Lesser General Public License as published by
7 | # the Free Software Foundation; either version 2 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # nss-mdns is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | # General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Lesser General Public
16 | # License along with nss-mdns; if not, write to the Free Software
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 |
19 | autoreconf -i -f -v
20 | ./configure --sysconfdir=/etc --localstatedir=/var CFLAGS="-Wall -W -Wextra" "$@"
21 | make clean
22 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | # -*- Autoconf -*-
2 | # Process this file with autoconf to produce a configure script.
3 |
4 | # This file is part of nss-mdns.
5 | #
6 | # nss-mdns is free software; you can redistribute it and/or
7 | # modify it under the terms of the GNU Lesser General Public
8 | # License as published by the Free Software Foundation; either
9 | # version 2.1 of the License, or (at your option) any later version.
10 | #
11 | # This library is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | # Lesser General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU Lesser General Public
17 | # License along with this library; if not, see .
18 | #
19 | # SPDX-License-Identifier: LGPL-2.1-or-later
20 |
21 | AC_PREREQ([2.69])
22 | AC_INIT([nss-mdns],[0.15.1],[https://github.com/lathiat/nss-mdns/issues])
23 | AC_CONFIG_SRCDIR([src/nss.c])
24 | AC_CONFIG_HEADERS([config.h])
25 | AM_INIT_AUTOMAKE([foreign 1.9 -Wall subdir-objects])
26 | AC_CONFIG_MACRO_DIRS([m4])
27 |
28 | AC_SUBST(PACKAGE_URL, [https://github.com/lathiat/nss-mdns])
29 |
30 | AC_PREFIX_DEFAULT([])
31 |
32 | AC_USE_SYSTEM_EXTENSIONS
33 |
34 | AM_SILENT_RULES([yes])
35 |
36 | # Conditionally enable unittests.
37 | AC_ARG_ENABLE([tests],
38 | AS_HELP_STRING([--disable-tests],
39 | [disable building tests]))
40 |
41 | AS_IF([test "x$enable_tests" != "xno"],
42 | [PKG_CHECK_MODULES([CHECK], [check >= 0.11],
43 | [have_check=yes], [have_check=no])],
44 | [have_check=no])
45 |
46 | AS_IF([test "x$have_check" = "xyes"],
47 | [],
48 | [AS_IF([test "x$enable_tests" = "xyes"],
49 | [AC_MSG_ERROR([Cannot enable tests: $CHECK_PKG_ERRORS])
50 | ])
51 | ])
52 |
53 | AM_CONDITIONAL([ENABLE_TESTS], [test "x$have_check" = "xyes"])
54 |
55 | # Options for file locations.
56 | AC_ARG_VAR([AVAHI_SOCKET],
57 | [Full path to the avahi-daemon socket, overriding default])
58 | AS_IF([test "x$AVAHI_SOCKET" = x],
59 | [AVAHI_SOCKET="${runstatedir}/avahi-daemon/socket"])
60 |
61 | AC_ARG_VAR([MDNS_ALLOW_FILE],
62 | [Full path to the mdns.allow file, overriding default])
63 | AS_IF([test "x$MDNS_ALLOW_FILE" = x],
64 | [MDNS_ALLOW_FILE="${sysconfdir}/mdns.allow"])
65 |
66 | # Checks for programs.
67 | AM_PROG_AR
68 | AC_PROG_CC
69 | AC_PROG_CPP
70 | AC_PROG_INSTALL
71 | AC_PROG_LN_S
72 | AC_PROG_MAKE_SET
73 |
74 | # libtool stuff
75 | AC_DISABLE_STATIC
76 | LT_INIT
77 |
78 | # Checks for header files.
79 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h nss.h sys/ioctl.h])
80 |
81 | # Enable C99.
82 | AC_PROG_CC_C99
83 |
84 | # Checks for library functions.
85 | AC_SEARCH_LIBS([__res_nquery], [resolv])
86 | AC_CHECK_FUNCS([gethostbyaddr gethostbyname gettimeofday inet_ntoa memset select socket strcspn strdup strerror strncasecmp strcasecmp strspn])
87 |
88 | # FreeBSD has a slightly different NSS interface
89 | case ${host} in
90 | *-freebsd*) freebsd="yes" ;;
91 | esac
92 |
93 | AM_CONDITIONAL([FREEBSD_NSS], [test "x$freebsd" = "xyes"])
94 |
95 | AC_CONFIG_FILES([Makefile])
96 | AC_OUTPUT
97 |
--------------------------------------------------------------------------------
/pkg/rpm-fedora/nss-mdns.spec:
--------------------------------------------------------------------------------
1 | Name: nss-mdns
2 | Version: 0.15.1
3 | Release: %autorelease
4 | Summary: glibc plugin for .local name resolution
5 |
6 | License: LGPL-2.1+
7 | URL: https://github.com/avahi/nss-mdns
8 | Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz
9 |
10 | BuildRequires: make
11 | BuildRequires: gcc
12 | BuildRequires: pkgconfig(check)
13 | BuildRequires: autoconf
14 | BuildRequires: automake
15 | BuildRequires: libtool
16 | Requires: avahi
17 | Requires(preun,posttrans): authselect
18 |
19 | %description
20 | nss-mdns is a plugin for the GNU Name Service Switch (NSS) functionality of
21 | the GNU C Library (glibc) providing host name resolution via Multicast DNS
22 | (aka Zeroconf, aka Apple Rendezvous, aka Apple Bonjour), effectively allowing
23 | name resolution by common Unix/Linux programs in the ad-hoc mDNS domain .local.
24 |
25 | nss-mdns provides client functionality only, which means that you have to
26 | run a mDNS responder daemon separately from nss-mdns if you want to register
27 | the local host name via mDNS (e.g. Avahi).
28 |
29 |
30 | %prep
31 | %autosetup -p1
32 |
33 | %build
34 | autoreconf -fiv
35 | %configure
36 | %make_build
37 |
38 | %check
39 | %make_build check || (R=$?; cat ./test-suite.log; exit $R)
40 |
41 | %install
42 | %make_install
43 |
44 |
45 | %posttrans
46 | authselect enable-feature with-mdns4 > /dev/null || :
47 |
48 | %preun
49 | authselect disable-feature with-mdns4 > /dev/null || :
50 |
51 | %{?ldconfig_scriptlets}
52 |
53 |
54 | %files
55 | %license LICENSE
56 | %doc README.md NEWS.md ACKNOWLEDGEMENTS.md
57 | %{_libdir}/libnss_mdns*.so.2*
58 |
59 |
60 | %changelog
61 | %autochangelog
62 |
--------------------------------------------------------------------------------
/src/avahi-test.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | #include "avahi.h"
25 |
26 | int main(int argc, char* argv[]) {
27 | query_address_result_t result;
28 | char t[256];
29 | int r;
30 |
31 | if ((r = avahi_resolve_name(AF_INET, argc >= 2 ? argv[1] : "cocaine.local",
32 | &result)) == 0)
33 | printf("AF_INET: %s\n",
34 | inet_ntop(AF_INET, &(result.address.ipv4), t, sizeof(t)));
35 | else
36 | printf("AF_INET: failed (%i).\n", r);
37 |
38 | if ((r = avahi_resolve_address(AF_INET, &(result.address.ipv4), t,
39 | sizeof(t))) == 0)
40 | printf("REVERSE: %s\n", t);
41 | else
42 | printf("REVERSE: failed (%i).\n", r);
43 |
44 | if ((r = avahi_resolve_name(AF_INET6, argc >= 2 ? argv[1] : "cocaine.local",
45 | &result)) == 0)
46 | printf("AF_INET6: %s\n",
47 | inet_ntop(AF_INET6, &(result.address.ipv6), t, sizeof(t)));
48 | else
49 | printf("AF_INET6: failed (%i).\n", r);
50 |
51 | return 0;
52 | }
53 |
--------------------------------------------------------------------------------
/src/avahi.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #ifdef HAVE_CONFIG_H
21 | #include
22 | #endif
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include "avahi.h"
35 | #include "util.h"
36 |
37 | #define WHITESPACE " \t"
38 |
39 | static FILE* open_socket(void) {
40 | int fd = -1;
41 | struct sockaddr_un sa;
42 | FILE* f = NULL;
43 |
44 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
45 | goto fail;
46 |
47 | set_cloexec(fd);
48 |
49 | memset(&sa, 0, sizeof(sa));
50 | sa.sun_family = AF_UNIX;
51 | strncpy(sa.sun_path, AVAHI_SOCKET, sizeof(sa.sun_path) - 1);
52 | sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
53 |
54 | if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
55 | goto fail;
56 |
57 | if (!(f = fdopen(fd, "r+")))
58 | goto fail;
59 |
60 | return f;
61 |
62 | fail:
63 | if (fd >= 0)
64 | close(fd);
65 |
66 | return NULL;
67 | }
68 |
69 | static avahi_resolve_result_t
70 | avahi_resolve_name_with_socket(FILE* f, int af, const char* name,
71 | query_address_result_t* result) {
72 | char* p;
73 | char ln[256];
74 |
75 | fprintf(f, "RESOLVE-HOSTNAME%s %s\n", af == AF_INET ? "-IPV4" : "-IPV6",
76 | name);
77 | fflush(f);
78 |
79 | if (!(fgets(ln, sizeof(ln), f))) {
80 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
81 | }
82 |
83 | if (ln[0] != '+') {
84 | return AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND;
85 | }
86 |
87 | result->af = af;
88 |
89 | p = ln + 1;
90 | p += strspn(p, WHITESPACE);
91 |
92 | /* Store interface number */
93 | result->scopeid = (uint32_t)strtol(p, NULL, 0);
94 | p += strcspn(p, WHITESPACE);
95 | p += strspn(p, WHITESPACE);
96 |
97 | /* Skip protocol */
98 | p += strcspn(p, WHITESPACE);
99 | p += strspn(p, WHITESPACE);
100 |
101 | /* Skip host name */
102 | p += strcspn(p, WHITESPACE);
103 | p += strspn(p, WHITESPACE);
104 |
105 | /* Cut off end of line */
106 | *(p + strcspn(p, "\n\r\t ")) = 0;
107 |
108 | if (inet_pton(af, p, &(result->address)) <= 0) {
109 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
110 | }
111 |
112 | return AVAHI_RESOLVE_RESULT_SUCCESS;
113 | }
114 |
115 | avahi_resolve_result_t avahi_resolve_name(int af, const char* name,
116 | query_address_result_t* result) {
117 | if (af != AF_INET && af != AF_INET6) {
118 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
119 | }
120 |
121 | FILE* f = open_socket();
122 | if (!f) {
123 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
124 | }
125 |
126 | avahi_resolve_result_t ret =
127 | avahi_resolve_name_with_socket(f, af, name, result);
128 | fclose(f);
129 | return ret;
130 | }
131 |
132 | static avahi_resolve_result_t
133 | avahi_resolve_address_with_socket(FILE* f, int af, const void* data, char* name,
134 | size_t name_len) {
135 | char* p;
136 | char a[256], ln[256];
137 |
138 | fprintf(f, "RESOLVE-ADDRESS %s\n", inet_ntop(af, data, a, sizeof(a)));
139 |
140 | if (!(fgets(ln, sizeof(ln), f))) {
141 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
142 | }
143 |
144 | if (ln[0] != '+') {
145 | return AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND;
146 | }
147 |
148 | p = ln + 1;
149 | p += strspn(p, WHITESPACE);
150 |
151 | /* Skip interface */
152 | p += strcspn(p, WHITESPACE);
153 | p += strspn(p, WHITESPACE);
154 |
155 | /* Skip protocol */
156 | p += strcspn(p, WHITESPACE);
157 | p += strspn(p, WHITESPACE);
158 |
159 | /* Cut off end of line */
160 | *(p + strcspn(p, "\n\r\t ")) = 0;
161 |
162 | strncpy(name, p, name_len - 1);
163 | name[name_len - 1] = 0;
164 |
165 | // Success.
166 | return AVAHI_RESOLVE_RESULT_SUCCESS;
167 | }
168 |
169 | avahi_resolve_result_t avahi_resolve_address(int af, const void* data,
170 | char* name, size_t name_len) {
171 | if (af != AF_INET && af != AF_INET6) {
172 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
173 | }
174 |
175 | FILE* f = open_socket();
176 | if (!f) {
177 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
178 | }
179 |
180 | avahi_resolve_result_t ret =
181 | avahi_resolve_address_with_socket(f, af, data, name, name_len);
182 | fclose(f);
183 | return ret;
184 | }
185 |
--------------------------------------------------------------------------------
/src/avahi.h:
--------------------------------------------------------------------------------
1 | #ifndef fooavahihfoo
2 | #define fooavahihfoo
3 |
4 | /*
5 | This file is part of nss-mdns.
6 |
7 | nss-mdns is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, see .
19 |
20 | SPDX-License-Identifier: LGPL-2.1-or-later
21 | */
22 |
23 | #include
24 | #include
25 |
26 | // Maximum number of entries to return.
27 | #define MAX_ENTRIES 16
28 |
29 | typedef struct {
30 | uint32_t address;
31 | } ipv4_address_t;
32 |
33 | typedef struct {
34 | uint8_t address[16];
35 | } ipv6_address_t;
36 |
37 | typedef struct {
38 | int af;
39 | union {
40 | ipv4_address_t ipv4;
41 | ipv6_address_t ipv6;
42 | } address;
43 | uint32_t scopeid;
44 | } query_address_result_t;
45 |
46 | typedef struct {
47 | int count;
48 | query_address_result_t result[MAX_ENTRIES];
49 | } userdata_t;
50 |
51 | typedef enum {
52 | AVAHI_RESOLVE_RESULT_SUCCESS,
53 | AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND,
54 | AVAHI_RESOLVE_RESULT_UNAVAIL
55 | } avahi_resolve_result_t;
56 |
57 | avahi_resolve_result_t avahi_resolve_name(int af, const char* name,
58 | query_address_result_t* result);
59 |
60 | avahi_resolve_result_t avahi_resolve_address(int af, const void* data,
61 | char* name, size_t name_len);
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/src/bsdnss.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | /* Original author: Bruce M. Simpson */
21 |
22 | #ifdef HAVE_CONFIG_H
23 | #include
24 | #endif
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | #include
44 | #include
45 |
46 | #include "avahi.h"
47 | #include "config.h"
48 | #include "util.h"
49 | #include "nss.h"
50 |
51 | #ifdef MDNS_MINIMAL
52 | /*
53 | * FreeBSD support prefers Avahi.
54 | */
55 | #endif
56 |
57 | /*
58 | * To turn on utrace() records, compile with -DDEBUG_UTRACE.
59 | */
60 | #ifdef DEBUG_UTRACE
61 | #define _NSS_UTRACE(msg) \
62 | do { \
63 | static const char __msg[] = msg; \
64 | (void)utrace(__msg, sizeof(__msg)); \
65 | } while (0)
66 | #else
67 | #define _NSS_UTRACE(msg)
68 | #endif
69 |
70 | ns_mtab* nss_module_register(const char* source, unsigned int* mtabsize,
71 | nss_module_unregister_fn* unreg);
72 |
73 | typedef enum nss_status (*_bsd_nsstub_fn_t)(const char*, struct hostent*, char*,
74 | size_t, int*, int*);
75 |
76 | static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_getaddrinfo);
77 | static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyaddr_r);
78 | static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_gethostbyname2_r);
79 | static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_ghbyaddr);
80 | static NSS_METHOD_PROTOTYPE(__nss_bsdcompat_ghbyname);
81 |
82 | static ns_mtab methods[] = {
83 | /* database, name, method, mdata */
84 | {NSDB_HOSTS, "getaddrinfo", __nss_bsdcompat_getaddrinfo, NULL},
85 | {NSDB_HOSTS, "gethostbyaddr_r", __nss_bsdcompat_gethostbyaddr_r, NULL},
86 | {NSDB_HOSTS, "gethostbyname2_r", __nss_bsdcompat_gethostbyname2_r, NULL},
87 | {NSDB_HOSTS, "ghbyaddr", __nss_bsdcompat_ghbyaddr, NULL},
88 | {NSDB_HOSTS, "ghbyname", __nss_bsdcompat_ghbyname, NULL},
89 | };
90 |
91 | ns_mtab* nss_module_register(const char* source, unsigned int* mtabsize,
92 | nss_module_unregister_fn* unreg) {
93 |
94 | *mtabsize = sizeof(methods) / sizeof(methods[0]);
95 | *unreg = NULL;
96 | return (methods);
97 | }
98 |
99 | /*
100 | * Calling convention:
101 | * ap: const char *name (optional), struct addrinfo *pai (hints, optional)
102 | * retval: struct addrinfo **
103 | *
104 | * name must always be specified by libc; pai is allocated
105 | * by libc and must always be specified.
106 | *
107 | * We can malloc() addrinfo instances and hang them off ai->next;
108 | * canonnames may also be malloc()'d.
109 | * libc is responsible for mapping our ns error return to gai_strerror().
110 | *
111 | * libc calls us only to look up qualified hostnames. We don't need to
112 | * worry about port numbers; libc will call getservbyname() and explore
113 | * the appropriate maps configured in nsswitch.conf(5).
114 | *
115 | * _errno and _h_errno are unused by getaddrinfo(), as it is
116 | * [mostly] OS independent interface implemented by Win32.
117 | */
118 | static int __nss_bsdcompat_getaddrinfo(void* retval, void* mdata __unused,
119 | va_list ap) {
120 | enum nss_status status;
121 | int _errno = 0;
122 | int _h_errno = 0;
123 | struct addrinfo sentinel = {0}, *curp = &sentinel;
124 | const char* name;
125 | const struct addrinfo* pai;
126 | struct addrinfo** resultp;
127 | userdata_t u;
128 |
129 | _NSS_UTRACE("__nss_bsdcompat_getaddrinfo: called");
130 |
131 | name = va_arg(ap, const char*);
132 | pai = va_arg(ap, struct addrinfo*);
133 | resultp = (struct addrinfo**)retval;
134 |
135 | if (name == NULL || pai == NULL) {
136 | *resultp = NULL;
137 | return (NS_UNAVAIL);
138 | }
139 |
140 | extern enum nss_status _nss_mdns_gethostbyname_impl(
141 | const char* name, int af, userdata_t* u, int* errnop, int* h_errnop);
142 |
143 | status = _nss_mdns_gethostbyname_impl(name, pai->ai_family, &u, &_errno,
144 | &_h_errno);
145 | status = __nss_compat_result(status, _errno);
146 | if (status != NS_SUCCESS) {
147 | return (status);
148 | }
149 |
150 | for (int i = 0; i < u.count; i++) {
151 | struct addrinfo* ai = (struct addrinfo*)malloc(
152 | sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
153 | if (ai == NULL) {
154 | if (sentinel.ai_next != NULL)
155 | freeaddrinfo(sentinel.ai_next);
156 | *resultp = NULL;
157 | return (NS_UNAVAIL);
158 | }
159 | struct sockaddr* psa = (struct sockaddr*)(ai + 1);
160 |
161 | memset(ai, 0, sizeof(struct addrinfo));
162 | ai->ai_flags = pai->ai_flags;
163 | ai->ai_socktype = pai->ai_socktype;
164 | ai->ai_protocol = pai->ai_protocol;
165 | ai->ai_family = u.result[i].af;
166 | memset(psa, 0, sizeof(struct sockaddr_storage));
167 | psa->sa_len = ai->ai_addrlen;
168 | psa->sa_family = ai->ai_family;
169 | ai->ai_addr = psa;
170 | switch (ai->ai_family) {
171 | case AF_INET:
172 | ai->ai_addrlen = sizeof(struct sockaddr_in);
173 | memcpy(&((struct sockaddr_in*)psa)->sin_addr, &u.result[i].address,
174 | ai->ai_addrlen);
175 | break;
176 | case AF_INET6:
177 | ai->ai_addrlen = sizeof(struct sockaddr_in6);
178 | memcpy(&((struct sockaddr_in6*)psa)->sin6_addr,
179 | &u.result[i].address, ai->ai_addrlen);
180 | break;
181 | default:
182 | ai->ai_addrlen = sizeof(struct sockaddr_storage);
183 | memcpy(psa->sa_data, &u.result[i].address, ai->ai_addrlen);
184 | }
185 |
186 | curp->ai_next = ai;
187 | curp = ai;
188 | }
189 |
190 | *resultp = sentinel.ai_next;
191 | return (status);
192 | }
193 |
194 | /*
195 | * Calling convention:
196 | * ap: const u_char *uaddr, socklen_t len, int af, struct hostent *hp,
197 | * char *buf, size_t buflen, int ret_errno, int *h_errnop
198 | * retval: should be set to NULL or hp passed in
199 | */
200 | static int __nss_bsdcompat_gethostbyaddr_r(void* retval, void* mdata __unused,
201 | va_list ap) {
202 | void* addr;
203 | char* buf;
204 | int* h_errnop;
205 | struct hostent* hp;
206 | struct hostent** resultp;
207 | int af;
208 | size_t buflen;
209 | int len;
210 | int ret_errno;
211 | enum nss_status status;
212 |
213 | addr = va_arg(ap, void*);
214 | len = va_arg(ap, socklen_t);
215 | af = va_arg(ap, int);
216 | hp = va_arg(ap, struct hostent*);
217 | buf = va_arg(ap, char*);
218 | buflen = va_arg(ap, size_t);
219 | ret_errno = va_arg(ap, int);
220 | h_errnop = va_arg(ap, int*);
221 | resultp = (struct hostent**)retval;
222 |
223 | *resultp = NULL;
224 | status = _nss_mdns_gethostbyaddr_r(addr, len, af, hp, buf, buflen,
225 | &ret_errno, h_errnop);
226 |
227 | status = __nss_compat_result(status, *h_errnop);
228 | if (status == NS_SUCCESS)
229 | *resultp = hp;
230 | return (status);
231 | }
232 |
233 | /*
234 | * Calling convention:
235 | * ap: const char *name, int af, struct hostent *hp, char *buf,
236 | * size_t buflen, int ret_errno, int *h_errnop
237 | * retval is a struct hostent **result passed in by the libc client,
238 | * which is responsible for allocating storage.
239 | */
240 | static int __nss_bsdcompat_gethostbyname2_r(void* retval, void* mdata __unused,
241 | va_list ap) {
242 | char* buf;
243 | const char* name;
244 | int* h_errnop;
245 | struct hostent* hp;
246 | struct hostent** resultp;
247 | int af;
248 | size_t buflen;
249 | int ret_errno;
250 | enum nss_status status;
251 |
252 | name = va_arg(ap, char*);
253 | af = va_arg(ap, int);
254 | hp = va_arg(ap, struct hostent*);
255 | buf = va_arg(ap, char*);
256 | buflen = va_arg(ap, size_t);
257 | ret_errno = va_arg(ap, int);
258 | h_errnop = va_arg(ap, int*);
259 | resultp = (struct hostent**)retval;
260 |
261 | *resultp = NULL;
262 | if (hp == NULL)
263 | return (NS_UNAVAIL);
264 |
265 | status = _nss_mdns_gethostbyname2_r(name, af, hp, buf, buflen, &ret_errno,
266 | h_errnop);
267 |
268 | status = __nss_compat_result(status, *h_errnop);
269 | if (status == NS_SUCCESS)
270 | *resultp = hp;
271 | return (status);
272 | }
273 |
274 | /*
275 | * Used by getipnodebyaddr(3).
276 | *
277 | * Calling convention:
278 | * ap: struct in[6]_addr *src, size_t len, int af, int *errp
279 | * retval: pointer to a pointer to an uninitialized struct hostent,
280 | * in which should be returned a single pointer to on-heap storage.
281 | *
282 | * This function is responsible for allocating on-heap storage.
283 | * The caller is responsible for calling freehostent() on the returned
284 | * storage.
285 | */
286 | static int __nss_bsdcompat_ghbyaddr(void* retval, void* mdata __unused,
287 | va_list ap) {
288 | char* buffer;
289 | void* bufp;
290 | int* errp;
291 | struct hostent* hp;
292 | struct hostent** resultp;
293 | void* src;
294 | int af;
295 | size_t buflen = 1024;
296 | size_t len;
297 | int h_errnop;
298 | enum nss_status status;
299 |
300 | src = va_arg(ap, void*);
301 | len = va_arg(ap, size_t);
302 | af = va_arg(ap, int);
303 | errp = va_arg(ap, int*);
304 | resultp = (struct hostent**)retval;
305 |
306 | _NSS_UTRACE("__nss_bsdcompat_ghbyaddr: called");
307 |
308 | bufp = malloc((sizeof(struct hostent) + buflen));
309 | if (bufp == NULL) {
310 | *resultp = NULL;
311 | return (NS_UNAVAIL);
312 | }
313 | hp = (struct hostent*)bufp;
314 | buffer = (char*)(hp + 1);
315 |
316 | status = _nss_mdns_gethostbyaddr_r(src, len, af, hp, buffer, buflen, errp,
317 | &h_errnop);
318 |
319 | status = __nss_compat_result(status, *errp);
320 | if (status != NS_SUCCESS) {
321 | free(bufp);
322 | hp = NULL;
323 | }
324 | *resultp = hp;
325 | return (status);
326 | }
327 |
328 | /*
329 | * Used by getipnodebyname(3).
330 | *
331 | * Calling convention:
332 | * ap: const char *name, int af, int *errp
333 | * retval: pointer to a pointer to an uninitialized struct hostent.
334 | *
335 | * This function is responsible for allocating on-heap storage.
336 | * The caller is responsible for calling freehostent() on the returned
337 | * storage.
338 | */
339 | static int __nss_bsdcompat_ghbyname(void* retval, void* mdata __unused,
340 | va_list ap) {
341 | char* buffer;
342 | void* bufp;
343 | int* errp;
344 | struct hostent* hp;
345 | struct hostent** resultp;
346 | char* name;
347 | int af;
348 | size_t buflen = 1024;
349 | int h_errnop;
350 | enum nss_status status;
351 |
352 | name = va_arg(ap, char*);
353 | af = va_arg(ap, int);
354 | errp = va_arg(ap, int*);
355 | resultp = (struct hostent**)retval;
356 |
357 | bufp = malloc((sizeof(struct hostent) + buflen));
358 | if (bufp == NULL) {
359 | *resultp = NULL;
360 | return (NS_UNAVAIL);
361 | }
362 | hp = (struct hostent*)bufp;
363 | buffer = (char*)(hp + 1);
364 |
365 | status =
366 | _nss_mdns_gethostbyname_r(name, hp, buffer, buflen, errp, &h_errnop);
367 |
368 | status = __nss_compat_result(status, *errp);
369 | if (status != NS_SUCCESS) {
370 | free(bufp);
371 | hp = NULL;
372 | }
373 | *resultp = hp;
374 | return (status);
375 | }
376 |
--------------------------------------------------------------------------------
/src/map-file:
--------------------------------------------------------------------------------
1 | NSSMDNS_0 {
2 | global:
3 |
4 | _nss_mdns_gethostbyaddr_r;
5 | _nss_mdns4_gethostbyaddr_r;
6 | _nss_mdns6_gethostbyaddr_r;
7 | _nss_mdns_minimal_gethostbyaddr_r;
8 | _nss_mdns4_minimal_gethostbyaddr_r;
9 | _nss_mdns6_minimal_gethostbyaddr_r;
10 |
11 | _nss_mdns_gethostbyname_r;
12 | _nss_mdns4_gethostbyname_r;
13 | _nss_mdns6_gethostbyname_r;
14 | _nss_mdns_minimal_gethostbyname_r;
15 | _nss_mdns4_minimal_gethostbyname_r;
16 | _nss_mdns6_minimal_gethostbyname_r;
17 |
18 | _nss_mdns_gethostbyname2_r;
19 | _nss_mdns4_gethostbyname2_r;
20 | _nss_mdns6_gethostbyname2_r;
21 | _nss_mdns_minimal_gethostbyname2_r;
22 | _nss_mdns4_minimal_gethostbyname2_r;
23 | _nss_mdns6_minimal_gethostbyname2_r;
24 |
25 | _nss_mdns_gethostbyname3_r;
26 | _nss_mdns4_gethostbyname3_r;
27 | _nss_mdns6_gethostbyname3_r;
28 | _nss_mdns_minimal_gethostbyname3_r;
29 | _nss_mdns4_minimal_gethostbyname3_r;
30 | _nss_mdns6_minimal_gethostbyname3_r;
31 |
32 | _nss_mdns_gethostbyname4_r;
33 | _nss_mdns4_gethostbyname4_r;
34 | _nss_mdns6_gethostbyname4_r;
35 | _nss_mdns_minimal_gethostbyname4_r;
36 | _nss_mdns4_minimal_gethostbyname4_r;
37 | _nss_mdns6_minimal_gethostbyname4_r;
38 |
39 | local:
40 | *;
41 | };
42 |
--------------------------------------------------------------------------------
/src/nss-test.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #ifdef HAVE_CONFIG_H
21 | #include
22 | #endif
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #ifdef __FreeBSD__
31 | #include
32 | #endif
33 |
34 | static int gai(const char* node) {
35 | struct addrinfo hints;
36 | struct addrinfo *result, *rp;
37 |
38 | char str[INET_ADDRSTRLEN];
39 | char str6[INET6_ADDRSTRLEN];
40 |
41 | memset(&hints, 0, sizeof(struct addrinfo));
42 | hints.ai_family = AF_UNSPEC;
43 | hints.ai_flags = AI_CANONNAME;
44 | hints.ai_socktype = SOCK_STREAM;
45 |
46 | fprintf(stderr, "* doing node lookup with getaddrinfo...\n");
47 |
48 | int s = getaddrinfo(node, NULL, &hints, &result);
49 | if (s != 0) {
50 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
51 | return 1;
52 | }
53 | int i = 0;
54 | for (rp = result; rp != NULL; rp = rp->ai_next) {
55 | if (rp->ai_canonname) {
56 | fprintf(stderr, "[%d] official name: %s\n", i, rp->ai_canonname);
57 | }
58 | switch (rp->ai_family) {
59 | case AF_INET:
60 | inet_ntop(AF_INET, &((struct sockaddr_in*)rp->ai_addr)->sin_addr,
61 | str, sizeof(str));
62 | fprintf(stderr, "[%d] addr type: inet\n[%d] address: %s\n", i, i,
63 | str);
64 | break;
65 | case AF_INET6:
66 | inet_ntop(AF_INET6, &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr,
67 | str6, sizeof(str6));
68 | int scope_id = ((struct sockaddr_in6*)rp->ai_addr)->sin6_scope_id;
69 | if (scope_id) {
70 | fprintf(stderr, "[%d] addr type: inet6\n[%d] address: %s%%%d\n",
71 | i, i, str6, scope_id);
72 | } else {
73 | fprintf(stderr, "[%d] addr type: inet6\n[%d] address: %s\n", i,
74 | i, str6);
75 | }
76 | break;
77 | }
78 | fprintf(stderr, "\n");
79 | i++;
80 | }
81 | freeaddrinfo(result);
82 |
83 | return 0;
84 | }
85 |
86 | static int gethostbyX(const char* node) {
87 | struct hostent* he;
88 | in_addr_t** a;
89 | uint8_t t[256];
90 |
91 | if (inet_pton(AF_INET, node, &t) > 0) {
92 | fprintf(stderr, "* doing ipv4 lookup with gethostbyaddr...\n");
93 | he = gethostbyaddr(t, 4, AF_INET);
94 | } else if (inet_pton(AF_INET6, node, &t) > 0) {
95 | fprintf(stderr, "* doing ipv6 lookup with gethostbyaddr...\n");
96 | he = gethostbyaddr(t, 16, AF_INET6);
97 | } else {
98 | fprintf(stderr, "* doing name lookup with gethostbyname...\n");
99 | he = gethostbyname(node);
100 | }
101 |
102 | if (!he) {
103 | fprintf(stderr, "lookup failed\n");
104 | return 1;
105 | }
106 |
107 | fprintf(stderr, "official name: %s\n", he->h_name);
108 |
109 | if (!he->h_aliases || !he->h_aliases[0])
110 | fprintf(stderr, "no aliases\n");
111 | else {
112 | char** h;
113 | fprintf(stderr, "aliases:");
114 | for (h = he->h_aliases; *h; h++)
115 | fprintf(stderr, " %s", *h);
116 | fprintf(stderr, "\n");
117 | }
118 |
119 | fprintf(stderr, "addr type: %s\n",
120 | he->h_addrtype == AF_INET
121 | ? "inet"
122 | : (he->h_addrtype == AF_INET6 ? "inet6" : NULL));
123 | fprintf(stderr, "addr length: %i\n", he->h_length);
124 |
125 | fprintf(stderr, "addresses:");
126 | for (a = (in_addr_t**)he->h_addr_list; *a; a++) {
127 | char txt[256];
128 | fprintf(stderr, " %s", inet_ntop(he->h_addrtype, *a, txt, sizeof(txt)));
129 | }
130 | fprintf(stderr, "\n");
131 |
132 | return 0;
133 | }
134 |
135 | int main(int argc, char* argv[]) {
136 | if (argc != 2) {
137 | fprintf(stderr,
138 | "Requires 1 argument: either a host or numeric address\n");
139 | return 1;
140 | }
141 | const char* node = argv[1];
142 | gethostbyX(node);
143 | fprintf(stderr, "\n\n");
144 | gai(node);
145 | return 0;
146 | }
147 |
--------------------------------------------------------------------------------
/src/nss.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #ifdef HAVE_CONFIG_H
21 | #include
22 | #endif
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include "avahi.h"
36 | #include "util.h"
37 | #include "nss.h"
38 |
39 | static avahi_resolve_result_t do_avahi_resolve_name(int af, const char* name,
40 | userdata_t* userdata) {
41 | bool ipv4_found = false;
42 | bool ipv6_found = false;
43 |
44 | if (af == AF_INET || af == AF_UNSPEC) {
45 | query_address_result_t address_result;
46 | switch (avahi_resolve_name(AF_INET, name, &address_result)) {
47 | case AVAHI_RESOLVE_RESULT_SUCCESS:
48 | append_address_to_userdata(&address_result, userdata);
49 | ipv4_found = true;
50 | break;
51 |
52 | case AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND:
53 | break;
54 |
55 | case AVAHI_RESOLVE_RESULT_UNAVAIL:
56 | // Something went wrong, just fail.
57 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
58 | }
59 | }
60 |
61 | if (af == AF_INET6 || af == AF_UNSPEC) {
62 | query_address_result_t address_result;
63 | switch (avahi_resolve_name(AF_INET6, name, &address_result)) {
64 | case AVAHI_RESOLVE_RESULT_SUCCESS:
65 | append_address_to_userdata(&address_result, userdata);
66 | ipv6_found = true;
67 | break;
68 |
69 | case AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND:
70 | break;
71 |
72 | case AVAHI_RESOLVE_RESULT_UNAVAIL:
73 | // Something went wrong, just fail.
74 | return AVAHI_RESOLVE_RESULT_UNAVAIL;
75 | }
76 | }
77 |
78 | if (ipv4_found || ipv6_found) {
79 | return AVAHI_RESOLVE_RESULT_SUCCESS;
80 | } else {
81 | return AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND;
82 | }
83 | }
84 |
85 | enum nss_status _nss_mdns_gethostbyname_impl(const char* name, int af,
86 | userdata_t* u, int* errnop,
87 | int* h_errnop) {
88 |
89 | FILE* mdns_allow_file = NULL;
90 | use_name_result_t result;
91 |
92 | #ifdef NSS_IPV4_ONLY
93 | if (af == AF_UNSPEC) {
94 | af = AF_INET;
95 | }
96 | #endif
97 |
98 | #ifdef NSS_IPV6_ONLY
99 | if (af == AF_UNSPEC) {
100 | af = AF_INET6;
101 | }
102 | #endif
103 |
104 | #ifdef NSS_IPV4_ONLY
105 | if (af != AF_INET)
106 | #elif NSS_IPV6_ONLY
107 | if (af != AF_INET6)
108 | #else
109 | if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
110 | #endif
111 | {
112 | *errnop = EINVAL;
113 | *h_errnop = NO_RECOVERY;
114 | return NSS_STATUS_UNAVAIL;
115 | }
116 |
117 | u->count = 0;
118 |
119 | #ifndef MDNS_MINIMAL
120 | mdns_allow_file = fopen(MDNS_ALLOW_FILE, "r");
121 | #endif
122 | result = verify_name_allowed_with_soa(name, mdns_allow_file,
123 | TEST_LOCAL_SOA_AUTO);
124 | #ifndef MDNS_MINIMAL
125 | if (mdns_allow_file)
126 | fclose(mdns_allow_file);
127 | #endif
128 |
129 | if (result == USE_NAME_RESULT_SKIP) {
130 | *errnop = EINVAL;
131 | *h_errnop = NO_RECOVERY;
132 | return NSS_STATUS_UNAVAIL;
133 | }
134 |
135 | switch (do_avahi_resolve_name(af, name, u)) {
136 | case AVAHI_RESOLVE_RESULT_SUCCESS:
137 | return NSS_STATUS_SUCCESS;
138 |
139 | case AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND:
140 | *errnop = ETIMEDOUT;
141 | *h_errnop = HOST_NOT_FOUND;
142 | if (result == USE_NAME_RESULT_OPTIONAL) {
143 | /* continue to dns plugin if DNS .local zone is detected. */
144 | *h_errnop = TRY_AGAIN;
145 | return NSS_STATUS_UNAVAIL;
146 | }
147 | return NSS_STATUS_NOTFOUND;
148 |
149 | case AVAHI_RESOLVE_RESULT_UNAVAIL:
150 | default:
151 | *errnop = ETIMEDOUT;
152 | *h_errnop = NO_RECOVERY;
153 | return NSS_STATUS_UNAVAIL;
154 | }
155 | }
156 |
157 | #ifndef __FreeBSD__
158 | enum nss_status _nss_mdns_gethostbyname4_r(const char* name,
159 | struct gaih_addrtuple** pat,
160 | char* buffer, size_t buflen,
161 | int* errnop, int* h_errnop,
162 | int32_t* ttlp) {
163 |
164 | (void)ttlp;
165 |
166 | userdata_t u;
167 | buffer_t buf;
168 |
169 | enum nss_status status =
170 | _nss_mdns_gethostbyname_impl(name, AF_UNSPEC, &u, errnop, h_errnop);
171 | if (status != NSS_STATUS_SUCCESS) {
172 | return status;
173 | }
174 | buffer_init(&buf, buffer, buflen);
175 | return convert_userdata_to_addrtuple(&u, name, pat, &buf, errnop, h_errnop);
176 | }
177 | #endif
178 |
179 | enum nss_status _nss_mdns_gethostbyname3_r(const char* name, int af,
180 | struct hostent* result, char* buffer,
181 | size_t buflen, int* errnop,
182 | int* h_errnop, int32_t* ttlp,
183 | char** canonp) {
184 |
185 | (void)ttlp;
186 | (void)canonp;
187 |
188 | buffer_t buf;
189 | userdata_t u;
190 |
191 | // The interfaces for gethostbyname3_r and below do not actually support
192 | // returning results for more than one address family
193 | if (af == AF_UNSPEC) {
194 | #ifdef NSS_IPV6_ONLY
195 | af = AF_INET6;
196 | #else
197 | af = AF_INET;
198 | #endif
199 | }
200 |
201 | enum nss_status status = _nss_mdns_gethostbyname_impl(name, af, &u, errnop, h_errnop);
202 | if (status != NSS_STATUS_SUCCESS) {
203 | return status;
204 | }
205 | buffer_init(&buf, buffer, buflen);
206 | return convert_userdata_for_name_to_hostent(&u, name, af, result, &buf,
207 | errnop, h_errnop);
208 | }
209 |
210 | enum nss_status _nss_mdns_gethostbyname2_r(const char* name, int af,
211 | struct hostent* result, char* buffer,
212 | size_t buflen, int* errnop,
213 | int* h_errnop) {
214 |
215 | return _nss_mdns_gethostbyname3_r(name, af, result, buffer, buflen, errnop,
216 | h_errnop, NULL, NULL);
217 | }
218 |
219 | enum nss_status _nss_mdns_gethostbyname_r(const char* name,
220 | struct hostent* result, char* buffer,
221 | size_t buflen, int* errnop,
222 | int* h_errnop) {
223 |
224 | return _nss_mdns_gethostbyname2_r(name, AF_UNSPEC, result, buffer, buflen,
225 | errnop, h_errnop);
226 | }
227 |
228 | enum nss_status _nss_mdns_gethostbyaddr_r(const void* addr, int len, int af,
229 | struct hostent* result, char* buffer,
230 | size_t buflen, int* errnop,
231 | int* h_errnop) {
232 |
233 | size_t address_length;
234 | char t[256];
235 |
236 | /* Check for address types */
237 | address_length =
238 | af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t);
239 |
240 | if (len < (int)address_length ||
241 | #ifdef NSS_IPV4_ONLY
242 | af != AF_INET
243 | #elif NSS_IPV6_ONLY
244 | af != AF_INET6
245 | #else
246 | (af != AF_INET && af != AF_INET6)
247 | #endif
248 | ) {
249 | *errnop = EINVAL;
250 | *h_errnop = NO_RECOVERY;
251 | return NSS_STATUS_UNAVAIL;
252 | }
253 |
254 | #ifdef MDNS_MINIMAL
255 | /* Only query for 169.254.0.0/16 IPv4 in minimal mode */
256 | if ((af == AF_INET &&
257 | ((ntohl(*(const uint32_t*)addr) & 0xFFFF0000UL) != 0xA9FE0000UL)) ||
258 | (af == AF_INET6 && !(((const uint8_t*)addr)[0] == 0xFE &&
259 | (((const uint8_t*)addr)[1] >> 6) == 2))) {
260 | *errnop = EINVAL;
261 | *h_errnop = NO_RECOVERY;
262 | return NSS_STATUS_UNAVAIL;
263 | }
264 | #endif
265 |
266 | /* Lookup using Avahi */
267 | buffer_t buf;
268 | switch (avahi_resolve_address(af, addr, t, sizeof(t))) {
269 | case AVAHI_RESOLVE_RESULT_SUCCESS:
270 | buffer_init(&buf, buffer, buflen);
271 | return convert_name_and_addr_to_hostent(t, addr, address_length, af,
272 | result, &buf, errnop, h_errnop);
273 |
274 | case AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND:
275 | *errnop = ETIMEDOUT;
276 | *h_errnop = HOST_NOT_FOUND;
277 | return NSS_STATUS_NOTFOUND;
278 |
279 | case AVAHI_RESOLVE_RESULT_UNAVAIL:
280 | default:
281 | *errnop = ETIMEDOUT;
282 | *h_errnop = NO_RECOVERY;
283 | return NSS_STATUS_UNAVAIL;
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/nss.h:
--------------------------------------------------------------------------------
1 | #ifndef src_nss_h
2 | #define src_nss_h
3 |
4 | /*
5 | nss-mdns is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, see .
17 |
18 | SPDX-License-Identifier: LGPL-2.1-or-later
19 | */
20 |
21 | #if defined(NSS_IPV4_ONLY) && !defined(MDNS_MINIMAL)
22 | #define _nss_mdns_gethostbyname4_r _nss_mdns4_gethostbyname4_r
23 | #define _nss_mdns_gethostbyname3_r _nss_mdns4_gethostbyname3_r
24 | #define _nss_mdns_gethostbyname2_r _nss_mdns4_gethostbyname2_r
25 | #define _nss_mdns_gethostbyname_r _nss_mdns4_gethostbyname_r
26 | #define _nss_mdns_gethostbyaddr_r _nss_mdns4_gethostbyaddr_r
27 | #elif defined(NSS_IPV4_ONLY) && defined(MDNS_MINIMAL)
28 | #define _nss_mdns_gethostbyname4_r _nss_mdns4_minimal_gethostbyname4_r
29 | #define _nss_mdns_gethostbyname3_r _nss_mdns4_minimal_gethostbyname3_r
30 | #define _nss_mdns_gethostbyname2_r _nss_mdns4_minimal_gethostbyname2_r
31 | #define _nss_mdns_gethostbyname_r _nss_mdns4_minimal_gethostbyname_r
32 | #define _nss_mdns_gethostbyaddr_r _nss_mdns4_minimal_gethostbyaddr_r
33 | #elif defined(NSS_IPV6_ONLY) && !defined(MDNS_MINIMAL)
34 | #define _nss_mdns_gethostbyname4_r _nss_mdns6_gethostbyname4_r
35 | #define _nss_mdns_gethostbyname3_r _nss_mdns6_gethostbyname3_r
36 | #define _nss_mdns_gethostbyname2_r _nss_mdns6_gethostbyname2_r
37 | #define _nss_mdns_gethostbyname_r _nss_mdns6_gethostbyname_r
38 | #define _nss_mdns_gethostbyaddr_r _nss_mdns6_gethostbyaddr_r
39 | #elif defined(NSS_IPV6_ONLY) && defined(MDNS_MINIMAL)
40 | #define _nss_mdns_gethostbyname4_r _nss_mdns6_minimal_gethostbyname4_r
41 | #define _nss_mdns_gethostbyname3_r _nss_mdns6_minimal_gethostbyname3_r
42 | #define _nss_mdns_gethostbyname2_r _nss_mdns6_minimal_gethostbyname2_r
43 | #define _nss_mdns_gethostbyname_r _nss_mdns6_minimal_gethostbyname_r
44 | #define _nss_mdns_gethostbyaddr_r _nss_mdns6_minimal_gethostbyaddr_r
45 | #elif defined(MDNS_MINIMAL)
46 | #define _nss_mdns_gethostbyname4_r _nss_mdns_minimal_gethostbyname4_r
47 | #define _nss_mdns_gethostbyname3_r _nss_mdns_minimal_gethostbyname3_r
48 | #define _nss_mdns_gethostbyname2_r _nss_mdns_minimal_gethostbyname2_r
49 | #define _nss_mdns_gethostbyname_r _nss_mdns_minimal_gethostbyname_r
50 | #define _nss_mdns_gethostbyaddr_r _nss_mdns_minimal_gethostbyaddr_r
51 | #endif
52 |
53 | // Define prototypes for nss function we're going to export (fixes GCC warnings)
54 | #ifndef __FreeBSD__
55 | enum nss_status _nss_mdns_gethostbyname4_r(const char*, struct gaih_addrtuple**,
56 | char*, size_t, int*, int*, int32_t*);
57 | #endif
58 | enum nss_status _nss_mdns_gethostbyname3_r(const char*, int, struct hostent*,
59 | char*, size_t, int*, int*, int32_t*,
60 | char**);
61 | enum nss_status _nss_mdns_gethostbyname2_r(const char*, int, struct hostent*,
62 | char*, size_t, int*, int*);
63 | enum nss_status _nss_mdns_gethostbyname_r(const char*, struct hostent*, char*,
64 | size_t, int*, int*);
65 | enum nss_status _nss_mdns_gethostbyaddr_r(const void*, int, int,
66 | struct hostent*, char*, size_t, int*,
67 | int*);
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/src/util.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #ifdef HAVE_CONFIG_H
21 | #include
22 | #endif
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #include "util.h"
34 |
35 | int set_cloexec(int fd) {
36 | int n;
37 | assert(fd >= 0);
38 |
39 | if ((n = fcntl(fd, F_GETFD)) < 0)
40 | return -1;
41 |
42 | if (n & FD_CLOEXEC)
43 | return 0;
44 |
45 | return fcntl(fd, F_SETFD, n | FD_CLOEXEC);
46 | }
47 |
48 | int ends_with(const char* name, const char* suffix) {
49 | size_t ln, ls;
50 | assert(name);
51 | assert(suffix);
52 |
53 | if ((ls = strlen(suffix)) > (ln = strlen(name)))
54 | return 0;
55 |
56 | return strcasecmp(name + ln - ls, suffix) == 0;
57 | }
58 |
59 | use_name_result_t verify_name_allowed_with_soa(const char* name,
60 | FILE* mdns_allow_file,
61 | test_local_soa_t test) {
62 | switch (verify_name_allowed(name, mdns_allow_file)) {
63 | case VERIFY_NAME_RESULT_NOT_ALLOWED:
64 | return USE_NAME_RESULT_SKIP;
65 | case VERIFY_NAME_RESULT_ALLOWED:
66 | return USE_NAME_RESULT_AUTHORITATIVE;
67 | case VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA:
68 | if (test == TEST_LOCAL_SOA_YES ||
69 | (test == TEST_LOCAL_SOA_AUTO && local_soa()) )
70 | /* Make multicast resolution not authoritative for .local zone.
71 | * Allow continuing to unicast resolution after multicast had not worked. */
72 | return USE_NAME_RESULT_OPTIONAL;
73 | else
74 | return USE_NAME_RESULT_AUTHORITATIVE;
75 | default:
76 | return USE_NAME_RESULT_SKIP;
77 | }
78 | }
79 |
80 | verify_name_result_t verify_name_allowed(const char* name,
81 | FILE* mdns_allow_file) {
82 | assert(name);
83 |
84 | if (mdns_allow_file) {
85 | int valid = 0;
86 |
87 | while (!feof(mdns_allow_file)) {
88 | char ln[128], ln2[129], *t;
89 |
90 | if (!fgets(ln, sizeof(ln), mdns_allow_file))
91 | break;
92 |
93 | ln[strcspn(ln, "#\t\n\r ")] = 0;
94 |
95 | if (ln[0] == 0)
96 | continue;
97 |
98 | if (strcmp(ln, "*") == 0) {
99 | valid = 1;
100 | break;
101 | }
102 |
103 | if (ln[0] != '.')
104 | snprintf(t = ln2, sizeof(ln2), ".%s", ln);
105 | else
106 | t = ln;
107 |
108 | if (ends_with(name, t)) {
109 | valid = 1;
110 | break;
111 | }
112 | }
113 | if (valid)
114 | return VERIFY_NAME_RESULT_ALLOWED;
115 | else
116 | return VERIFY_NAME_RESULT_NOT_ALLOWED;
117 | } else {
118 | if ((ends_with(name, ".local") || ends_with(name, ".local.")) &&
119 | (label_count(name) == 2))
120 | return VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA;
121 | else
122 | return VERIFY_NAME_RESULT_NOT_ALLOWED;
123 | }
124 | }
125 |
126 | int local_soa(void) {
127 | /* FreeBSD requires the state to be zeroed before calling res_ninit() */
128 | struct __res_state state = {
129 | 0,
130 | };
131 | int result;
132 | unsigned char answer[NS_MAXMSG];
133 |
134 | result = res_ninit(&state);
135 | if (result == -1)
136 | return 0;
137 | result =
138 | res_nquery(&state, "local", ns_c_in, ns_t_soa, answer, sizeof answer);
139 | res_nclose(&state);
140 | return result > 0;
141 | }
142 |
143 | int label_count(const char* name) {
144 | // Start with single label.
145 | int count = 1;
146 | size_t i, len;
147 | assert(name);
148 |
149 | len = strlen(name);
150 | // Count all dots not in the last place.
151 | for (i = 0; i < len; i++) {
152 | if ((name[i] == '.') && (i != (len - 1)))
153 | count++;
154 | }
155 |
156 | return count;
157 | }
158 |
159 | enum nss_status convert_name_and_addr_to_hostent(const char* name,
160 | const void* addr, int len,
161 | int af, struct hostent* result,
162 | buffer_t* buf, int* errnop,
163 | int* h_errnop) {
164 | // Set empty list of aliases.
165 | result->h_aliases = (char**)buffer_alloc(buf, sizeof(char**));
166 | RETURN_IF_FAILED_ALLOC(result->h_aliases);
167 |
168 | // Set official name.
169 | result->h_name = buffer_strdup(buf, name);
170 | RETURN_IF_FAILED_ALLOC(result->h_name);
171 |
172 | // Set addrtype and length.
173 | result->h_addrtype = af;
174 | result->h_length = len;
175 |
176 | // Initialize address list of length 1, NULL terminated.
177 | result->h_addr_list = buffer_alloc(buf, 2 * sizeof(char**));
178 | RETURN_IF_FAILED_ALLOC(result->h_addr_list);
179 |
180 | // Copy the address.
181 | result->h_addr_list[0] = buffer_alloc(buf, len);
182 | RETURN_IF_FAILED_ALLOC(result->h_addr_list[0]);
183 | memcpy(result->h_addr_list[0], addr, len);
184 |
185 | return NSS_STATUS_SUCCESS;
186 | }
187 |
188 | enum nss_status convert_userdata_for_name_to_hostent(const userdata_t* u,
189 | const char* name, int af,
190 | struct hostent* result,
191 | buffer_t* buf, int* errnop,
192 | int* h_errnop) {
193 | size_t address_length =
194 | af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t);
195 |
196 | // Set empty list of aliases.
197 | result->h_aliases = (char**)buffer_alloc(buf, sizeof(char**));
198 | RETURN_IF_FAILED_ALLOC(result->h_aliases);
199 |
200 | // Set official name.
201 | result->h_name = buffer_strdup(buf, name);
202 | RETURN_IF_FAILED_ALLOC(result->h_name);
203 |
204 | // Set addrtype and length.
205 | result->h_addrtype = af;
206 | result->h_length = address_length;
207 |
208 | // Initialize address list, NULL terminated.
209 | result->h_addr_list = buffer_alloc(buf, (u->count + 1) * sizeof(char**));
210 | RETURN_IF_FAILED_ALLOC(result->h_addr_list);
211 |
212 | // Copy the addresses.
213 | for (int i = 0; i < u->count; i++) {
214 | char* addr = buffer_alloc(buf, address_length);
215 | RETURN_IF_FAILED_ALLOC(addr);
216 | memcpy(addr, &u->result[i].address, address_length);
217 | result->h_addr_list[i] = addr;
218 | }
219 |
220 | return NSS_STATUS_SUCCESS;
221 | }
222 |
223 | #ifndef __FreeBSD__
224 | enum nss_status convert_userdata_to_addrtuple(const userdata_t* u,
225 | const char* name,
226 | struct gaih_addrtuple** pat,
227 | buffer_t* buf, int* errnop,
228 | int* h_errnop) {
229 |
230 | // Copy name to buffer (referenced in every result address tuple).
231 | char* buffer_name = buffer_strdup(buf, name);
232 | RETURN_IF_FAILED_ALLOC(buffer_name);
233 |
234 | struct gaih_addrtuple* tuple_prev = NULL;
235 | for (int i = 0; i < u->count; i++) {
236 | const query_address_result_t* result = &u->result[i];
237 | struct gaih_addrtuple* tuple;
238 | if (tuple_prev == NULL && *pat) {
239 | // The caller has provided a valid initial location in *pat,
240 | // so use that as the first result. Without this, nscd will
241 | // segfault because it assumes that the buffer is only used as
242 | // an overflow.
243 | // See
244 | // https://lists.freedesktop.org/archives/systemd-devel/2013-February/008606.html
245 | tuple = *pat;
246 | memset(tuple, 0, sizeof(*tuple));
247 | } else {
248 | // Allocate a new tuple from the buffer.
249 | tuple = buffer_alloc(buf, sizeof(struct gaih_addrtuple));
250 | RETURN_IF_FAILED_ALLOC(tuple);
251 | }
252 |
253 | size_t address_length = result->af == AF_INET ? sizeof(ipv4_address_t)
254 | : sizeof(ipv6_address_t);
255 |
256 | // Assign the (always same) name.
257 | tuple->name = buffer_name;
258 |
259 | // Assign actual address family of address.
260 | tuple->family = result->af;
261 |
262 | // Copy address.
263 | memcpy(&(tuple->addr), &(result->address), address_length);
264 |
265 | // Assign interface scope id
266 | tuple->scopeid = result->scopeid;
267 |
268 | if (tuple_prev == NULL) {
269 | // This is the first tuple.
270 | // Return the start of the list in *pat.
271 | *pat = tuple;
272 | } else {
273 | // Link the new tuple into the previous tuple.
274 | tuple_prev->next = tuple;
275 | }
276 |
277 | tuple_prev = tuple;
278 | }
279 |
280 | return NSS_STATUS_SUCCESS;
281 | }
282 | #endif
283 |
284 | static char* aligned_ptr(char* p) {
285 | uintptr_t ptr = (uintptr_t)p;
286 | if (ptr % sizeof(void*)) {
287 | p += sizeof(void*) - (ptr % sizeof(void*));
288 | }
289 | return p;
290 | }
291 |
292 | void buffer_init(buffer_t* buf, char* buffer, size_t buflen) {
293 | // next always points to an aligned location.
294 | buf->next = aligned_ptr(buffer);
295 | // end is one past the buffer.
296 | buf->end = buffer + buflen;
297 | }
298 |
299 | void* buffer_alloc(buffer_t* buf, size_t size) {
300 | // Zero-length allocations always succeed with non-NULL.
301 | if (size == 0) {
302 | return buf; // Just a convenient non-NULL pointer.
303 | }
304 |
305 | char* alloc_end = buf->next + size;
306 | if (alloc_end > buf->end) {
307 | // No more memory in the buffer.
308 | return NULL;
309 | }
310 |
311 | // We have enough space. Set up the next aligned pointer and return
312 | // the current one, zeroed.
313 | char* current = buf->next;
314 | buf->next = aligned_ptr(alloc_end);
315 | memset(current, 0, size);
316 | return current;
317 | }
318 |
319 | char* buffer_strdup(buffer_t* buf, const char* str) {
320 | char* result = buffer_alloc(buf, strlen(str) + 1);
321 | if (result == NULL) {
322 | return NULL;
323 | }
324 | strcpy(result, str);
325 | return result;
326 | }
327 |
328 | void append_address_to_userdata(const query_address_result_t* result,
329 | userdata_t* u) {
330 | assert(result && u);
331 |
332 | if (u->count >= MAX_ENTRIES)
333 | return;
334 |
335 | memcpy(&(u->result[u->count]), result, sizeof(*result));
336 | u->count++;
337 | }
338 |
--------------------------------------------------------------------------------
/src/util.h:
--------------------------------------------------------------------------------
1 | #ifndef fooutilhfoo
2 | #define fooutilhfoo
3 |
4 | /*
5 | This file is part of nss-mdns.
6 |
7 | nss-mdns is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, see .
19 |
20 | SPDX-License-Identifier: LGPL-2.1-or-later
21 | */
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #ifdef __FreeBSD__
30 | #include
31 | #endif
32 | #include
33 |
34 | #include "avahi.h"
35 |
36 | // Simple buffer allocator.
37 | typedef struct {
38 | char* next;
39 | char* end;
40 | } buffer_t;
41 |
42 | // Sets up a buffer.
43 | void buffer_init(buffer_t* buf, char* buffer, size_t buflen);
44 |
45 | // Allocates a zeroed, aligned chunk of memory of a given size from the buffer
46 | // manager.
47 | // If there is insufficient space, returns NULL.
48 | void* buffer_alloc(buffer_t* buf, size_t size);
49 |
50 | // Duplicates a string into a newly allocated chunk of memory.
51 | // If there is insufficient space, returns NULL.
52 | char* buffer_strdup(buffer_t* buf, const char* str);
53 |
54 | // Macro to help with checking buffer allocation results.
55 | #define RETURN_IF_FAILED_ALLOC(ptr) \
56 | if (ptr == NULL) { \
57 | *errnop = ERANGE; \
58 | *h_errnop = NO_RECOVERY; \
59 | return NSS_STATUS_TRYAGAIN; \
60 | }
61 |
62 | int set_cloexec(int fd);
63 | int ends_with(const char* name, const char* suffix);
64 |
65 | typedef enum {
66 | USE_NAME_RESULT_SKIP,
67 | USE_NAME_RESULT_AUTHORITATIVE,
68 | USE_NAME_RESULT_OPTIONAL,
69 | } use_name_result_t;
70 |
71 | typedef enum {
72 | TEST_LOCAL_SOA_NO,
73 | TEST_LOCAL_SOA_YES,
74 | TEST_LOCAL_SOA_AUTO,
75 | } test_local_soa_t;
76 |
77 | // Returns true if we should try to resolve the name with mDNS.
78 | //
79 | // If mdns_allow_file is NULL, then this implements the "local" SOA
80 | // check and two-label name checks similarly to the algorithm
81 | // described at https://support.apple.com/en-us/HT201275. This means
82 | // that if a unicast DNS server claims authority on "local", or if the
83 | // user tries to resolve a >2-label name, we will not do mDNS resolution.
84 | //
85 | // The two heuristics described above are disabled if mdns_allow_file
86 | // is not NULL.
87 | use_name_result_t verify_name_allowed_with_soa(const char* name,
88 | FILE* mdns_allow_file,
89 | test_local_soa_t test);
90 |
91 | typedef enum {
92 | VERIFY_NAME_RESULT_NOT_ALLOWED,
93 | VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA,
94 | VERIFY_NAME_RESULT_ALLOWED
95 | } verify_name_result_t;
96 |
97 | // Tells us if the name is not allowed unconditionally, allowed only
98 | // if local_soa() returns false, or unconditionally allowed.
99 | verify_name_result_t verify_name_allowed(const char* name,
100 | FILE* mdns_allow_file);
101 |
102 | // Returns true if a DNS server claims authority over "local".
103 | int local_soa(void);
104 |
105 | // Returns the number of labels in a name.
106 | int label_count(const char* name);
107 |
108 | // Converts from a name and addr into the hostent format, used by
109 | // gethostbyaddr_r.
110 | enum nss_status convert_name_and_addr_to_hostent(const char* name,
111 | const void* addr, int len,
112 | int af, struct hostent* result,
113 | buffer_t* buf, int* errnop,
114 | int* h_errnop);
115 |
116 | // Converts from the userdata struct into the hostent format, used by
117 | // gethostbyaddr3_r.
118 | enum nss_status convert_userdata_for_name_to_hostent(const userdata_t* u,
119 | const char* name, int af,
120 | struct hostent* result,
121 | buffer_t* buf, int* errnop,
122 | int* h_errnop);
123 |
124 | // Converts from the userdata struct into the gaih_addrtuple format, used by
125 | // gethostbyaddr4_r.
126 | #ifndef __FreeBSD__
127 | enum nss_status convert_userdata_to_addrtuple(const userdata_t* u,
128 | const char* name,
129 | struct gaih_addrtuple** pat,
130 | buffer_t* buf, int* errnop,
131 | int* h_errnop);
132 | #endif
133 |
134 | // Appends a query_address_result to userdata.
135 | void append_address_to_userdata(const query_address_result_t* result,
136 | userdata_t* u);
137 |
138 | #endif
139 |
--------------------------------------------------------------------------------
/tests/check_util.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of nss-mdns.
3 |
4 | nss-mdns is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 2.1 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public
15 | License along with this library; if not, see .
16 |
17 | SPDX-License-Identifier: LGPL-2.1-or-later
18 | */
19 |
20 | #define _DEFAULT_SOURCE
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include "../src/util.h"
28 |
29 | // Tests that verify_name_allowed works in MINIMAL mode, or with no config file.
30 | // Only names with TLD "local" are allowed.
31 | // Only 2-label names are allowed.
32 | // SOA check is required.
33 | START_TEST(test_verify_name_allowed_minimal) {
34 | ck_assert_int_eq(verify_name_allowed("example.local", NULL),
35 | VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA);
36 | ck_assert_int_eq(verify_name_allowed("example.local.", NULL),
37 | VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA);
38 | ck_assert_int_eq(verify_name_allowed("com.example.local", NULL),
39 | VERIFY_NAME_RESULT_NOT_ALLOWED);
40 | ck_assert_int_eq(verify_name_allowed("com.example.local.", NULL),
41 | VERIFY_NAME_RESULT_NOT_ALLOWED);
42 | ck_assert_int_eq(verify_name_allowed("example.com", NULL),
43 | VERIFY_NAME_RESULT_NOT_ALLOWED);
44 | ck_assert_int_eq(verify_name_allowed("example.com.", NULL),
45 | VERIFY_NAME_RESULT_NOT_ALLOWED);
46 | ck_assert_int_eq(verify_name_allowed("example.local.com", NULL),
47 | VERIFY_NAME_RESULT_NOT_ALLOWED);
48 | ck_assert_int_eq(verify_name_allowed("example.local.com.", NULL),
49 | VERIFY_NAME_RESULT_NOT_ALLOWED);
50 | ck_assert_int_eq(verify_name_allowed("", NULL),
51 | VERIFY_NAME_RESULT_NOT_ALLOWED);
52 | ck_assert_int_eq(verify_name_allowed(".", NULL),
53 | VERIFY_NAME_RESULT_NOT_ALLOWED);
54 |
55 | ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_YES),
56 | USE_NAME_RESULT_SKIP);
57 | ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_NO),
58 | USE_NAME_RESULT_SKIP);
59 | ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_AUTO),
60 | USE_NAME_RESULT_SKIP);
61 | ck_assert_int_eq(verify_name_allowed_with_soa("example3.sub.local",
62 | NULL, TEST_LOCAL_SOA_YES), USE_NAME_RESULT_SKIP);
63 | ck_assert_int_eq(verify_name_allowed_with_soa("example4.sub.local",
64 | NULL, TEST_LOCAL_SOA_NO), USE_NAME_RESULT_SKIP);
65 | ck_assert_int_eq(verify_name_allowed_with_soa("example4.sub.local",
66 | NULL, TEST_LOCAL_SOA_AUTO), USE_NAME_RESULT_SKIP);
67 | ck_assert_int_eq(verify_name_allowed_with_soa("example1.local",
68 | NULL, TEST_LOCAL_SOA_YES), USE_NAME_RESULT_OPTIONAL);
69 | ck_assert_int_eq(verify_name_allowed_with_soa("example2.local",
70 | NULL, TEST_LOCAL_SOA_NO), USE_NAME_RESULT_AUTHORITATIVE);
71 | /* TEST_LOCAL_SOA_AUTO would test actual DNS on host, skip that. */
72 | }
73 | END_TEST
74 |
75 | // Calls verify_name_allowed by first creating a memfile to read from.
76 | static int verify_name_allowed_from_string(const char* name,
77 | const char* file_contents) {
78 | FILE* f = fmemopen((void*)file_contents, strlen(file_contents), "r");
79 | int result = verify_name_allowed(name, f);
80 | fclose(f);
81 | return result;
82 | }
83 |
84 | // Tests verify_name_allowed with empty config.
85 | // Nothing is permitted.
86 | START_TEST(test_verify_name_allowed_empty) {
87 | const char allow_file[] = "";
88 |
89 | ck_assert_int_eq(
90 | verify_name_allowed_from_string("example.local", allow_file),
91 | VERIFY_NAME_RESULT_NOT_ALLOWED);
92 | ck_assert_int_eq(
93 | verify_name_allowed_from_string("example.local.", allow_file),
94 | VERIFY_NAME_RESULT_NOT_ALLOWED);
95 | ck_assert_int_eq(
96 | verify_name_allowed_from_string("com.example.local", allow_file),
97 | VERIFY_NAME_RESULT_NOT_ALLOWED);
98 | ck_assert_int_eq(
99 | verify_name_allowed_from_string("com.example.local.", allow_file),
100 | VERIFY_NAME_RESULT_NOT_ALLOWED);
101 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
102 | VERIFY_NAME_RESULT_NOT_ALLOWED);
103 | ck_assert_int_eq(
104 | verify_name_allowed_from_string("example.com.", allow_file),
105 | VERIFY_NAME_RESULT_NOT_ALLOWED);
106 | ck_assert_int_eq(
107 | verify_name_allowed_from_string("example.local.com", allow_file),
108 | VERIFY_NAME_RESULT_NOT_ALLOWED);
109 | ck_assert_int_eq(
110 | verify_name_allowed_from_string("example.local.com.", allow_file),
111 | VERIFY_NAME_RESULT_NOT_ALLOWED);
112 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
113 | VERIFY_NAME_RESULT_NOT_ALLOWED);
114 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
115 | VERIFY_NAME_RESULT_NOT_ALLOWED);
116 | }
117 | END_TEST
118 |
119 | // Tests verify_name_allowed with the standard config.
120 | // .local is unconditionally permitted, without SOA check.
121 | // Multi-label names are allowed.
122 | START_TEST(test_verify_name_allowed_default) {
123 | const char allow_file[] = "# /etc/mdns.allow\n"
124 | ".local.\n"
125 | ".local\n";
126 |
127 | ck_assert_int_eq(
128 | verify_name_allowed_from_string("example.local", allow_file),
129 | VERIFY_NAME_RESULT_ALLOWED);
130 | ck_assert_int_eq(
131 | verify_name_allowed_from_string("example.local.", allow_file),
132 | VERIFY_NAME_RESULT_ALLOWED);
133 | ck_assert_int_eq(
134 | verify_name_allowed_from_string("com.example.local", allow_file),
135 | VERIFY_NAME_RESULT_ALLOWED);
136 | ck_assert_int_eq(
137 | verify_name_allowed_from_string("com.example.local.", allow_file),
138 | VERIFY_NAME_RESULT_ALLOWED);
139 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
140 | VERIFY_NAME_RESULT_NOT_ALLOWED);
141 | ck_assert_int_eq(
142 | verify_name_allowed_from_string("example.com.", allow_file),
143 | VERIFY_NAME_RESULT_NOT_ALLOWED);
144 | ck_assert_int_eq(
145 | verify_name_allowed_from_string("example.local.com", allow_file),
146 | VERIFY_NAME_RESULT_NOT_ALLOWED);
147 | ck_assert_int_eq(
148 | verify_name_allowed_from_string("example.local.com.", allow_file),
149 | VERIFY_NAME_RESULT_NOT_ALLOWED);
150 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
151 | VERIFY_NAME_RESULT_NOT_ALLOWED);
152 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
153 | VERIFY_NAME_RESULT_NOT_ALLOWED);
154 | }
155 | END_TEST
156 |
157 | // Tests verify_name_allowed with wildcard.
158 | // Everything is permitted, with no SOA check.
159 | // Multi-label names are allowed.
160 | START_TEST(test_verify_name_allowed_wildcard) {
161 | const char allow_file[] = "*\n";
162 |
163 | ck_assert_int_eq(
164 | verify_name_allowed_from_string("example.local", allow_file),
165 | VERIFY_NAME_RESULT_ALLOWED);
166 | ck_assert_int_eq(
167 | verify_name_allowed_from_string("example.local.", allow_file),
168 | VERIFY_NAME_RESULT_ALLOWED);
169 | ck_assert_int_eq(
170 | verify_name_allowed_from_string("com.example.local", allow_file),
171 | VERIFY_NAME_RESULT_ALLOWED);
172 | ck_assert_int_eq(
173 | verify_name_allowed_from_string("com.example.local.", allow_file),
174 | VERIFY_NAME_RESULT_ALLOWED);
175 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
176 | VERIFY_NAME_RESULT_ALLOWED);
177 | ck_assert_int_eq(
178 | verify_name_allowed_from_string("example.com.", allow_file),
179 | VERIFY_NAME_RESULT_ALLOWED);
180 | ck_assert_int_eq(
181 | verify_name_allowed_from_string("example.local.com", allow_file),
182 | VERIFY_NAME_RESULT_ALLOWED);
183 | ck_assert_int_eq(
184 | verify_name_allowed_from_string("example.local.com.", allow_file),
185 | VERIFY_NAME_RESULT_ALLOWED);
186 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
187 | VERIFY_NAME_RESULT_ALLOWED);
188 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
189 | VERIFY_NAME_RESULT_ALLOWED);
190 | }
191 | END_TEST
192 |
193 | // Tests verify_name_allowed with too-long lines.
194 | START_TEST(test_verify_name_allowed_too_long) {
195 | const char allow_file[] =
196 | "# /etc/mdns.allow\n"
197 | ".local."
198 | " " // 50 spaces
199 | " " // 50 spaces
200 | " " // 50 spaces
201 | "\n"
202 | ".local\n";
203 |
204 | ck_assert_int_eq(
205 | verify_name_allowed_from_string("example.local", allow_file),
206 | VERIFY_NAME_RESULT_ALLOWED);
207 | ck_assert_int_eq(
208 | verify_name_allowed_from_string("example.local.", allow_file),
209 | VERIFY_NAME_RESULT_ALLOWED);
210 | ck_assert_int_eq(
211 | verify_name_allowed_from_string("com.example.local", allow_file),
212 | VERIFY_NAME_RESULT_ALLOWED);
213 | ck_assert_int_eq(
214 | verify_name_allowed_from_string("com.example.local.", allow_file),
215 | VERIFY_NAME_RESULT_ALLOWED);
216 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
217 | VERIFY_NAME_RESULT_NOT_ALLOWED);
218 | ck_assert_int_eq(
219 | verify_name_allowed_from_string("example.com.", allow_file),
220 | VERIFY_NAME_RESULT_NOT_ALLOWED);
221 | ck_assert_int_eq(
222 | verify_name_allowed_from_string("example.local.com", allow_file),
223 | VERIFY_NAME_RESULT_NOT_ALLOWED);
224 | ck_assert_int_eq(
225 | verify_name_allowed_from_string("example.local.com.", allow_file),
226 | VERIFY_NAME_RESULT_NOT_ALLOWED);
227 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
228 | VERIFY_NAME_RESULT_NOT_ALLOWED);
229 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
230 | VERIFY_NAME_RESULT_NOT_ALLOWED);
231 | }
232 | END_TEST
233 |
234 | // Tests verify_name_allowed with too-long non-empty lines.
235 | START_TEST(test_verify_name_allowed_too_long2) {
236 | const char allow_file[] =
237 | "# /etc/mdns.allow\n"
238 | ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50 characters
239 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50 characters
240 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50 characters
241 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50 characters
242 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50 characters
243 | "\n"
244 | ".local.\n"
245 | ".local\n";
246 |
247 | // The input is truncated at 127 bytes, so we allow this string.
248 | ck_assert_int_eq(
249 | verify_name_allowed_from_string(
250 | "example"
251 | ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
252 | // characters
253 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
254 | // characters
255 | "aaaaaaaaaaaaaaaaaaaaaaaaaaa", // 27 characters
256 | allow_file),
257 | VERIFY_NAME_RESULT_ALLOWED);
258 |
259 | // Even though this exactly matches the item in the allow file,
260 | // it is too long.
261 | ck_assert_int_eq(
262 | verify_name_allowed_from_string(
263 | "example"
264 | ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
265 | // characters
266 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
267 | // characters
268 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
269 | // characters
270 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 50
271 | // characters
272 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50
273 | // characters
274 | allow_file),
275 | VERIFY_NAME_RESULT_NOT_ALLOWED);
276 |
277 | ck_assert_int_eq(
278 | verify_name_allowed_from_string("example.local", allow_file),
279 | VERIFY_NAME_RESULT_ALLOWED);
280 | ck_assert_int_eq(
281 | verify_name_allowed_from_string("example.local.", allow_file),
282 | VERIFY_NAME_RESULT_ALLOWED);
283 | ck_assert_int_eq(
284 | verify_name_allowed_from_string("com.example.local", allow_file),
285 | VERIFY_NAME_RESULT_ALLOWED);
286 | ck_assert_int_eq(
287 | verify_name_allowed_from_string("com.example.local.", allow_file),
288 | VERIFY_NAME_RESULT_ALLOWED);
289 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
290 | VERIFY_NAME_RESULT_NOT_ALLOWED);
291 | ck_assert_int_eq(
292 | verify_name_allowed_from_string("example.com.", allow_file),
293 | VERIFY_NAME_RESULT_NOT_ALLOWED);
294 | ck_assert_int_eq(
295 | verify_name_allowed_from_string("example.local.com", allow_file),
296 | VERIFY_NAME_RESULT_NOT_ALLOWED);
297 | ck_assert_int_eq(
298 | verify_name_allowed_from_string("example.local.com.", allow_file),
299 | VERIFY_NAME_RESULT_NOT_ALLOWED);
300 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
301 | VERIFY_NAME_RESULT_NOT_ALLOWED);
302 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
303 | VERIFY_NAME_RESULT_NOT_ALLOWED);
304 | }
305 | END_TEST
306 |
307 | // Tests verify_name_allowed with a custom config.
308 | START_TEST(test_verify_name_allowed_com_and_local) {
309 | const char allow_file[] = "# /etc/mdns.allow\n"
310 | ".com.\n"
311 | ".com\n"
312 | ".local.\n"
313 | ".local\n";
314 |
315 | ck_assert_int_eq(
316 | verify_name_allowed_from_string("example.local", allow_file),
317 | VERIFY_NAME_RESULT_ALLOWED);
318 | ck_assert_int_eq(
319 | verify_name_allowed_from_string("example.local.", allow_file),
320 | VERIFY_NAME_RESULT_ALLOWED);
321 | ck_assert_int_eq(
322 | verify_name_allowed_from_string("com.example.local", allow_file),
323 | VERIFY_NAME_RESULT_ALLOWED);
324 | ck_assert_int_eq(
325 | verify_name_allowed_from_string("com.example.local.", allow_file),
326 | VERIFY_NAME_RESULT_ALLOWED);
327 | ck_assert_int_eq(verify_name_allowed_from_string("example.com", allow_file),
328 | VERIFY_NAME_RESULT_ALLOWED);
329 | ck_assert_int_eq(
330 | verify_name_allowed_from_string("example.com.", allow_file),
331 | VERIFY_NAME_RESULT_ALLOWED);
332 | ck_assert_int_eq(
333 | verify_name_allowed_from_string("example.local.com", allow_file),
334 | VERIFY_NAME_RESULT_ALLOWED);
335 | ck_assert_int_eq(
336 | verify_name_allowed_from_string("example.local.com.", allow_file),
337 | VERIFY_NAME_RESULT_ALLOWED);
338 | ck_assert_int_eq(verify_name_allowed_from_string("example.net", allow_file),
339 | VERIFY_NAME_RESULT_NOT_ALLOWED);
340 | ck_assert_int_eq(
341 | verify_name_allowed_from_string("example.net.", allow_file),
342 | VERIFY_NAME_RESULT_NOT_ALLOWED);
343 | ck_assert_int_eq(verify_name_allowed_from_string("", allow_file),
344 | VERIFY_NAME_RESULT_NOT_ALLOWED);
345 | ck_assert_int_eq(verify_name_allowed_from_string(".", allow_file),
346 | VERIFY_NAME_RESULT_NOT_ALLOWED);
347 | }
348 | END_TEST
349 |
350 | // Tests ends_with.
351 | START_TEST(test_ends_with) {
352 | ck_assert(ends_with("", ""));
353 | ck_assert(!ends_with("", " "));
354 | ck_assert(!ends_with("", "z"));
355 | ck_assert(ends_with("z", ""));
356 | ck_assert(ends_with("z", "z"));
357 | ck_assert(!ends_with("z", "zz"));
358 | ck_assert(ends_with("example.local", ".local"));
359 | ck_assert(ends_with("example.local.", ".local."));
360 | ck_assert(!ends_with("example.local.", ".local"));
361 | ck_assert(!ends_with("example.local.", ".local"));
362 | }
363 | END_TEST
364 |
365 | // Tests label_count.
366 | START_TEST(test_label_count) {
367 | ck_assert_int_eq(label_count(""), 1);
368 | ck_assert_int_eq(label_count("."), 1);
369 | ck_assert_int_eq(label_count("local"), 1);
370 | ck_assert_int_eq(label_count("local."), 1);
371 | ck_assert_int_eq(label_count("foo.local"), 2);
372 | ck_assert_int_eq(label_count("foo.local."), 2);
373 | ck_assert_int_eq(label_count("bar.foo.local"), 3);
374 | ck_assert_int_eq(label_count("bar.foo.local."), 3);
375 | ck_assert_int_eq(label_count("my-foo.local"), 2);
376 | ck_assert_int_eq(label_count("my-foo.local."), 2);
377 | }
378 | END_TEST
379 |
380 | // Tests for buffer_t functions.
381 |
382 | START_TEST(test_buffer_alloc_too_large_returns_null) {
383 | char *buffer = malloc(100);
384 | buffer_t buf;
385 | buffer_init(&buf, buffer, 100);
386 |
387 | ck_assert_ptr_null(buffer_alloc(&buf, 101));
388 | free(buffer);
389 | }
390 | END_TEST
391 |
392 | START_TEST(test_buffer_alloc_just_right_returns_nonnull) {
393 | char *buffer = malloc(100);
394 | buffer_t buf;
395 | buffer_init(&buf, buffer, 100);
396 |
397 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 100));
398 | free(buffer);
399 | }
400 | END_TEST
401 |
402 | START_TEST(test_unaligned_buffer_alloc_returns_aligned) {
403 | char *buffer = malloc(1000);
404 | buffer_t buf;
405 |
406 | for (size_t i = 0; i < 32; i++) {
407 | buffer_init(&buf, buffer + i, 1000 - i);
408 | char* ptr = buffer_alloc(&buf, 10);
409 | ck_assert_uint_eq((uintptr_t)ptr % sizeof(void*), 0);
410 | }
411 | free(buffer);
412 | }
413 | END_TEST
414 |
415 | START_TEST(test_buffer_alloc_returns_aligned) {
416 | char *buffer = malloc(1000);
417 | buffer_t buf;
418 | buffer_init(&buf, buffer, 1000);
419 |
420 | for (size_t i = 0; i < 32; i++) {
421 | char* ptr = buffer_alloc(&buf, i);
422 | ck_assert_ptr_nonnull(ptr);
423 | ck_assert_uint_eq((uintptr_t)ptr % sizeof(void*), 0);
424 | }
425 | free(buffer);
426 | }
427 | END_TEST
428 |
429 | START_TEST(test_null_buffer_zero_alloc_returns_nonnull) {
430 | buffer_t buf;
431 | buffer_init(&buf, NULL, 0);
432 |
433 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 0));
434 | }
435 | END_TEST
436 |
437 | START_TEST(test_zero_buffer_zero_alloc_returns_nonnull) {
438 | char *buffer = malloc(1);
439 | buffer_t buf;
440 | buffer_init(&buf, buffer, 0);
441 |
442 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 0));
443 | free(buffer);
444 | }
445 | END_TEST
446 |
447 | START_TEST(test_nonzero_buffer_zero_alloc_returns_nonnull) {
448 | char *buffer = malloc(100);
449 | buffer_t buf;
450 | buffer_init(&buf, buffer, 100);
451 |
452 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 0));
453 | free(buffer);
454 | }
455 | END_TEST
456 |
457 | START_TEST(test_null_buffer_nonzero_alloc_returns_null) {
458 | buffer_t buf;
459 | buffer_init(&buf, NULL, 0);
460 |
461 | ck_assert_ptr_null(buffer_alloc(&buf, 1));
462 | }
463 | END_TEST
464 |
465 | START_TEST(test_zero_buffer_nonzero_alloc_returns_null) {
466 | char *buffer = malloc(1);
467 | buffer_t buf;
468 | buffer_init(&buf, buffer, 0);
469 |
470 | ck_assert_ptr_null(buffer_alloc(&buf, 1));
471 | free(buffer);
472 | }
473 | END_TEST
474 |
475 | START_TEST(test_buffer_tiny_alloc_returns_nonnull) {
476 | char *buffer = malloc(100);
477 | buffer_t buf;
478 | buffer_init(&buf, buffer, 100);
479 |
480 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 1));
481 | free(buffer);
482 | }
483 | END_TEST
484 |
485 | START_TEST(test_tiny_buffer_tiny_alloc_returns_nonnull) {
486 | char* buffer = malloc(1);
487 | buffer_t buf;
488 | buffer_init(&buf, buffer, 1);
489 |
490 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 1));
491 | free(buffer);
492 | }
493 | END_TEST
494 |
495 | START_TEST(test_tiny_unaligned_buffer_tiny_alloc_returns_null) {
496 | char* buffer = malloc(2);
497 | buffer_t buf;
498 | buffer_init(&buf, buffer + 1, 1);
499 |
500 | ck_assert_ptr_null(buffer_alloc(&buf, 1));
501 | free(buffer);
502 | }
503 | END_TEST
504 |
505 | START_TEST(test_tiny_buffer_second_alloc_returns_null) {
506 | char* buffer = malloc(2);
507 | buffer_t buf;
508 | buffer_init(&buf, buffer, 2);
509 |
510 | ck_assert_ptr_nonnull(buffer_alloc(&buf, 1));
511 | ck_assert_ptr_null(buffer_alloc(&buf, 1)); // With alignment, out of room.
512 | free(buffer);
513 | }
514 | END_TEST
515 |
516 | START_TEST(test_tiny_buffer_one_too_big_alloc_returns_null) {
517 | char* buffer = malloc(1); // Need to malloc to get pre-aligned buffer.
518 | buffer_t buf;
519 | buffer_init(&buf, buffer, 1);
520 |
521 | ck_assert_ptr_null(buffer_alloc(&buf, 2));
522 | free(buffer);
523 | }
524 | END_TEST
525 |
526 | START_TEST(test_buffer_alloc_returns_zeroed_memory) {
527 | char *buffer = malloc(100);
528 | memset(buffer, 0xFF, 100);
529 |
530 | char zero[100];
531 | memset(zero, 0, sizeof(zero));
532 |
533 | buffer_t buf;
534 | buffer_init(&buf, buffer, 100);
535 | ck_assert_mem_eq(buffer_alloc(&buf, 50), zero, 50);
536 | free(buffer);
537 | }
538 | END_TEST
539 |
540 | static const uint8_t ipv6_doc_prefix[16] = {
541 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
542 | 0x0, 0x0, 0x0, 0x0, 0xb8, 0x0d, 0x01, 0x20}; // Documentation prefix
543 | static const uint32_t ipv4_test_addr = 0xc6336401; // 198.51.100.1 (TEST-NET-2)
544 |
545 | static query_address_result_t create_address_result(int offset, int af) {
546 | query_address_result_t result;
547 |
548 | if (af == AF_UNSPEC) {
549 | // Alternate between IPv4 and IPv6.
550 | af = offset % 2 ? AF_INET : AF_INET6;
551 | result.af = af;
552 | }
553 |
554 | switch (af) {
555 | case AF_INET:
556 | result.address.ipv4.address = htonl(ipv4_test_addr + offset);
557 | break;
558 | case AF_INET6:
559 | memcpy(result.address.ipv6.address, ipv6_doc_prefix,
560 | sizeof ipv6_doc_prefix);
561 | result.address.ipv6.address[0] = offset;
562 | result.scopeid = (offset / 2) % 3;
563 | break;
564 | }
565 | return result;
566 | }
567 |
568 | #ifndef __FreeBSD__
569 | static void validate_addrtuples(struct gaih_addrtuple* pat,
570 | const char* expected_name, int expected_count) {
571 | int i = 0;
572 | while (pat != NULL) {
573 | ck_assert_str_eq(pat->name, expected_name);
574 | int expected_af = i % 2 ? AF_INET : AF_INET6;
575 | ck_assert_int_eq(pat->family, expected_af);
576 |
577 | uint32_t expected_ipv4 = htonl(ipv4_test_addr + i);
578 | uint8_t expected_ipv6[16];
579 | memcpy(expected_ipv6, ipv6_doc_prefix, sizeof ipv6_doc_prefix);
580 | expected_ipv6[0] = i;
581 | switch (expected_af) {
582 | case AF_INET:
583 | ck_assert_mem_eq(pat->addr, &expected_ipv4, sizeof expected_ipv4);
584 | break;
585 | case AF_INET6:
586 | ck_assert_mem_eq(pat->addr, expected_ipv6, sizeof expected_ipv6);
587 | ck_assert_int_eq(pat->scopeid, (i / 2) % 3);
588 | break;
589 | }
590 |
591 | i++;
592 | pat = pat->next;
593 | }
594 | ck_assert_int_eq(i, expected_count);
595 | }
596 | #endif
597 |
598 | static userdata_t create_address_userdata(int num_addresses, int af) {
599 | ck_assert_int_le(num_addresses, MAX_ENTRIES);
600 |
601 | userdata_t u;
602 | u.count = 0;
603 | for (int i = 0; i < num_addresses; i++) {
604 | query_address_result_t result = create_address_result(i, af);
605 | append_address_to_userdata(&result, &u);
606 | }
607 | return u;
608 | }
609 |
610 | static void poison(char* buf, size_t buflen) { memset(buf, 0x55, buflen); }
611 |
612 | static void validate_poison(char* buf, size_t buflen, size_t full_buflen) {
613 | size_t excess = full_buflen - buflen;
614 | char poison[excess];
615 | memset(poison, 0x55, sizeof(poison));
616 | ck_assert_mem_eq(buf + buflen, poison, excess);
617 | }
618 |
619 | static void validate_hostent(struct hostent* hostent, const char* name, int af,
620 | int expected_count) {
621 | ck_assert_str_eq(name, hostent->h_name);
622 | ck_assert_ptr_nonnull(hostent->h_aliases);
623 | ck_assert_ptr_null(hostent->h_aliases[0]);
624 | ck_assert_int_eq(af, hostent->h_addrtype);
625 | ck_assert_int_eq(af == AF_INET ? sizeof(ipv4_address_t)
626 | : sizeof(ipv6_address_t),
627 | hostent->h_length);
628 | ck_assert_ptr_nonnull(hostent->h_addr_list);
629 |
630 | int i = 0;
631 | char** addr = hostent->h_addr_list;
632 | while (*addr != NULL) {
633 | uint32_t expected_ipv4 = htonl(ipv4_test_addr + i);
634 | uint8_t expected_ipv6[16];
635 | memcpy(expected_ipv6, ipv6_doc_prefix, sizeof ipv6_doc_prefix);
636 | expected_ipv6[0] = i;
637 | switch (af) {
638 | case AF_INET:
639 | ck_assert_mem_eq(*addr, &expected_ipv4, sizeof expected_ipv4);
640 | break;
641 | case AF_INET6:
642 | ck_assert_mem_eq(*addr, expected_ipv6, sizeof expected_ipv6);
643 | break;
644 | }
645 | addr++;
646 | i++;
647 | }
648 | ck_assert_int_eq(expected_count, i);
649 | }
650 |
651 | // Tests for convert_userdata_to_addrtuple.
652 |
653 | #ifndef __FreeBSD__
654 | START_TEST(test_userdata_to_addrtuple_returns_tuples) {
655 | userdata_t u = create_address_userdata(16, AF_UNSPEC);
656 | struct gaih_addrtuple* pat = NULL;
657 | char buffer[2048];
658 | int errnop;
659 | int h_errnop;
660 |
661 | buffer_t buf;
662 | buffer_init(&buf, buffer, sizeof(buffer));
663 | enum nss_status status = convert_userdata_to_addrtuple(
664 | &u, "example.local", &pat, &buf, &errnop, &h_errnop);
665 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
666 | validate_addrtuples(pat, "example.local", 16);
667 | }
668 | END_TEST
669 |
670 | START_TEST(test_userdata_to_addrtuple_buffer_too_small_returns_erange) {
671 | userdata_t u = create_address_userdata(8, AF_UNSPEC);
672 | struct gaih_addrtuple* pat = NULL;
673 | char buffer[10];
674 | int errnop;
675 | int h_errnop;
676 |
677 | buffer_t buf;
678 | buffer_init(&buf, buffer, 0);
679 | enum nss_status status = convert_userdata_to_addrtuple(
680 | &u, "example.local", &pat, &buf, &errnop, &h_errnop);
681 | ck_assert_int_eq(errnop, ERANGE);
682 | ck_assert_int_eq(h_errnop, NO_RECOVERY);
683 | ck_assert_int_eq(status, NSS_STATUS_TRYAGAIN);
684 | }
685 | END_TEST
686 |
687 | START_TEST(test_userdata_to_addrtuple_smallest_buffer_eventually_works) {
688 | userdata_t u = create_address_userdata(16, AF_UNSPEC);
689 | struct gaih_addrtuple* pat;
690 | char buffer[2048];
691 | int errnop;
692 | int h_errnop;
693 |
694 | enum nss_status status = 0;
695 | size_t buflen;
696 | for (buflen = 0; buflen < sizeof(buffer); buflen++) {
697 | poison(buffer, sizeof(buffer));
698 | errnop = h_errnop = 0;
699 | buffer_t buf;
700 | pat = NULL;
701 | buffer_init(&buf, buffer, buflen);
702 | status = convert_userdata_to_addrtuple(&u, "example.local", &pat, &buf,
703 | &errnop, &h_errnop);
704 | validate_poison(buffer, buflen, sizeof(buffer));
705 | if (errnop != ERANGE)
706 | break;
707 | if (h_errnop != NO_RECOVERY)
708 | break;
709 | if (status != NSS_STATUS_TRYAGAIN)
710 | break;
711 | }
712 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
713 | validate_addrtuples(pat, "example.local", 16);
714 | }
715 | END_TEST
716 |
717 | START_TEST(test_userdata_to_addrtuple_nonnull_pat_is_used) {
718 | userdata_t u = create_address_userdata(16, AF_UNSPEC);
719 | struct gaih_addrtuple tuple;
720 | struct gaih_addrtuple* pat = &tuple;
721 | char buffer[2048];
722 | int errnop;
723 | int h_errnop;
724 |
725 | memset(&tuple, 0, sizeof tuple);
726 | buffer_t buf;
727 | buffer_init(&buf, buffer, sizeof(buffer));
728 | enum nss_status status = convert_userdata_to_addrtuple(
729 | &u, "example.local", &pat, &buf, &errnop, &h_errnop);
730 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
731 | validate_addrtuples(&tuple, "example.local", 16);
732 | }
733 | END_TEST
734 | #endif
735 |
736 | // Tests for convert_userdata_for_name_to_hostent.
737 |
738 | START_TEST(test_userdata_for_name_to_hostent_returns_hostent_4) {
739 | userdata_t u = create_address_userdata(16, AF_INET);
740 | struct hostent result;
741 | char buffer[2048];
742 | int errnop;
743 | int h_errnop;
744 |
745 | buffer_t buf;
746 | buffer_init(&buf, buffer, sizeof(buffer));
747 | enum nss_status status = convert_userdata_for_name_to_hostent(
748 | &u, "example.local", AF_INET, &result, &buf, &errnop, &h_errnop);
749 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
750 | validate_hostent(&result, "example.local", AF_INET, 16);
751 | }
752 | END_TEST
753 |
754 | START_TEST(test_userdata_for_name_to_hostent_returns_hostent_6) {
755 | userdata_t u = create_address_userdata(16, AF_INET6);
756 | struct hostent result;
757 | char buffer[2048];
758 | int errnop;
759 | int h_errnop;
760 |
761 | buffer_t buf;
762 | buffer_init(&buf, buffer, sizeof(buffer));
763 | enum nss_status status = convert_userdata_for_name_to_hostent(
764 | &u, "example.local", AF_INET6, &result, &buf, &errnop, &h_errnop);
765 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
766 | validate_hostent(&result, "example.local", AF_INET6, 16);
767 | }
768 | END_TEST
769 |
770 | START_TEST(test_userdata_for_name_to_hostent_buffer_too_small_returns_erange) {
771 | userdata_t u = create_address_userdata(16, AF_INET);
772 | struct hostent result;
773 | char buffer[10];
774 | int errnop;
775 | int h_errnop;
776 |
777 | buffer_t buf;
778 | buffer_init(&buf, buffer, 0);
779 | enum nss_status status = convert_userdata_for_name_to_hostent(
780 | &u, "example.local", AF_INET, &result, &buf, &errnop, &h_errnop);
781 | ck_assert_int_eq(errnop, ERANGE);
782 | ck_assert_int_eq(h_errnop, NO_RECOVERY);
783 | ck_assert_int_eq(status, NSS_STATUS_TRYAGAIN);
784 | }
785 | END_TEST
786 |
787 | START_TEST(
788 | test_userdata_for_name_to_hostent_smallest_buffer_eventually_works_4) {
789 | userdata_t u = create_address_userdata(16, AF_INET);
790 | struct hostent result;
791 | char buffer[2048];
792 | int errnop;
793 | int h_errnop;
794 |
795 | enum nss_status status = 0;
796 | size_t buflen;
797 | for (buflen = 0; buflen < sizeof(buffer); buflen++) {
798 | poison(buffer, sizeof(buffer));
799 | errnop = h_errnop = 0;
800 |
801 | buffer_t buf;
802 | buffer_init(&buf, buffer, buflen);
803 | status = convert_userdata_for_name_to_hostent(
804 | &u, "example.local", AF_INET, &result, &buf, &errnop, &h_errnop);
805 | validate_poison(buffer, buflen, sizeof(buffer));
806 | if (errnop != ERANGE)
807 | break;
808 | if (h_errnop != NO_RECOVERY)
809 | break;
810 | if (status != NSS_STATUS_TRYAGAIN)
811 | break;
812 | }
813 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
814 | validate_hostent(&result, "example.local", AF_INET, 16);
815 | }
816 | END_TEST
817 |
818 | START_TEST(
819 | test_userdata_for_name_to_hostent_smallest_buffer_eventually_works_6) {
820 | userdata_t u = create_address_userdata(16, AF_INET6);
821 | struct hostent result;
822 | char buffer[2048];
823 | int errnop;
824 | int h_errnop;
825 |
826 | enum nss_status status = 0;
827 | size_t buflen;
828 | for (buflen = 0; buflen < sizeof(buffer); buflen++) {
829 | poison(buffer, sizeof(buffer));
830 | errnop = h_errnop = 0;
831 | buffer_t buf;
832 | buffer_init(&buf, buffer, buflen);
833 | status = convert_userdata_for_name_to_hostent(
834 | &u, "example.local", AF_INET6, &result, &buf, &errnop, &h_errnop);
835 | validate_poison(buffer, buflen, sizeof(buffer));
836 | if (errnop != ERANGE)
837 | break;
838 | if (h_errnop != NO_RECOVERY)
839 | break;
840 | if (status != NSS_STATUS_TRYAGAIN)
841 | break;
842 | }
843 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
844 | validate_hostent(&result, "example.local", AF_INET6, 16);
845 | }
846 | END_TEST
847 |
848 | // Tests for convert_name_and_addr_to_hostent.
849 |
850 | START_TEST(test_name_and_addr_to_hostent_returns_hostent_4) {
851 | struct hostent result;
852 | char buffer[2048];
853 | int errnop;
854 | int h_errnop;
855 | uint32_t ipv4 = htonl(ipv4_test_addr);
856 |
857 | buffer_t buf;
858 | buffer_init(&buf, buffer, sizeof(buffer));
859 | enum nss_status status = convert_name_and_addr_to_hostent(
860 | "example.local", &ipv4, sizeof ipv4, AF_INET, &result, &buf, &errnop,
861 | &h_errnop);
862 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
863 | validate_hostent(&result, "example.local", AF_INET, 1);
864 | }
865 | END_TEST
866 |
867 | START_TEST(test_name_and_addr_to_hostent_returns_hostent_6) {
868 | struct hostent result;
869 | char buffer[2048];
870 | int errnop;
871 | int h_errnop;
872 |
873 | buffer_t buf;
874 | buffer_init(&buf, buffer, sizeof(buffer));
875 | enum nss_status status = convert_name_and_addr_to_hostent(
876 | "example.local", &ipv6_doc_prefix, sizeof ipv6_doc_prefix, AF_INET6,
877 | &result, &buf, &errnop, &h_errnop);
878 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
879 | validate_hostent(&result, "example.local", AF_INET6, 1);
880 | }
881 | END_TEST
882 |
883 | START_TEST(test_name_and_addr_to_hostent_buffer_too_small_returns_erange) {
884 | struct hostent result;
885 | char buffer[10];
886 | int errnop;
887 | int h_errnop;
888 | uint32_t ipv4 = htonl(ipv4_test_addr);
889 |
890 | buffer_t buf;
891 | buffer_init(&buf, buffer, sizeof(buffer));
892 | enum nss_status status = convert_name_and_addr_to_hostent(
893 | "example.local", &ipv4, sizeof ipv4, AF_INET, &result, &buf, &errnop,
894 | &h_errnop);
895 | ck_assert_int_eq(errnop, ERANGE);
896 | ck_assert_int_eq(h_errnop, NO_RECOVERY);
897 | ck_assert_int_eq(status, NSS_STATUS_TRYAGAIN);
898 | }
899 | END_TEST
900 |
901 | START_TEST(test_name_and_addr_to_hostent_smallest_buffer_eventually_works_4) {
902 | struct hostent result;
903 | char buffer[2048];
904 | int errnop;
905 | int h_errnop;
906 | uint32_t ipv4 = htonl(ipv4_test_addr);
907 | enum nss_status status = 0;
908 | size_t buflen;
909 | for (buflen = 0; buflen < sizeof(buffer); buflen++) {
910 | poison(buffer, sizeof(buffer));
911 | errnop = h_errnop = 0;
912 | buffer_t buf;
913 | buffer_init(&buf, buffer, buflen);
914 | status = convert_name_and_addr_to_hostent("example.local", &ipv4,
915 | sizeof ipv4, AF_INET, &result,
916 | &buf, &errnop, &h_errnop);
917 | validate_poison(buffer, buflen, sizeof(buffer));
918 | if (errnop != ERANGE)
919 | break;
920 | if (h_errnop != NO_RECOVERY)
921 | break;
922 | if (status != NSS_STATUS_TRYAGAIN)
923 | break;
924 | }
925 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
926 | validate_hostent(&result, "example.local", AF_INET, 1);
927 | }
928 | END_TEST
929 |
930 | START_TEST(test_name_and_addr_to_hostent_smallest_buffer_eventually_works_6) {
931 | struct hostent result;
932 | char buffer[2048];
933 | int errnop;
934 | int h_errnop;
935 | enum nss_status status = 0;
936 | size_t buflen;
937 | for (buflen = 0; buflen < sizeof(buffer); buflen++) {
938 | poison(buffer, sizeof(buffer));
939 | errnop = h_errnop = 0;
940 | buffer_t buf;
941 | buffer_init(&buf, buffer, buflen);
942 | status = convert_name_and_addr_to_hostent(
943 | "example.local", &ipv6_doc_prefix, sizeof ipv6_doc_prefix, AF_INET6,
944 | &result, &buf, &errnop, &h_errnop);
945 | validate_poison(buffer, buflen, sizeof(buffer));
946 | if (errnop != ERANGE)
947 | break;
948 | if (h_errnop != NO_RECOVERY)
949 | break;
950 | if (status != NSS_STATUS_TRYAGAIN)
951 | break;
952 | }
953 | ck_assert_int_eq(status, NSS_STATUS_SUCCESS);
954 | validate_hostent(&result, "example.local", AF_INET6, 1);
955 | }
956 | END_TEST
957 |
958 | // Boilerplate from https://libcheck.github.io/check/doc/check_html/check_3.html
959 | static Suite* util_suite(void) {
960 | Suite* s = suite_create("util");
961 |
962 | TCase* tc_verify_name = tcase_create("verify_name");
963 | tcase_add_test(tc_verify_name, test_verify_name_allowed_minimal);
964 | tcase_add_test(tc_verify_name, test_verify_name_allowed_default);
965 | tcase_add_test(tc_verify_name, test_verify_name_allowed_empty);
966 | tcase_add_test(tc_verify_name, test_verify_name_allowed_wildcard);
967 | tcase_add_test(tc_verify_name, test_verify_name_allowed_too_long);
968 | tcase_add_test(tc_verify_name, test_verify_name_allowed_too_long2);
969 | tcase_add_test(tc_verify_name, test_verify_name_allowed_com_and_local);
970 | suite_add_tcase(s, tc_verify_name);
971 |
972 | TCase* tc_ends_with = tcase_create("ends_with");
973 | tcase_add_test(tc_ends_with, test_ends_with);
974 | suite_add_tcase(s, tc_ends_with);
975 |
976 | TCase* tc_label_count = tcase_create("label_count");
977 | tcase_add_test(tc_label_count, test_label_count);
978 | suite_add_tcase(s, tc_label_count);
979 |
980 | TCase* tc_buffer = tcase_create("buffer");
981 | tcase_add_test(tc_buffer, test_buffer_alloc_too_large_returns_null);
982 | tcase_add_test(tc_buffer, test_buffer_alloc_just_right_returns_nonnull);
983 | tcase_add_test(tc_buffer, test_unaligned_buffer_alloc_returns_aligned);
984 | tcase_add_test(tc_buffer, test_buffer_alloc_returns_aligned);
985 | tcase_add_test(tc_buffer, test_null_buffer_zero_alloc_returns_nonnull);
986 | tcase_add_test(tc_buffer, test_zero_buffer_zero_alloc_returns_nonnull);
987 | tcase_add_test(tc_buffer, test_nonzero_buffer_zero_alloc_returns_nonnull);
988 | tcase_add_test(tc_buffer, test_null_buffer_nonzero_alloc_returns_null);
989 | tcase_add_test(tc_buffer, test_zero_buffer_nonzero_alloc_returns_null);
990 | tcase_add_test(tc_buffer, test_buffer_tiny_alloc_returns_nonnull);
991 | tcase_add_test(tc_buffer, test_tiny_buffer_tiny_alloc_returns_nonnull);
992 | tcase_add_test(tc_buffer,
993 | test_tiny_unaligned_buffer_tiny_alloc_returns_null);
994 | tcase_add_test(tc_buffer, test_tiny_buffer_second_alloc_returns_null);
995 | tcase_add_test(tc_buffer, test_tiny_buffer_one_too_big_alloc_returns_null);
996 | tcase_add_test(tc_buffer, test_buffer_alloc_returns_zeroed_memory);
997 | suite_add_tcase(s, tc_buffer);
998 |
999 | #ifndef __FreeBSD__
1000 | TCase* tc_userdata_to_addrtuple = tcase_create("userdata_to_addrtuple");
1001 | tcase_add_test(tc_userdata_to_addrtuple,
1002 | test_userdata_to_addrtuple_returns_tuples);
1003 | tcase_add_test(tc_userdata_to_addrtuple,
1004 | test_userdata_to_addrtuple_buffer_too_small_returns_erange);
1005 | tcase_add_test(tc_userdata_to_addrtuple,
1006 | test_userdata_to_addrtuple_smallest_buffer_eventually_works);
1007 | tcase_add_test(tc_userdata_to_addrtuple,
1008 | test_userdata_to_addrtuple_nonnull_pat_is_used);
1009 | suite_add_tcase(s, tc_userdata_to_addrtuple);
1010 | #endif
1011 |
1012 | TCase* tc_userdata_for_name_to_hostent =
1013 | tcase_create("userdata_for_name_to_hostent");
1014 | tcase_add_test(tc_userdata_for_name_to_hostent,
1015 | test_userdata_for_name_to_hostent_returns_hostent_4);
1016 | tcase_add_test(tc_userdata_for_name_to_hostent,
1017 | test_userdata_for_name_to_hostent_returns_hostent_6);
1018 | tcase_add_test(
1019 | tc_userdata_for_name_to_hostent,
1020 | test_userdata_for_name_to_hostent_buffer_too_small_returns_erange);
1021 | tcase_add_test(
1022 | tc_userdata_for_name_to_hostent,
1023 | test_userdata_for_name_to_hostent_smallest_buffer_eventually_works_4);
1024 | tcase_add_test(
1025 | tc_userdata_for_name_to_hostent,
1026 | test_userdata_for_name_to_hostent_smallest_buffer_eventually_works_6);
1027 | suite_add_tcase(s, tc_userdata_for_name_to_hostent);
1028 |
1029 | TCase* tc_name_and_addr_to_hostent =
1030 | tcase_create("name_and_addr_to_hostent");
1031 | tcase_add_test(tc_name_and_addr_to_hostent,
1032 | test_name_and_addr_to_hostent_returns_hostent_4);
1033 | tcase_add_test(tc_name_and_addr_to_hostent,
1034 | test_name_and_addr_to_hostent_returns_hostent_6);
1035 | tcase_add_test(
1036 | tc_name_and_addr_to_hostent,
1037 | test_name_and_addr_to_hostent_buffer_too_small_returns_erange);
1038 | tcase_add_test(
1039 | tc_name_and_addr_to_hostent,
1040 | test_name_and_addr_to_hostent_smallest_buffer_eventually_works_4);
1041 | tcase_add_test(
1042 | tc_name_and_addr_to_hostent,
1043 | test_name_and_addr_to_hostent_smallest_buffer_eventually_works_6);
1044 | suite_add_tcase(s, tc_name_and_addr_to_hostent);
1045 |
1046 | return s;
1047 | }
1048 |
1049 | int main(void) {
1050 | int number_failed;
1051 | Suite* s;
1052 | SRunner* sr;
1053 |
1054 | s = util_suite();
1055 | sr = srunner_create(s);
1056 |
1057 | srunner_run_all(sr, CK_VERBOSE);
1058 | number_failed = srunner_ntests_failed(sr);
1059 | srunner_free(sr);
1060 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
1061 | }
1062 |
--------------------------------------------------------------------------------