├── .clang-format ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .packit.yml ├── ACKNOWLEDGEMENTS.md ├── LICENSE ├── Makefile.am ├── NEWS.md ├── README.md ├── bootstrap.sh ├── configure.ac ├── pkg └── rpm-fedora │ └── nss-mdns.spec ├── src ├── avahi-test.c ├── avahi.c ├── avahi.h ├── bsdnss.c ├── map-file ├── nss-test.c ├── nss.c ├── nss.h ├── util.c └── util.h └── tests └── check_util.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | PointerAlignment: Left 4 | SortIncludes: false 5 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: autoreconf 17 | run: autoreconf -i -f -v 18 | - name: configure 19 | run: ./configure --sysconfdir=/etc --localstatedir=/var CFLAGS="-Wall -W -Wextra" 20 | - name: make 21 | run: make 22 | - name: make check 23 | run: make check 24 | - name: make distcheck 25 | run: make distcheck 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.la 4 | *.lo 5 | *.log 6 | *.tar.gz 7 | *.trs 8 | .deps 9 | .dirstamp 10 | .libs 11 | Makefile 12 | Makefile.in 13 | /aclocal.m4 14 | /ar-lib 15 | /autom4te.cache 16 | /avahi-test 17 | /compile 18 | /config.* 19 | /configure 20 | /check_util 21 | /depcomp 22 | /install-sh 23 | /libtool 24 | /ltmain.sh 25 | /m4 26 | /missing 27 | /nss-test 28 | /stamp-h1 29 | /test-driver 30 | -------------------------------------------------------------------------------- /.packit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | specfile_path: pkg/rpm-fedora/nss-mdns.spec 3 | files_to_sync: 4 | - .packit.yml 5 | - src: pkg/rpm-fedora/nss-mdns.spec 6 | dest: nss-mdns.spec 7 | upstream_package_name: nss-mdns 8 | downstream_package_name: nss-mdns 9 | upstream_tag_template: "v{version}" 10 | srpm_build_deps: [] 11 | csmock_args: --cppcheck-add-flag=--enable=style 12 | 13 | jobs: 14 | - job: copr_build 15 | trigger: pull_request 16 | targets: 17 | - fedora-all-aarch64 18 | - fedora-all-i386 19 | - fedora-all-ppc64le 20 | - fedora-all-s390x 21 | - fedora-all-x86_64 22 | - job: copr_build 23 | trigger: commit 24 | targets: 25 | - fedora-all-aarch64 26 | - fedora-all-i386 27 | - fedora-all-ppc64le 28 | - fedora-all-s390x 29 | - fedora-all-x86_64 30 | -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | - SHIROYAMA Takayuki, for patches 2 | - Anand Kumria, for patches 3 | - Bastien Nocera, for patches 4 | - Sjoerd Simons, for patches 5 | - Sean Meiners, for search list support 6 | - Philipp Zabel, for ARM support 7 | - Bruce M Simpson, for porting it to FreeBSD 8 | - Trent Lloyd, for migrating to GitHub 9 | - Google LLC, for patches 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, see . 488 | 489 | Also add information on how to contact you by electronic and paper mail. 490 | 491 | You should also get your employer (if you work as a programmer) or your 492 | school, if any, to sign a "copyright disclaimer" for the library, if 493 | necessary. Here is a sample; alter the names: 494 | 495 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 496 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 497 | 498 | , 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 | --------------------------------------------------------------------------------