├── .github └── workflows │ └── build.yml ├── COPYING ├── Makefile ├── README.md ├── compiler.h ├── doc ├── llmnr-query.1 └── llmnrd.8 ├── err.h ├── etc ├── llmnrd-init-script └── llmnrd.service ├── iface.c ├── iface.h ├── list.h ├── llmnr-packet.h ├── llmnr-query.c ├── llmnr.c ├── llmnr.h ├── llmnrd.c ├── log.c ├── log.h ├── pkt.h ├── socket.c ├── socket.h ├── util.c └── util.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | CC: [gcc, clang] 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Check out code 20 | uses: actions/checkout@v2 21 | 22 | - name: Build 23 | run: | 24 | make CC=${{ matrix.CC }} 25 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for llmnrd 2 | # 3 | # Copyright (C) 2014-2019 Tobias Klauser 4 | 5 | VERSION = 0.7 6 | 7 | # llmnrd binary 8 | D_P = llmnrd 9 | D_OBJS = llmnr.o iface.o socket.o util.o log.o llmnrd.o 10 | D_LIBS = 11 | D_MAN = $(D_P).8 12 | 13 | # llmnr-query binary 14 | Q_P = llmnr-query 15 | Q_OBJS = util.o log.o llmnr-query.o 16 | Q_LIBS = 17 | Q_MAN = $(Q_P).1 18 | 19 | CC ?= gcc 20 | INSTALL = install 21 | GZIP = gzip -9 -c 22 | 23 | CPPFLAGS ?= 24 | LDFLAGS ?= 25 | 26 | ifeq ($(shell git rev-parse > /dev/null 2>&1; echo $$?), 0) 27 | GIT_VERSION = "(git id $(shell git describe --always))" 28 | else 29 | GIT_VERSION = 30 | endif 31 | 32 | PIDFILE ?= /run/llmnrd/pid 33 | 34 | CFLAGS_MIN := -W -Wall \ 35 | -DVERSION_STRING=\"v$(VERSION)\" \ 36 | -DGIT_VERSION=\"$(GIT_VERSION)\" \ 37 | -DPIDFILE=\"$(PIDFILE)\" 38 | 39 | ifeq ($(DEBUG), 1) 40 | CFLAGS_MIN += -g -DDEBUG 41 | endif 42 | 43 | CFLAGS_WARN := -Wextra \ 44 | -Waggregate-return \ 45 | -Wbad-function-cast \ 46 | -Wdeclaration-after-statement \ 47 | -Wformat-nonliteral \ 48 | -Wformat-security \ 49 | -Wmissing-declarations \ 50 | -Wmissing-format-attribute \ 51 | -Wmissing-prototypes \ 52 | -Wsign-compare \ 53 | -Wstrict-prototypes \ 54 | -Wundef \ 55 | -Wunused \ 56 | -Wwrite-strings 57 | 58 | CFLAGS ?= -O2 $(CFLAGS_WARN) 59 | override CFLAGS := $(CFLAGS_MIN) $(CFLAGS) 60 | 61 | Q ?= @ 62 | ifeq ($(Q),) 63 | CCQ = $(CC) 64 | LDQ = $(CC) 65 | else 66 | CCQ = $(Q)echo " CC $<" && $(CC) 67 | LDQ = $(Q)echo " LD $@" && $(CC) 68 | endif 69 | 70 | prefix ?= /usr/local 71 | 72 | BINDIR = $(prefix)/bin 73 | SBINDIR = $(prefix)/sbin 74 | MAN1DIR = $(prefix)/share/man/man1 75 | MAN8DIR = $(prefix)/share/man/man8 76 | DESTDIR = 77 | 78 | all: $(D_P) $(Q_P) 79 | 80 | $(D_P): $(D_OBJS) 81 | $(LDQ) $(LDFLAGS) -o $@ $(D_OBJS) $(D_LIBS) 82 | 83 | $(Q_P): $(Q_OBJS) 84 | $(LDQ) $(LDFLAGS) -o $@ $(Q_OBJS) $(Q_LIBS) 85 | 86 | %.o: %.c %.h 87 | $(CCQ) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< 88 | 89 | %.o: %.c 90 | $(CCQ) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< 91 | 92 | install_$(D_P): $(D_P) 93 | @echo " INSTALL $(D_P)" 94 | $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(SBINDIR) 95 | $(Q)$(INSTALL) -m 755 $(D_P) $(DESTDIR)$(SBINDIR)/$(D_P) 96 | $(Q)$(GZIP) doc/$(D_MAN) > $(D_MAN).gz 97 | $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(MAN8DIR) 98 | $(Q)$(INSTALL) -m 644 $(D_MAN).gz $(DESTDIR)$(MAN8DIR)/$(D_MAN).gz 99 | 100 | install_$(Q_P): $(Q_P) 101 | @echo " INSTALL $(Q_P)" 102 | $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) 103 | $(Q)$(INSTALL) -m 755 $(Q_P) $(DESTDIR)$(BINDIR)/$(Q_P) 104 | $(Q)$(GZIP) doc/$(Q_MAN) > $(Q_MAN).gz 105 | $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(MAN1DIR) 106 | $(Q)$(INSTALL) -m 644 $(Q_MAN).gz $(DESTDIR)$(MAN1DIR)/$(Q_MAN).gz 107 | 108 | install: install_$(D_P) install_$(Q_P) 109 | 110 | clean: 111 | @echo " CLEAN" 112 | $(Q)rm -f $(D_OBJS) $(D_P) 113 | $(Q)rm -f $(Q_OBJS) $(Q_P) 114 | $(Q)rm -f $(D_P).8.gz 115 | $(Q)rm -f $(Q_P).1.gz 116 | 117 | # Maintainer targets 118 | 119 | GIT_TAG = git tag -a v$(VERSION) -s -m "llmnrd $(VERSION) release" 120 | GIT_ARCHIVE = git archive --prefix=llmnrd-$(VERSION)/ v$(VERSION) | \ 121 | $(1) -9 > ../llmnrd-$(VERSION).tar.$(2) 122 | GPG_SIGN = gpg -a --output ../llmnrd-$(VERSION).tar.$(1).asc --detach-sig \ 123 | ../llmnrd-$(VERSION).tar.$(1) 124 | release: 125 | $(GIT_TAG) 126 | $(call GIT_ARCHIVE,gzip,gz) 127 | $(call GIT_ARCHIVE,bzip2,bz2) 128 | $(call GPG_SIGN,gz) 129 | $(call GPG_SIGN,bz2) 130 | $(Q)echo "Created release $(VERSION)" 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # llmnrd - Link-Local Multicast Resolution Daemon 2 | 3 | [![GitHub Action Status](https://github.com/tklauser/llmnrd/workflows/Build/badge.svg)](https://github.com/tklauser/llmnrd/actions?query=workflow%3Abuild) 4 | [![Coverity Status](https://scan.coverity.com/projects/8697/badge.svg)](https://scan.coverity.com/projects/tklauser-llmnrd) 5 | 6 | llmnrd is a daemon implementing the Link-Local Multicast Name Resolution (LLMNR) 7 | protocol according to [RFC 4795](https://tools.ietf.org/html/rfc4795). It 8 | currently only supports Linux, as it uses the 9 | [netlink kernel interface](http://man7.org/linux/man-pages/man7/netlink.7.html). 10 | 11 | llmnrd will respond to name resolution queries sent by Windows clients in 12 | networks where no DNS server is available. It supports both IPv4 and IPv6. 13 | 14 | Installation 15 | ============ 16 | 17 | To build and install llmnrd use the following commands: 18 | 19 | ``` 20 | $ make 21 | $ sudo make install 22 | ``` 23 | 24 | By default, the llmnrd binary will be installed to /usr/local/sbin. To install 25 | the binary to a different installation path, use: 26 | 27 | ``` 28 | $ make 29 | $ sudo make prefix= install 30 | ``` 31 | 32 | Cross-Compilation 33 | ================= 34 | 35 | To cross-compile llmnrd for a different architecture, set the `CC` make 36 | variable to the corresponding cross-compiler. To e.g. build it using the 37 | arm-linux-gnueabihf toolchain use: 38 | 39 | ``` 40 | $ make CC=arm-linux-gnueabihf-gcc 41 | ``` 42 | 43 | When cross-compiling, you usually don't want to install the generated binary to 44 | your root filesystem, but to the sysroot of a cross-compiled system. Use the 45 | `DESTDIR` variable to change the installation destination path, e.g. 46 | 47 | ``` 48 | $ make DESTDIR=$HOME/sysroot/ prefix=/usr install 49 | ``` 50 | 51 | Usage 52 | ===== 53 | 54 | To run llmnrd in the default mode (listening on UDP port 5355): 55 | 56 | ``` 57 | $ llmnrd 58 | ``` 59 | 60 | By default, LLMNR name resolution is only possible over IPv4. To additionally 61 | enable LLMNR name resolution over IPv6 use: 62 | 63 | ``` 64 | $ llmnrd -6 65 | ``` 66 | 67 | Use `llmnrd --help` to show additional usage information. 68 | 69 | Additionally, the `llmnr-query` utility is shipped together with llmnrd and 70 | can be used to send customized LLMNR queries: 71 | 72 | ``` 73 | $ llmnr-query 74 | ``` 75 | 76 | Use `llmnr-query --help` to show additional usage information. 77 | 78 | License 79 | ======= 80 | 81 | llmnrd is free software: you can redistribute it and/or modify it under the 82 | terms of the GNU General Public License as published by the Free Software 83 | Foundation, version 2 of the License. 84 | 85 | llmnrd is distributed in the hope that it will be useful, but WITHOUT ANY 86 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 87 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 88 | 89 | Please see the [COPYING](https://github.com/tklauser/llmnrd/blob/master/COPYING) 90 | file for the full license text. 91 | 92 | Contributors 93 | ============ 94 | 95 | llmnrd is authored and maintained by Tobias Klauser 96 | 97 | The following people contributed patches and ideas, found and reported bugs or 98 | otherwise helped in the development of llmnrd: 99 | 100 | * Diego Santa Cruz (@diego-santacruz) 101 | * Elazar Leibovich (@elazarl) 102 | * Martin Hauke 103 | * Michael Evertz (@dvl-mevertz) 104 | * Pali Rohár 105 | * @Schimmelreiter 106 | * @svimik 107 | * @tbetker 108 | 109 | Thanks a lot! 110 | 111 | References 112 | ========== 113 | 114 | * [RFC 4795](https://tools.ietf.org/html/rfc4795) 115 | * [Microsoft TechNet article about LLMNR](https://technet.microsoft.com/en-us/library/bb878128.aspx) 116 | * [xllmnrd: An IPv6-only LLMNR responder daemon](http://www.vx68k.org/xllmnrd) ([Repository](https://bitbucket.org/kazssym/xllmnrd/)) 117 | -------------------------------------------------------------------------------- /compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2017 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef COMPILER_H 20 | #define COMPILER_H 21 | 22 | #ifdef __GNUC__ 23 | # define __noreturn __attribute__((noreturn)) 24 | # define __warn_unused_result __attribute__((warn_unused_result)) 25 | # ifndef __packed 26 | # define __packed __attribute__((packed)) 27 | # endif 28 | # ifndef __unused 29 | # define __unused __attribute__((unused)) 30 | # endif 31 | # ifndef __check_format_printf 32 | # define __check_format_printf(__fmt, __args) \ 33 | __attribute__ ((format (printf, (__fmt), (__args)))) 34 | # endif 35 | # ifndef offsetof 36 | # define offsetof(a, b) __builtin_offsetof(a, b) 37 | # endif 38 | #else 39 | # define __noreturn 40 | # define __packed 41 | # define __unused 42 | #endif 43 | 44 | #ifndef offsetof 45 | # define offsetof(type, member) ((size_t) &((type *)0)->member) 46 | #endif 47 | 48 | #ifndef container_of 49 | # define container_of(ptr, type, member) ({ \ 50 | const typeof(((type *)0)->member) *__mptr = (ptr); \ 51 | (type *)((char *)__mptr - offsetof(type, member));}) 52 | #endif 53 | 54 | #endif /* COMPILER_H */ 55 | -------------------------------------------------------------------------------- /doc/llmnr-query.1: -------------------------------------------------------------------------------- 1 | .TH LLMNR-QUERY 1 "07 March 2017" llmnrd llmnrd 2 | .SH NAME 3 | llmnr-query \- LLMNR (RFC 4795) query tool 4 | .SH SYNOPSIS 5 | \fBllmnr-query\fR [\fIoptions\fR] \fIname\fR 6 | .SH DESCRIPTION 7 | Send LLMNR queries to the LLMNR multicast address (224.0.0.252 for IPv4, 8 | ff02:0:0:0:0:0:1:3 for IPv6). \fIname\fR specifies the hostname to query. 9 | .SH OPTIONS 10 | .TP 11 | .B -c , --count 12 | Number of queries to send (default: 1). 13 | .TP 14 | .B -d , --id 15 | Set LLMNR transaction id (default: 0). 16 | .TP 17 | .B -i , --interval 18 | Set interval between queries in milliseconds (default: 500). 19 | .TP 20 | .B -I , --interface 21 | Send multicast query over specified interface, e.g. eth0, wlan0. 22 | .TP 23 | .B -t , --timeout 24 | Set time to wait for reply in milliseconds (default: 1000). 25 | .TP 26 | .B -T , --type 27 | Set query type. Must be one of A, AAAA, ANY (default: ANY). 28 | .TP 29 | .B -6, --ipv6 30 | Send queries over IPv6. 31 | .TP 32 | .B -h, --help 33 | Show usage information. 34 | .TP 35 | .B -V, --version 36 | Show llmnr-query version. 37 | .SH SEE ALSO 38 | .BR llmnrd(8) 39 | .SH AUTHOR 40 | Tobias Klauser 41 | -------------------------------------------------------------------------------- /doc/llmnrd.8: -------------------------------------------------------------------------------- 1 | .TH LLMNRD 8 "07 March 2017" llmnrd llmnrd 2 | .SH NAME 3 | llmnrd \- LLMNR (RFC 4795) responder daemon 4 | .SH SYNOPSIS 5 | \fBllmnrd\fR [\fIoptions\fR] 6 | .SH DESCRIPTION 7 | .B llmnrd 8 | is a user space daemon implementing the Link-Local Multicast Name Resolution 9 | (LLMNR) protocol according to RFC 4795. It will respond to name resolution 10 | queries sent by Windows clients in networks where no DNS server is available. 11 | Both IPv4 and IPv6 are supported. 12 | .SH OPTIONS 13 | .TP 14 | .B -H , --hostname 15 | Manually set hostname to respond with. By default the system hostname is used. 16 | .TP 17 | .B -i , --interface 18 | Bind socket to a specific interface, e.g. eth0, wlan0. 19 | .TP 20 | .B -p , --port 21 | Set port number to listen on. By default the port 5355 as specified in RFC 4795 is 22 | used. 23 | .TP 24 | .B -6, --ipv6 25 | Enable LLMNR name resolution over IPv6. 26 | .TP 27 | .B -d, --daemonize 28 | Run llmnrd as daemon in the background. 29 | .TP 30 | .B -h, --help 31 | Show usage information. 32 | .TP 33 | .B -V, --version 34 | Show llmnrd version. 35 | .SH SEE ALSO 36 | .BR llmnr-query(1) 37 | .SH AUTHOR 38 | Tobias Klauser 39 | -------------------------------------------------------------------------------- /err.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef ERR_H 20 | #define ERR_H 21 | 22 | static inline void *ERR_PTR(long err) 23 | { 24 | return (void *)err; 25 | } 26 | 27 | static inline long PTR_ERR(const void *ptr) 28 | { 29 | return (long)ptr; 30 | } 31 | 32 | #endif /* ERR_H */ 33 | -------------------------------------------------------------------------------- /etc/llmnrd-init-script: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # License: Copyright 2020 SpinetiX. This file is licensed 4 | # under the terms of the GNU General Public License version 2. 5 | # This program is licensed "as is" without any warranty of any 6 | # kind, whether express or implied. 7 | # 8 | # Copyright 1999-2003 MontaVista Software, Inc. 9 | # Copyright 2002, 2003, 2004 Sony Corporation 10 | # Copyright 2002, 2003, 2004 Matsushita Electric Industrial Co., Ltd. 11 | # 12 | ### BEGIN INIT INFO 13 | # Required-Start: 14 | # Required-Stop: 15 | # Should-Start: 16 | # Should-Stop: 17 | # Default-Start: 2 3 5 18 | # Default-Stop: 19 | # Short-Description: Starting/stopping llmnrd 20 | # Description: Starting/stopping llmnrd 21 | ### END INIT INFO 22 | 23 | # Init script information 24 | NAME=llmnrd 25 | DESC="llmnrd" 26 | 27 | # Individual Daemon information 28 | DAEMON=/usr/sbin/llmnrd 29 | ARGS="-6 -d -s" 30 | BASENAME=llmnrd 31 | 32 | # Load init script configuration 33 | DISABLE_LLMNRD= 34 | [ -f /etc/default/$NAME ] && . /etc/default/$NAME 35 | 36 | # Source the init script functions 37 | . /etc/init.d/functions 38 | 39 | # Verify daemons are installed 40 | if [ ! -x $DAEMON -a "$1" != "stop" ]; then 41 | echo -n "Not starting $DESC $NAME, $DAEMON not installed" 42 | echo 43 | exit 0 44 | fi 45 | 46 | if [ -n "$DISABLE_LLMNRD" -a "$1" != "stop" ]; then 47 | echo -n "Not starting $DESC $NAME, disabled via /etc/default/$NAME" 48 | echo 49 | exit 0 50 | fi 51 | 52 | start() { 53 | local RET 54 | 55 | echo -n "Starting $DESC: " 56 | 57 | echo -n "$NAME " 58 | 59 | start-stop-daemon --start -u llmnrd -n llmnrd -p /run/llmnrd/pid -c llmnrd -x $DAEMON -- $ARGS 60 | RET=$? 61 | if [ $RET -eq 0 ]; then 62 | success; echo 63 | else 64 | failure; echo 65 | return 1 66 | fi 67 | 68 | return 0 69 | } 70 | 71 | stop () { 72 | local RET 73 | 74 | echo -n "Stopping $DESC: $NAME " 75 | start-stop-daemon --stop -u llmnrd -n llmnrd -p /run/llmnrd/pid -q 76 | RET=$? 77 | if [ $RET -eq 0 ]; then 78 | success; echo 79 | else 80 | failure; echo 81 | return 1 82 | fi 83 | 84 | return 0 85 | } 86 | 87 | restart() { 88 | local RET 89 | 90 | echo "Restarting $DESC..." 91 | stop 92 | start 93 | RET=$? 94 | 95 | return $RET 96 | } 97 | 98 | condrestart() { 99 | local RET 100 | 101 | pidofproc $BASENAME >/dev/null 102 | RET=$? 103 | if [ $RET -eq 0 ]; then 104 | restart 105 | RET=$? 106 | else 107 | RET=1 108 | fi 109 | 110 | return $RET 111 | } 112 | 113 | reload() { 114 | local RET pid 115 | 116 | # llmnrd has no support for HUP, so just restart 117 | condrestart 118 | } 119 | 120 | forcereload() { 121 | local RET 122 | 123 | reload 124 | RET=$? 125 | if [ $RET -ne 0 ]; then 126 | restart 127 | RET=$? 128 | fi 129 | 130 | return $RET 131 | } 132 | 133 | parse() { 134 | case "$1" in 135 | start) 136 | start 137 | return $? 138 | ;; 139 | stop) 140 | stop 141 | return $? 142 | ;; 143 | restart) 144 | restart 145 | return $? 146 | ;; 147 | condrestart|try-restart) 148 | condrestart 149 | return $? 150 | ;; 151 | reload) 152 | reload 153 | return $? 154 | ;; 155 | force-reload) 156 | forcereload 157 | return $? 158 | ;; 159 | status) 160 | status $BASENAME 161 | return $? 162 | ;; 163 | *) 164 | echo "Usage: $NAME " \ 165 | "{start|stop|restart|condrestart|reload|force-reload|status}" >&2 166 | ;; 167 | esac 168 | 169 | return 1 170 | } 171 | 172 | parse $@ 173 | -------------------------------------------------------------------------------- /etc/llmnrd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Link-Local Multicast Name Resolution Daemon 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/sbin/llmnrd 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /iface.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2017 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "err.h" 35 | #include "list.h" 36 | #include "log.h" 37 | #include "socket.h" 38 | #include "util.h" 39 | 40 | #include "iface.h" 41 | 42 | static unsigned int iface_ifindex = 0; 43 | static iface_event_handler_t iface_event_handler; 44 | 45 | struct iface_record { 46 | struct list_head list; 47 | unsigned int index; 48 | struct sockaddr_storage *addrs; 49 | size_t size; 50 | }; 51 | 52 | static struct list_head iface_list_head; 53 | 54 | size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, 55 | struct sockaddr_storage *addrs, size_t addrs_size) 56 | { 57 | struct iface_record *rec; 58 | size_t n = 0; 59 | 60 | if (!addrs) 61 | return 0; 62 | 63 | list_for_each_entry(rec, &iface_list_head, list) { 64 | if (rec->index == ifindex) { 65 | size_t i; 66 | 67 | for (i = 0; i < rec->size && n < addrs_size; i++) { 68 | if (family == AF_UNSPEC || family == rec->addrs[i].ss_family) { 69 | memcpy(&addrs[n], &rec->addrs[i], sizeof(addrs[n])); 70 | n++; 71 | } 72 | } 73 | break; 74 | } 75 | } 76 | 77 | return n; 78 | } 79 | 80 | static bool iface_record_addr_eq(const struct sockaddr_storage *addr1, 81 | const struct sockaddr_storage *addr2) 82 | { 83 | int family = addr1->ss_family; 84 | 85 | if (family != addr2->ss_family) 86 | return false; 87 | 88 | if (family == AF_INET) { 89 | const struct sockaddr_in *sin1 = (const struct sockaddr_in *)addr1; 90 | const struct sockaddr_in *sin2 = (const struct sockaddr_in *)addr2; 91 | 92 | return memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)) == 0; 93 | } else if (family == AF_INET6) { 94 | const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)addr1; 95 | const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)addr2; 96 | 97 | return memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof(sin1->sin6_addr)) == 0; 98 | } else { 99 | /* This should never happen */ 100 | log_warn("Unsupported address family: %d\n", family); 101 | return memcmp(addr1, addr2, sizeof(*addr1)); 102 | } 103 | } 104 | 105 | static void iface_record_addr_add(struct iface_record *rec, struct sockaddr_storage *addr) 106 | { 107 | size_t i; 108 | struct sockaddr_storage *addrs = rec->addrs; 109 | 110 | for (i = 0; i < rec->size; i++) { 111 | /* Address already in record? */ 112 | if (iface_record_addr_eq(&addrs[i], addr)) 113 | return; 114 | } 115 | 116 | addrs = xrealloc(rec->addrs, (rec->size + 1) * sizeof(*addr)); 117 | memcpy(&addrs[rec->size], addr, sizeof(*addr)); 118 | rec->addrs = addrs; 119 | rec->size++; 120 | } 121 | 122 | static void iface_record_addr_del(struct iface_record *rec, struct sockaddr_storage *addr) 123 | { 124 | if (rec->size > 1) { 125 | size_t i, j = 0; 126 | struct sockaddr_storage *addrs = xmalloc((rec->size - 1) * sizeof(*addr)); 127 | 128 | for (i = 0; i < rec->size; i++) { 129 | if (!iface_record_addr_eq(&rec->addrs[i], addr)) { 130 | memcpy(&addrs[j], &rec->addrs[i], sizeof(addrs[j])); 131 | j++; 132 | } 133 | } 134 | 135 | if (j == i - 1) { 136 | free(rec->addrs); 137 | rec->addrs = addrs; 138 | rec->size--; 139 | } else { 140 | char as[NI_MAXHOST]; 141 | 142 | if (getnameinfo((struct sockaddr *)addr, sizeof(*addr), 143 | as, sizeof(as), NULL, 0, NI_NUMERICHOST)) 144 | strncpy(as, "", sizeof(as) - 1); 145 | log_err("Address %s to delete not found in records\n", as); 146 | 147 | free(addrs); 148 | } 149 | } else if (rec->size == 1) { 150 | free(rec->addrs); 151 | rec->addrs = NULL; 152 | rec->size = 0; 153 | } 154 | } 155 | 156 | static inline void fill_sockaddr_storage(struct sockaddr_storage *sst, 157 | unsigned char family, const void *addr) 158 | { 159 | sst->ss_family = family; 160 | if (family == AF_INET) { 161 | struct sockaddr_in *sin = (struct sockaddr_in *)sst; 162 | memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr)); 163 | } else if (family == AF_INET6) { 164 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sst; 165 | memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr)); 166 | } 167 | } 168 | 169 | static void iface_addr_add(unsigned int index, unsigned char family, const void *addr) 170 | { 171 | struct iface_record *rec; 172 | struct sockaddr_storage sst; 173 | 174 | fill_sockaddr_storage(&sst, family, addr); 175 | 176 | list_for_each_entry(rec, &iface_list_head, list) 177 | if (rec->index == index) 178 | goto add; 179 | 180 | rec = xzalloc(sizeof(*rec)); 181 | INIT_LIST_HEAD(&rec->list); 182 | rec->index = index; 183 | 184 | list_add_tail(&rec->list, &iface_list_head); 185 | add: 186 | iface_record_addr_add(rec, &sst); 187 | } 188 | 189 | static void iface_addr_del(unsigned int index, unsigned char family, const void *addr) 190 | { 191 | struct iface_record *rec; 192 | struct sockaddr_storage sst; 193 | 194 | fill_sockaddr_storage(&sst, family, addr); 195 | 196 | list_for_each_entry(rec, &iface_list_head, list) { 197 | if (rec->index == index) { 198 | iface_record_addr_del(rec, &sst); 199 | break; 200 | } 201 | } 202 | } 203 | 204 | static void iface_nlmsg_change_link(const struct nlmsghdr *nlh __unused) 205 | { 206 | /* TODO */ 207 | } 208 | 209 | static void iface_nlmsg_change_addr(const struct nlmsghdr *nlh) 210 | { 211 | struct ifaddrmsg *ifa = NLMSG_DATA(nlh); 212 | struct rtattr *rta; 213 | size_t rtalen = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifa)); 214 | unsigned char family = ifa->ifa_family; 215 | unsigned int index = ifa->ifa_index; 216 | char ifname[IF_NAMESIZE]; 217 | 218 | /* don't report temporary addresses */ 219 | if ((ifa->ifa_flags & (IFA_F_TEMPORARY | IFA_F_TENTATIVE)) != 0) 220 | return; 221 | 222 | /* 223 | * If bound to a specific interface, don't report addresses of any other 224 | * interface. 225 | */ 226 | if (iface_ifindex > 0 && index != iface_ifindex) 227 | return; 228 | 229 | if_indextoname(index, ifname); 230 | 231 | rta = (struct rtattr *)((const uint8_t *)nlh + NLMSG_SPACE(sizeof(*ifa))); 232 | for ( ; RTA_OK(rta, rtalen); rta = RTA_NEXT(rta, rtalen)) { 233 | char addr[INET6_ADDRSTRLEN]; 234 | enum iface_event_type type; 235 | 236 | if (rta->rta_type != IFA_ADDRESS) 237 | continue; 238 | 239 | if (!inet_ntop(family, RTA_DATA(rta), addr, sizeof(addr))) 240 | strncpy(addr, "", sizeof(addr) - 1); 241 | 242 | if (nlh->nlmsg_type == RTM_NEWADDR) { 243 | iface_addr_add(index, family, RTA_DATA(rta)); 244 | type = IFACE_ADD; 245 | } else if (nlh->nlmsg_type == RTM_DELADDR) { 246 | iface_addr_del(index, family, RTA_DATA(rta)); 247 | type = IFACE_DEL; 248 | } else { 249 | /* This case shouldn't occur */ 250 | continue; 251 | } 252 | 253 | if (iface_event_handler) 254 | (*iface_event_handler)(type, family, index); 255 | 256 | log_info("%s IPv%c address %s on interface %s\n", 257 | type == IFACE_ADD ? "Added" : "Deleted", 258 | family == AF_INET ? '4' : '6', addr, ifname); 259 | } 260 | } 261 | 262 | static int iface_nlmsg_process(const struct nlmsghdr *nlh, size_t len) 263 | { 264 | for ( ; len > 0; nlh = NLMSG_NEXT(nlh, len)) { 265 | struct nlmsgerr *err; 266 | 267 | if (!NLMSG_OK(nlh, len)) { 268 | log_err("netlink message truncated\n"); 269 | return -1; 270 | } 271 | 272 | switch (nlh->nlmsg_type) { 273 | case RTM_NEWADDR: 274 | case RTM_DELADDR: 275 | iface_nlmsg_change_addr(nlh); 276 | break; 277 | case RTM_NEWLINK: 278 | case RTM_DELLINK: 279 | iface_nlmsg_change_link(nlh); 280 | break; 281 | case NLMSG_ERROR: 282 | err = NLMSG_DATA(nlh); 283 | log_err("netlink error: %s\n", strerror(-(err->error))); 284 | break; 285 | case NLMSG_DONE: 286 | if (!NLMSG_OK(nlh, len)) { 287 | log_err("netlink message truncated\n"); 288 | return -1; 289 | } else 290 | return 0; 291 | default: 292 | /* log_warn("Unknown netlink message type: 0x%x\n", nlh->nlmsg_type); */ 293 | break; 294 | } 295 | } 296 | 297 | return 0; 298 | } 299 | 300 | static void iface_rtnl_enumerate(int sock, uint16_t type, unsigned char family) 301 | { 302 | struct { 303 | struct nlmsghdr n; 304 | struct rtgenmsg r; 305 | } req; 306 | 307 | memset(&req, 0, sizeof(req)); 308 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); 309 | req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 310 | req.n.nlmsg_type = type; 311 | req.r.rtgen_family = family; 312 | 313 | if (send(sock, &req, req.n.nlmsg_len, 0) < 0) { 314 | log_err("Failed to send netlink enumeration message: %s\n", strerror(errno)); 315 | return; 316 | } 317 | 318 | if (iface_recv(sock) < 0) 319 | log_err("Failed to enumerate rtnl interfaces: %s\n", strerror(errno)); 320 | } 321 | 322 | int iface_init(int sock, const char *iface, bool ipv6, 323 | iface_event_handler_t event_handler) 324 | { 325 | INIT_LIST_HEAD(&iface_list_head); 326 | iface_event_handler = event_handler; 327 | if (iface) { 328 | iface_ifindex = if_nametoindex(iface); 329 | if (!iface_ifindex) { 330 | log_err("Interface %s not found: %s\n", iface, strerror(errno)); 331 | return -1; 332 | } 333 | } 334 | 335 | /* send RTM_GETADDR request to initially populate the interface list */ 336 | iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET); 337 | if (ipv6) 338 | iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET6); 339 | return 0; 340 | } 341 | 342 | int iface_recv(int sock) 343 | { 344 | ssize_t recvlen; 345 | uint8_t pktbuf[8192]; 346 | 347 | if ((recvlen = recv(sock, pktbuf, sizeof(pktbuf), 0)) < 0) { 348 | if (errno != EINTR) 349 | log_err("Failed to receive netlink message: %s\n", strerror(errno)); 350 | return -1; 351 | } 352 | 353 | return iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen); 354 | } 355 | -------------------------------------------------------------------------------- /iface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2017 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef IFACE_H 20 | #define IFACE_H 21 | 22 | #include 23 | #include 24 | 25 | enum iface_event_type { 26 | IFACE_ADD, 27 | IFACE_DEL, 28 | }; 29 | 30 | typedef void (*iface_event_handler_t)(enum iface_event_type, unsigned char af, 31 | unsigned int ifindex); 32 | 33 | int iface_init(int sock, const char *iface, bool ipv6, 34 | iface_event_handler_t event_handler); 35 | int iface_recv(int sock); 36 | 37 | size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, 38 | struct sockaddr_storage *addrs, size_t addrs_size); 39 | 40 | #endif /* IFACE_H */ 41 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple doubly linked list, based on the Linux kernel linked list. 3 | * 4 | * Copyright (C) 2015 Tobias Klauser 5 | * 6 | * This file is part of llmnrd. 7 | * 8 | * llmnrd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 2 of the License. 11 | * 12 | * llmnrd 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 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with llmnrd. If not, see . 19 | */ 20 | 21 | #ifndef LIST_H 22 | #define LIST_H 23 | 24 | #include 25 | 26 | #include "compiler.h" 27 | 28 | struct list_head { 29 | struct list_head *next, *prev; 30 | }; 31 | 32 | static inline void INIT_LIST_HEAD(struct list_head *list) 33 | { 34 | list->next = list; 35 | list->prev = list; 36 | } 37 | 38 | static inline void __list_add(struct list_head *obj, 39 | struct list_head *prev, 40 | struct list_head *next) 41 | { 42 | prev->next = obj; 43 | obj->prev = prev; 44 | obj->next = next; 45 | next->prev = obj; 46 | } 47 | 48 | static inline void list_add_tail(struct list_head *obj, struct list_head *head) 49 | { 50 | __list_add(obj, head->prev, head); 51 | } 52 | 53 | static inline void list_add_head(struct list_head *obj, struct list_head *head) 54 | { 55 | __list_add(obj, head, head->next); 56 | } 57 | 58 | static inline void list_del(struct list_head *obj) 59 | { 60 | obj->next->prev = obj->prev; 61 | obj->prev->next = obj->next; 62 | } 63 | 64 | static inline bool list_empty(struct list_head *head) 65 | { 66 | return head->next == head; 67 | } 68 | 69 | #define list_entry(ptr, type, member) container_of(ptr, type, member) 70 | 71 | #define list_for_each_entry(pos, head, member) \ 72 | for (pos = list_entry((head)->next, typeof(*pos), member); \ 73 | &(pos)->member != (head); \ 74 | (pos) = list_entry((pos)->member.next, typeof(*(pos)), member)) 75 | 76 | #define list_for_each_entry_safe(pos, n, head, member) \ 77 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 78 | n = list_entry(pos->member.next, typeof(*pos), member); \ 79 | &(pos)->member != (head); \ 80 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 81 | 82 | #endif /* LIST_H */ 83 | -------------------------------------------------------------------------------- /llmnr-packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LLMNR (RFC 4795) packet format definitions 3 | * 4 | * Copyright (C) 2015-2016 Tobias Klauser 5 | * 6 | * This file is part of llmnrd. 7 | * 8 | * llmnrd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 2 of the License. 11 | * 12 | * llmnrd 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 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with llmnrd. If not, see . 19 | */ 20 | 21 | #ifndef LLMNR_PACKET_H 22 | #define LLMNR_PACKET_H 23 | 24 | #include 25 | 26 | #include "compiler.h" 27 | 28 | #define LLMNR_IPV4_MCAST_ADDR "224.0.0.252" 29 | #define LLMNR_IPV6_MCAST_ADDR "ff02:0:0:0:0:0:1:3" 30 | 31 | #define LLMNR_UDP_PORT 5355 32 | 33 | /* 34 | * LLMNR packet header (RFC 4795, section 2.1.1) 35 | */ 36 | struct llmnr_hdr { 37 | uint16_t id; 38 | uint16_t flags; 39 | #define LLMNR_F_QR 0x8000 40 | #define LLMNR_F_OPCODE 0x7800 41 | #define LLMNR_F_C 0x0400 42 | #define LLMNR_F_TC 0x0200 43 | #define LLMNR_F_T 0x0100 44 | #define LLMNR_F_RCODE 0x000f 45 | uint16_t qdcount; 46 | uint16_t ancount; 47 | uint16_t nscount; 48 | uint16_t arcount; 49 | } __packed; 50 | 51 | /* Maximum label length according to RFC 1035 */ 52 | #define LLMNR_LABEL_MAX_SIZE 63 53 | 54 | /* TYPE values according to RFC 1035, section 3.2.2 */ 55 | #define LLMNR_TYPE_A 1 56 | #define LLMNR_TYPE_NS 2 57 | #define LLMNR_TYPE_CNAME 5 58 | #define LLMNR_TYPE_SOA 6 59 | #define LLMNR_TYPE_PTR 12 60 | #define LLMNR_TYPE_HINFO 13 61 | #define LLMNR_TYPE_MINFO 14 62 | #define LLMNR_TYPE_MX 15 63 | #define LLMNR_TYPE_TXT 16 64 | #define LLMNR_TYPE_AAAA 28 /* RFC 3596 */ 65 | 66 | /* QTYPE values according to RFC 1035, section 3.2.3 */ 67 | #define LLMNR_QTYPE_A LLMNR_TYPE_A 68 | #define LLMNR_QTYPE_AAAA LLMNR_TYPE_AAAA 69 | #define LLMNR_QTYPE_ANY 255 70 | 71 | /* CLASS values */ 72 | #define LLMNR_CLASS_IN 1 73 | 74 | /* QCLASS values */ 75 | #define LLMNR_QCLASS_IN LLMNR_CLASS_IN 76 | 77 | /* Default RR TTL in seconds (RFC 4795, section 2.8) */ 78 | #define LLMNR_TTL_DEFAULT 30 79 | 80 | #endif /* LLMNR_PACKET_H */ 81 | -------------------------------------------------------------------------------- /llmnr-query.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple LLMNR (RFC 4795) query tool. 3 | * 4 | * Copyright (C) 2015-2018 Tobias Klauser 5 | * 6 | * This file is part of llmnrd. 7 | * 8 | * llmnrd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 2 of the License. 11 | * 12 | * llmnrd 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 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with llmnrd. If not, see . 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | #define __APPLE_USE_RFC_3542 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "compiler.h" 38 | #include "llmnr-packet.h" 39 | #include "log.h" 40 | #include "pkt.h" 41 | 42 | /* Maximum possible size RFC 4795, section 2.1 */ 43 | static const size_t LLMNR_QUERY_PKT_BUF_SIZE = 9194; 44 | 45 | static const char *short_ops = "c:d:i:I:t:T:6ChV"; 46 | static const struct option long_opts[] = { 47 | { "count", required_argument, NULL, 'c' }, 48 | { "id", required_argument, NULL, 'd' }, 49 | { "interval", required_argument, NULL, 'i' }, 50 | { "interface", required_argument, NULL, 'I' }, 51 | { "timeout", required_argument, NULL, 't' }, 52 | { "type", required_argument, NULL, 'T' }, 53 | { "conflict", no_argument, NULL, 'C' }, 54 | { "ipv6", no_argument, NULL, '6' }, 55 | { "help", no_argument, NULL, 'h' }, 56 | { "version", no_argument, NULL, 'V' }, 57 | { NULL, 0, NULL, 0 }, 58 | }; 59 | 60 | static void __noreturn usage_and_exit(int status) 61 | { 62 | fprintf(stdout, "Usage: llmnr-query [OPTIONS...] NAME\n" 63 | "Options:\n" 64 | " -c, --count NUM number of queries to send (default: 1)\n" 65 | " -d, --id NUM set LLMNR transaction id (default: 0)\n" 66 | " -i, --interval NUM interval between queries in ms (default: 500)\n" 67 | " -I, --interface NAME send multicast over specified interface\n" 68 | " -t, --timeout NUM time to wait for reply in ms (default: 1000)\n" 69 | " -T, --type TYPE set query type; must be one of A, AAAA, ANY (default: ANY)\n" 70 | " -6, --ipv6 send queries over IPv6\n" 71 | " -C, --conflict set conflict bit in query\n" 72 | " -h, --help show this help and exit\n" 73 | " -V, --version show version information and exit\n"); 74 | exit(status); 75 | } 76 | 77 | static void __noreturn version_and_exit(void) 78 | { 79 | fprintf(stdout, "llmnr-query %s %s\n" 80 | "Copyright (C) 2015-2018 Tobias Klauser \n" 81 | "Licensed under the GNU General Public License, version 2\n", 82 | VERSION_STRING, GIT_VERSION); 83 | exit(EXIT_SUCCESS); 84 | } 85 | 86 | static const char *query_type(uint16_t qtype) 87 | { 88 | switch (qtype) { 89 | case LLMNR_QTYPE_A: 90 | return "A"; 91 | case LLMNR_QTYPE_AAAA: 92 | return "AAAA"; 93 | case LLMNR_QTYPE_ANY: 94 | return "ANY"; 95 | default: 96 | return ""; 97 | } 98 | } 99 | 100 | int main(int argc, char **argv) 101 | { 102 | int c, sock; 103 | const char *query_name, *iface = NULL; 104 | size_t query_name_len; 105 | unsigned long i, id = 0, count = 1, interval_ms = 500, timeout_ms = 1000; 106 | uint16_t flags = 0; 107 | uint16_t qtype = LLMNR_QTYPE_ANY; 108 | bool ipv6 = false; 109 | struct pkt *p; 110 | unsigned int ifindex = 0; 111 | static const int TTL = 255; 112 | 113 | while ((c = getopt_long(argc, argv, short_ops, long_opts, NULL)) != -1) { 114 | switch (c) { 115 | case 'c': 116 | count = strtoul(optarg, NULL, 0); 117 | break; 118 | case 'C': 119 | flags |= LLMNR_F_C; 120 | break; 121 | case 'd': 122 | id = strtoul(optarg, NULL, 0); 123 | break; 124 | case 'i': 125 | interval_ms = strtoul(optarg, NULL, 0); 126 | break; 127 | case 'I': 128 | iface = xstrdup(optarg); 129 | break; 130 | case 't': 131 | timeout_ms = strtoul(optarg, NULL, 0); 132 | break; 133 | case 'T': 134 | if (xstreq("A", optarg)) 135 | qtype = LLMNR_QTYPE_A; 136 | else if (xstreq("AAAA", optarg)) 137 | qtype = LLMNR_QTYPE_AAAA; 138 | else if (xstreq("ANY", optarg)) 139 | qtype = LLMNR_QTYPE_ANY; 140 | else { 141 | printf("Invalid query type: %s\n", optarg); 142 | usage_and_exit(EXIT_FAILURE); 143 | } 144 | break; 145 | case '6': 146 | ipv6 = true; 147 | break; 148 | case 'V': 149 | version_and_exit(); 150 | /* fall through */ 151 | case 'h': 152 | usage_and_exit(EXIT_SUCCESS); 153 | default: 154 | usage_and_exit(EXIT_FAILURE); 155 | } 156 | } 157 | 158 | if (optind >= argc) 159 | usage_and_exit(EXIT_FAILURE); 160 | 161 | query_name = argv[optind]; 162 | query_name_len = strlen(query_name); 163 | if (query_name_len > UINT8_MAX) { 164 | log_err("Query name too long\n"); 165 | return -1; 166 | } 167 | 168 | sock = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); 169 | if (sock < 0) { 170 | log_err("Failed to open UDP socket: %s\n", strerror(errno)); 171 | return -1; 172 | } 173 | 174 | if (ipv6) { 175 | /* RFC 4795, section 2.5 recommends to set TTL to 255 for UDP */ 176 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &TTL, sizeof(TTL)) < 0) { 177 | log_err("Failed to set IPv6 unicast hops socket option: %s\n", strerror(errno)); 178 | goto err; 179 | } 180 | 181 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &TTL, sizeof(TTL)) < 0) { 182 | log_err("Failed to set IPv6 multicast hops socket option: %s\n", strerror(errno)); 183 | goto err; 184 | } 185 | } else { 186 | /* RFC 4795, section 2.5 recommends to set TTL to 255 for UDP */ 187 | if (setsockopt(sock, IPPROTO_IP, IP_TTL, &TTL, sizeof(TTL)) < 0) { 188 | log_err("Failed to set IPv4 unicast TTL socket option: %s\n", strerror(errno)); 189 | goto err; 190 | } 191 | 192 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &TTL, sizeof(TTL)) < 0) { 193 | log_err("Failed to set IPv4 multicast TTL socket option: %s\n", strerror(errno)); 194 | goto err; 195 | } 196 | } 197 | 198 | if (iface != NULL) { 199 | ifindex = if_nametoindex(iface); 200 | 201 | if (ifindex == 0 && errno != 0) { 202 | log_err("Could not get interface %s: %s\n", iface, strerror(errno)); 203 | goto err; 204 | } 205 | 206 | if (ipv6) { 207 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) { 208 | log_err("Failed to set interface '%s' for IPv6 multicast socket: %s\n", 209 | iface, strerror(errno)); 210 | goto err; 211 | } 212 | } else { 213 | struct ip_mreqn mreq; 214 | 215 | memset(&mreq, 0, sizeof(mreq)); 216 | mreq.imr_ifindex = ifindex; 217 | 218 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) { 219 | log_err("Failed to set interface '%s' for IPv4 multicast socket: %s\n", 220 | iface, strerror(errno)); 221 | goto err; 222 | } 223 | } 224 | } 225 | 226 | p = pkt_alloc(LLMNR_QUERY_PKT_BUF_SIZE); 227 | 228 | log_info("LLMNR query: %s IN %s\n", query_name, query_type(qtype)); 229 | 230 | for (i = 0; i < count; i++) { 231 | struct llmnr_hdr *hdr; 232 | struct sockaddr_storage sst; 233 | socklen_t sst_len; 234 | struct iovec iov[1]; 235 | struct msghdr msg; 236 | struct in6_pktinfo ipi6; 237 | uint8_t c[CMSG_SPACE(sizeof(ipi6))]; 238 | size_t query_pkt_len; 239 | fd_set rfds; 240 | struct timeval tv; 241 | int ret; 242 | 243 | hdr = (struct llmnr_hdr *)pkt_put(p, sizeof(*hdr)); 244 | hdr->id = htons(id + i % UINT16_MAX); 245 | hdr->flags = htons(flags); 246 | hdr->qdcount = htons(1); 247 | hdr->ancount = 0; 248 | hdr->nscount = 0; 249 | hdr->arcount = 0; 250 | 251 | pkt_put_u8(p, (uint8_t)query_name_len); 252 | memcpy(pkt_put(p, query_name_len), query_name, query_name_len); 253 | pkt_put_u8(p, 0); 254 | 255 | pkt_put_u16(p, htons(qtype)); 256 | pkt_put_u16(p, htons(LLMNR_QCLASS_IN)); 257 | 258 | memset(&sst, 0, sizeof(sst)); 259 | memset(&iov, 0, sizeof(iov)); 260 | memset(&msg, 0, sizeof(msg)); 261 | memset(&ipi6, 0, sizeof(ipi6)); 262 | 263 | if (ipv6) { 264 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&sst; 265 | struct cmsghdr *cmsg; 266 | 267 | sin6->sin6_family = AF_INET6; 268 | sin6->sin6_port = htons(LLMNR_UDP_PORT); 269 | if (inet_pton(AF_INET6, LLMNR_IPV6_MCAST_ADDR, &sin6->sin6_addr) != 1) { 270 | log_err("Failed to convert IPv6 address: %s\n", strerror(errno)); 271 | break; 272 | } 273 | sst_len = sizeof(*sin6); 274 | 275 | ipi6.ipi6_ifindex = ifindex; 276 | msg.msg_control = c; 277 | msg.msg_controllen = sizeof(c); 278 | cmsg = CMSG_FIRSTHDR(&msg); 279 | cmsg->cmsg_level = IPPROTO_IPV6; 280 | cmsg->cmsg_type = IPV6_PKTINFO; 281 | cmsg->cmsg_len = CMSG_LEN(sizeof(ipi6)); 282 | memcpy(CMSG_DATA(cmsg), &ipi6, sizeof(ipi6)); 283 | } else { 284 | struct sockaddr_in *sin = (struct sockaddr_in *)&sst; 285 | 286 | sin->sin_family = AF_INET; 287 | sin->sin_port = htons(LLMNR_UDP_PORT); 288 | if (inet_pton(AF_INET, LLMNR_IPV4_MCAST_ADDR, &sin->sin_addr) != 1) { 289 | log_err("Failed to convert IPv4 address: %s\n", strerror(errno)); 290 | break; 291 | } 292 | sst_len = sizeof(*sin); 293 | } 294 | 295 | iov[0].iov_base = p->data; 296 | iov[0].iov_len = pkt_len(p); 297 | msg.msg_iov = iov; 298 | msg.msg_iovlen = 1; 299 | msg.msg_name = &sst; 300 | msg.msg_namelen = sst_len; 301 | 302 | if (sendmsg(sock, &msg, 0) < 0) { 303 | log_err("Failed to send UDP packet: %s\n", strerror(errno)); 304 | break; 305 | } 306 | 307 | FD_ZERO(&rfds); 308 | FD_SET(sock, &rfds); 309 | 310 | tv.tv_sec = timeout_ms / 1000; 311 | tv.tv_usec = (timeout_ms % 1000) * 1000; 312 | 313 | ret = select(sock + 1, &rfds, NULL, NULL, &tv); 314 | if (ret < 0) { 315 | log_err("Failed to select() on socket: %s\n", strerror(errno)); 316 | break; 317 | } else if (ret) { 318 | uint16_t j, ancount; 319 | 320 | /* save query length and re-use packet for RX */ 321 | query_pkt_len = pkt_len(p) - sizeof(*hdr); 322 | pkt_reset(p); 323 | 324 | if (recv(sock, p->data, p->size, 0) < 0) { 325 | log_err("Failed to receive from socket: %s\n", strerror(errno)); 326 | break; 327 | } 328 | 329 | hdr = (struct llmnr_hdr *)pkt_put(p, sizeof(*hdr)); 330 | ancount = htons(hdr->ancount); 331 | 332 | if (ancount == 0) { 333 | log_info("LLMNR response: no answer records returned\n"); 334 | continue; 335 | } 336 | 337 | /* skip the original query */ 338 | pkt_put(p, query_pkt_len); 339 | 340 | for (j = 0; j < ancount; ++j) { 341 | uint8_t nl = pkt_put_extract_u8(p); 342 | char addr[INET6_ADDRSTRLEN + 1]; 343 | uint16_t type, clss, addr_size; 344 | uint32_t ttl; 345 | char name[LLMNR_LABEL_MAX_SIZE + 1]; 346 | int af; 347 | 348 | /* compression? */ 349 | if (nl & 0xC0) { 350 | uint16_t ptr = (nl & 0x3F) << 8 | pkt_put_extract_u8(p); 351 | if (ptr < p->size - 1) { 352 | uint8_t nnl = p->data[ptr]; 353 | strncpy(name, (char *)&p->data[ptr + 1], nnl); 354 | name[nnl] = '\0'; 355 | } else 356 | strncpy(name, "", LLMNR_LABEL_MAX_SIZE); 357 | } else { 358 | strncpy(name, (char *)pkt_put(p, nl + 1), nl); 359 | name[nl] = '\0'; 360 | } 361 | 362 | type = htons(pkt_put_extract_u16(p)); 363 | clss = htons(pkt_put_extract_u16(p)); 364 | 365 | if (clss != LLMNR_CLASS_IN) 366 | log_warn("Unexpected response class received: %d\n", clss); 367 | 368 | ttl = htonl(pkt_put_extract_u32(p)); 369 | addr_size = htons(pkt_put_extract_u16(p)); 370 | 371 | if (addr_size == sizeof(struct in_addr)) { 372 | af = AF_INET; 373 | } else if (addr_size == sizeof(struct in6_addr)) { 374 | af = AF_INET6; 375 | } else { 376 | log_warn("Unexpected address size received: %d\n", addr_size); 377 | break; 378 | } 379 | 380 | memcpy(&sst, pkt_put(p, addr_size), addr_size); 381 | if (!inet_ntop(af, &sst, addr, ARRAY_SIZE(addr))) 382 | strncpy(addr, "", sizeof(addr)); 383 | addr[INET6_ADDRSTRLEN] = '\0'; 384 | 385 | log_info("LLMNR response: %s IN %s %s (TTL %d)\n", name, query_type(type), addr, ttl); 386 | } 387 | } else 388 | log_info("No LLMNR response received within timeout (%lu ms)\n", timeout_ms); 389 | 390 | if (i < count - 1) { 391 | pkt_reset(p); 392 | usleep(interval_ms * 1000); 393 | } 394 | } 395 | 396 | pkt_free(p); 397 | err: 398 | close(sock); 399 | return 0; 400 | } 401 | -------------------------------------------------------------------------------- /llmnr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2017 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "iface.h" 33 | #include "log.h" 34 | #include "pkt.h" 35 | #include "socket.h" 36 | 37 | #include "iface.h" 38 | #include "llmnr-packet.h" 39 | #include "llmnr.h" 40 | 41 | static bool llmnr_ipv6 = false; 42 | /* Host name in DNS name format (length octet + name + 0 byte) */ 43 | static char llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 2]; 44 | 45 | void llmnr_set_hostname(const char *hostname) 46 | { 47 | llmnr_hostname[0] = strlen(hostname); 48 | strncpy(&llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE); 49 | llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0'; 50 | } 51 | 52 | void llmnr_init(const char *hostname, bool ipv6) 53 | { 54 | llmnr_set_hostname(hostname); 55 | llmnr_ipv6 = ipv6; 56 | } 57 | 58 | static bool llmnr_name_matches(const uint8_t *query) 59 | { 60 | uint8_t n = llmnr_hostname[0]; 61 | 62 | /* length */ 63 | if (query[0] != n) 64 | return false; 65 | /* NULL byte */ 66 | if (query[1 + n] != 0) 67 | return false; 68 | 69 | return strncasecmp((const char *)&query[1], &llmnr_hostname[1], n) == 0; 70 | } 71 | 72 | static void llmnr_respond(unsigned int ifindex, const struct llmnr_hdr *hdr, 73 | const uint8_t *query, size_t query_len, int sock, 74 | const struct sockaddr_storage *sst) 75 | { 76 | uint16_t qtype, qclass; 77 | uint8_t name_len = query[0]; 78 | /* skip name length & additional '\0' byte */ 79 | const uint8_t *query_name_end = query + name_len + 2; 80 | size_t i, n, response_len; 81 | unsigned char family = AF_UNSPEC; 82 | /* 83 | * arbitrary restriction to 16 addresses per interface for the 84 | * sake of a simple, atomic interface 85 | */ 86 | struct sockaddr_storage addrs[16]; 87 | struct pkt *p; 88 | struct llmnr_hdr *r; 89 | 90 | /* 4 bytes expected for QTYPE and QCLASS */ 91 | if ((query_len - name_len - 2) < (sizeof(qtype) + sizeof(qclass))) 92 | return; 93 | 94 | memcpy(&qtype, query_name_end, sizeof(qtype)); 95 | qtype = ntohs(qtype); 96 | memcpy(&qclass, query_name_end + sizeof(qtype), sizeof(qclass)); 97 | qclass = ntohs(qclass); 98 | 99 | /* Only IN queries supported */ 100 | if (qclass != LLMNR_QCLASS_IN) 101 | return; 102 | 103 | /* No AAAA responses if IPv6 is disabled */ 104 | if (!llmnr_ipv6 && qtype == LLMNR_QTYPE_AAAA) 105 | return; 106 | 107 | switch (qtype) { 108 | case LLMNR_QTYPE_A: 109 | family = AF_INET; 110 | break; 111 | case LLMNR_QTYPE_AAAA: 112 | family = AF_INET6; 113 | break; 114 | case LLMNR_QTYPE_ANY: 115 | family = AF_UNSPEC; 116 | break; 117 | default: 118 | return; 119 | } 120 | 121 | n = iface_addr_lookup(ifindex, family, addrs, ARRAY_SIZE(addrs)); 122 | /* Don't respond if no address was found for the given interface */ 123 | if (n == 0) 124 | return; 125 | 126 | /* 127 | * This is the max response length (i.e. using all IPv6 addresses and 128 | * no message compression). We might not use all of it. 129 | */ 130 | response_len = n * (1 + name_len + 1 + 2 + 2 + 4 + 2 + sizeof(struct in6_addr)); 131 | p = pkt_alloc(sizeof(*hdr) + query_len + response_len); 132 | 133 | /* fill the LLMNR header */ 134 | r = (struct llmnr_hdr *)pkt_put(p, sizeof(*r)); 135 | r->id = hdr->id; 136 | /* response flag */ 137 | r->flags = htons(LLMNR_F_QR); 138 | r->qdcount = hdr->qdcount; 139 | r->ancount = htons(n); 140 | r->nscount = 0; 141 | r->arcount = 0; 142 | 143 | /* copy the original question */ 144 | memcpy(pkt_put(p, query_len), query, query_len); 145 | 146 | /* append an RR for each address */ 147 | for (i = 0; i < n; i++) { 148 | void *addr; 149 | size_t addr_size; 150 | uint16_t type; 151 | 152 | if (addrs[i].ss_family == AF_INET) { 153 | struct sockaddr_in *sin = (struct sockaddr_in *)&addrs[i]; 154 | addr = &sin->sin_addr; 155 | addr_size = sizeof(sin->sin_addr); 156 | type = LLMNR_TYPE_A; 157 | } else if (addrs[i].ss_family == AF_INET6) { 158 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addrs[i]; 159 | addr = &sin6->sin6_addr; 160 | addr_size = sizeof(sin6->sin6_addr); 161 | type = LLMNR_TYPE_AAAA; 162 | } else 163 | continue; 164 | 165 | /* NAME */ 166 | if (i == 0) 167 | memcpy(pkt_put(p, llmnr_hostname[0] + 2), llmnr_hostname, llmnr_hostname[0] + 2); 168 | else { 169 | /* message compression (RFC 1035, section 4.1.3) */ 170 | uint16_t ptr = 0xC000 | (sizeof(*hdr) + query_len); 171 | pkt_put_u16(p, ntohs(ptr)); 172 | } 173 | /* TYPE */ 174 | pkt_put_u16(p, htons(type)); 175 | /* CLASS */ 176 | pkt_put_u16(p, htons(LLMNR_CLASS_IN)); 177 | /* TTL */ 178 | pkt_put_u32(p, htonl(LLMNR_TTL_DEFAULT)); 179 | /* RDLENGTH */ 180 | pkt_put_u16(p, htons(addr_size)); 181 | /* RDATA */ 182 | memcpy(pkt_put(p, addr_size), addr, addr_size); 183 | } 184 | 185 | if (sendto(sock, p->data, pkt_len(p), 0, (struct sockaddr *)sst, sizeof(*sst)) < 0) 186 | log_err("Failed to send response: %s\n", strerror(errno)); 187 | 188 | pkt_free(p); 189 | } 190 | 191 | static void llmnr_packet_process(int ifindex, const uint8_t *pktbuf, size_t len, 192 | int sock, const struct sockaddr_storage *sst) 193 | { 194 | const struct llmnr_hdr *hdr = (const struct llmnr_hdr *)pktbuf; 195 | uint16_t flags, qdcount; 196 | const uint8_t *query; 197 | size_t query_len; 198 | uint8_t name_len; 199 | 200 | /* Query too short? */ 201 | if (len < sizeof(struct llmnr_hdr)) 202 | return; 203 | 204 | flags = ntohs(hdr->flags); 205 | qdcount = ntohs(hdr->qdcount); 206 | 207 | /* Query invalid as per RFC 4795, section 2.1.1 */ 208 | if (((flags & (LLMNR_F_QR | LLMNR_F_OPCODE | LLMNR_F_TC)) != 0) || 209 | qdcount != 1 || hdr->ancount != 0 || hdr->nscount != 0) 210 | return; 211 | 212 | query = pktbuf + sizeof(struct llmnr_hdr); 213 | query_len = len - sizeof(struct llmnr_hdr); 214 | name_len = query[0]; 215 | 216 | /* Invalid name in query? */ 217 | if (name_len == 0 || name_len >= query_len || name_len > LLMNR_LABEL_MAX_SIZE || query[1 + name_len] != 0) 218 | return; 219 | 220 | /* Authoritative? */ 221 | if (llmnr_name_matches(query)) 222 | llmnr_respond(ifindex, hdr, query, query_len, sock, sst); 223 | } 224 | 225 | void llmnr_recv(int sock) 226 | { 227 | uint8_t pktbuf[2048], aux[128]; 228 | struct msghdr msg; 229 | struct iovec io; 230 | struct sockaddr_storage sin_r; 231 | struct cmsghdr *cmsg; 232 | ssize_t recvlen; 233 | int ifindex = -1; 234 | 235 | io.iov_base = pktbuf; 236 | io.iov_len = sizeof(pktbuf); 237 | 238 | memset(&msg, 0, sizeof(msg)); 239 | msg.msg_name = &sin_r; 240 | msg.msg_namelen = sizeof(sin_r); 241 | msg.msg_iov = &io; 242 | msg.msg_iovlen = 1; 243 | msg.msg_control = aux; 244 | msg.msg_controllen = sizeof(aux); 245 | 246 | if ((recvlen = recvmsg(sock, &msg, 0)) < 0) { 247 | if (errno != EINTR) 248 | log_err("Failed to receive packet: %s\n", strerror(errno)); 249 | return; 250 | } 251 | 252 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 253 | if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { 254 | struct in_pktinfo *in = (struct in_pktinfo *)CMSG_DATA(cmsg); 255 | ifindex = in->ipi_ifindex; 256 | } else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { 257 | struct in6_pktinfo *in6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 258 | ifindex = in6->ipi6_ifindex; 259 | } 260 | } 261 | 262 | if (ifindex >= 0) 263 | llmnr_packet_process(ifindex, pktbuf, recvlen, sock, 264 | (const struct sockaddr_storage *)&sin_r); 265 | else 266 | log_warn("Could not get interface of incoming packet\n"); 267 | } 268 | -------------------------------------------------------------------------------- /llmnr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2017 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef LLMNR_H 20 | #define LLMNR_H 21 | 22 | #include 23 | 24 | void llmnr_set_hostname(const char *hostname); 25 | void llmnr_init(const char *hostname, bool ipv6); 26 | void llmnr_recv(int sock); 27 | 28 | #endif /* LLMNR_H */ 29 | -------------------------------------------------------------------------------- /llmnrd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * llmnrd -- LLMNR (RFC 4705) responder daemon. 3 | * 4 | * Copyright (C) 2014-2017 Tobias Klauser 5 | * 6 | * This file is part of llmnrd. 7 | * 8 | * llmnrd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 2 of the License. 11 | * 12 | * llmnrd 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 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with llmnrd. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include "compiler.h" 39 | #include "log.h" 40 | #include "util.h" 41 | 42 | #include "iface.h" 43 | #include "llmnr.h" 44 | #include "llmnr-packet.h" 45 | #include "socket.h" 46 | 47 | static bool llmnrd_running = true; 48 | static int llmnrd_sock_ipv4 = -1; 49 | static int llmnrd_sock_ipv6 = -1; 50 | 51 | static const char *short_opts = "H:i:p:6dshV"; 52 | static const struct option long_opts[] = { 53 | { "hostname", required_argument, NULL, 'H' }, 54 | { "interface", required_argument, NULL, 'i' }, 55 | { "port", required_argument, NULL, 'p' }, 56 | { "ipv6", no_argument, NULL, '6' }, 57 | { "daemonize", no_argument, NULL, 'd' }, 58 | { "syslog", no_argument, NULL, 's' }, 59 | { "help", no_argument, NULL, 'h' }, 60 | { "version", no_argument, NULL, 'V' }, 61 | { NULL, 0, NULL, 0 }, 62 | }; 63 | 64 | static void __noreturn usage_and_exit(int status) 65 | { 66 | fprintf(stdout, "Usage: llmnrd [OPTIONS]\n" 67 | "Options:\n" 68 | " -H, --hostname NAME set hostname to respond with (default: system hostname)\n" 69 | " -i, --interface DEV bind socket to a specific interface, e.g. eth0\n" 70 | " -p, --port NUM set port number to listen on (default: %d)\n" 71 | " -6, --ipv6 enable LLMNR name resolution over IPv6\n" 72 | " -d, --daemonize run as daemon in the background\n" 73 | " -s, --syslog send all log output to syslog\n" 74 | " -h, --help show this help and exit\n" 75 | " -V, --version show version information and exit\n", 76 | LLMNR_UDP_PORT); 77 | exit(status); 78 | } 79 | 80 | static void __noreturn version_and_exit(void) 81 | { 82 | fprintf(stdout, "llmnrd %s %s\n" 83 | "Copyright (C) 2014-2017 Tobias Klauser \n" 84 | "Licensed under the GNU General Public License, version 2\n", 85 | VERSION_STRING, GIT_VERSION); 86 | exit(EXIT_SUCCESS); 87 | } 88 | 89 | static void signal_handler(int sig) 90 | { 91 | switch (sig) { 92 | case SIGINT: 93 | case SIGQUIT: 94 | case SIGTERM: 95 | llmnrd_running = false; 96 | break; 97 | case SIGHUP: 98 | default: 99 | /* ignore */ 100 | break; 101 | } 102 | } 103 | 104 | static void register_signal(int sig, void (*handler)(int)) 105 | { 106 | sigset_t block_mask; 107 | struct sigaction saction; 108 | 109 | sigfillset(&block_mask); 110 | 111 | saction.sa_handler = handler; 112 | saction.sa_mask = block_mask; 113 | saction.sa_flags = SA_RESTART; 114 | 115 | if (sigaction(sig, &saction, NULL) != 0) { 116 | log_err("Failed to register signal handler for %s (%d)\n", 117 | strsignal(sig), sig); 118 | } 119 | } 120 | 121 | static void iface_event_handle(enum iface_event_type type, unsigned char af, 122 | unsigned int ifindex) 123 | { 124 | switch (af) { 125 | case AF_INET: 126 | socket_mcast_group_ipv4(llmnrd_sock_ipv4, ifindex, type == IFACE_ADD); 127 | break; 128 | case AF_INET6: 129 | socket_mcast_group_ipv6(llmnrd_sock_ipv6, ifindex, type == IFACE_ADD); 130 | break; 131 | default: 132 | /* ignore */ 133 | break; 134 | } 135 | } 136 | 137 | static void hostname_change_handle(char *hostname, size_t maxlen) 138 | { 139 | char *newname; 140 | 141 | newname = xzalloc(maxlen); 142 | if (gethostname(newname, maxlen) == 0) { 143 | newname[maxlen - 1] = '\0'; 144 | if (strncmp(hostname, newname, maxlen) != 0) { 145 | log_info("Hostname changed to %s\n", newname); 146 | strncpy(hostname, newname, maxlen); 147 | llmnr_set_hostname(hostname); 148 | } 149 | } 150 | free(newname); 151 | } 152 | 153 | static bool write_pid_file(void) 154 | { 155 | int fd; 156 | char buf[64]; 157 | ssize_t len; 158 | 159 | fd = open(PIDFILE, O_CREAT|O_EXCL|O_RDWR, 0644); 160 | if (fd == -1) { 161 | log_err("Failed to open pid file %s: %s", PIDFILE, strerror(errno)); 162 | return false; 163 | } 164 | 165 | if (snprintf(buf, sizeof(buf), "%ji\n", (intmax_t) getpid()) < 0) 166 | goto err; 167 | 168 | len = strlen(buf); 169 | if (write(fd, buf, len) != len) 170 | goto err; 171 | 172 | close(fd); 173 | return true; 174 | 175 | err: 176 | log_err("Failed to write pid to %s", PIDFILE); 177 | if (fd != -1) { 178 | unlink(PIDFILE); 179 | close(fd); 180 | } 181 | return false; 182 | } 183 | 184 | int main(int argc, char **argv) 185 | { 186 | int c, ret = -1; 187 | long num_arg; 188 | bool daemonize = false, ipv6 = false; 189 | char *hostname = NULL; 190 | char *iface = NULL; 191 | uint16_t port = LLMNR_UDP_PORT; 192 | int llmnrd_sock_rtnl = -1; 193 | int llmnrd_fd_hostname = -1; 194 | bool rm_pid_file = false; 195 | int nfds; 196 | 197 | setlinebuf(stdout); 198 | 199 | while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { 200 | switch (c) { 201 | case 'd': 202 | daemonize = true; 203 | break; 204 | case 's': 205 | openlog("llmnrd", LOG_PID, LOG_DAEMON); 206 | log_to_syslog(); 207 | break; 208 | case 'H': 209 | hostname = xstrdup(optarg); 210 | break; 211 | case 'i': 212 | iface = xstrdup(optarg); 213 | break; 214 | case 'p': 215 | num_arg = strtol(optarg, NULL, 0); 216 | if (num_arg < 0 || num_arg > UINT16_MAX) { 217 | log_err("Invalid port number: %ld\n", num_arg); 218 | return EXIT_FAILURE; 219 | } 220 | port = num_arg; 221 | break; 222 | case '6': 223 | ipv6 = true; 224 | break; 225 | case 'V': 226 | version_and_exit(); 227 | case 'h': 228 | usage_and_exit(EXIT_SUCCESS); 229 | default: 230 | usage_and_exit(EXIT_FAILURE); 231 | } 232 | } 233 | 234 | register_signal(SIGINT, signal_handler); 235 | register_signal(SIGQUIT, signal_handler); 236 | register_signal(SIGTERM, signal_handler); 237 | register_signal(SIGHUP, signal_handler); 238 | 239 | if (!hostname) { 240 | hostname = xzalloc(MAXHOSTNAMELEN); 241 | if (gethostname(hostname, MAXHOSTNAMELEN) != 0) { 242 | log_err("Failed to get hostname"); 243 | return EXIT_FAILURE; 244 | } 245 | hostname[MAXHOSTNAMELEN - 1] = '\0'; 246 | 247 | llmnrd_fd_hostname = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY); 248 | } 249 | 250 | if (daemonize) { 251 | if (daemon(0, 0) != 0) { 252 | log_err("Failed to daemonize process: %s\n", strerror(errno)); 253 | goto out; 254 | } 255 | if (!write_pid_file()) 256 | goto out; 257 | rm_pid_file = true; 258 | } 259 | 260 | log_info("Starting llmnrd on port %u, hostname %s\n", port, hostname); 261 | if (iface) 262 | log_info("Binding to interface %s\n", iface); 263 | 264 | llmnrd_sock_ipv4 = socket_open_ipv4(port, iface); 265 | if (llmnrd_sock_ipv4 < 0) 266 | goto out; 267 | 268 | if (ipv6) { 269 | llmnrd_sock_ipv6 = socket_open_ipv6(port, iface); 270 | if (llmnrd_sock_ipv6 < 0) 271 | goto out; 272 | } 273 | 274 | llmnrd_sock_rtnl = socket_open_rtnl(ipv6); 275 | if (llmnrd_sock_rtnl < 0) 276 | goto out; 277 | 278 | llmnr_init(hostname, ipv6); 279 | 280 | ret = iface_init(llmnrd_sock_rtnl, iface, ipv6, &iface_event_handle); 281 | if (ret < 0) 282 | goto out; 283 | 284 | nfds = max(llmnrd_sock_ipv4, llmnrd_sock_rtnl); 285 | if (llmnrd_sock_ipv6 >= 0) 286 | nfds = max(nfds, llmnrd_sock_ipv6); 287 | if (llmnrd_fd_hostname >= 0) 288 | nfds = max(nfds, llmnrd_fd_hostname); 289 | nfds += 1; 290 | 291 | while (llmnrd_running) { 292 | fd_set rfds, efds; 293 | 294 | FD_ZERO(&rfds); 295 | FD_SET(llmnrd_sock_ipv4, &rfds); 296 | FD_SET(llmnrd_sock_rtnl, &rfds); 297 | if (llmnrd_sock_ipv6 >= 0) 298 | FD_SET(llmnrd_sock_ipv6, &rfds); 299 | 300 | FD_ZERO(&efds); 301 | if (llmnrd_fd_hostname >= 0) 302 | FD_SET(llmnrd_fd_hostname, &efds); 303 | 304 | ret = select(nfds, &rfds, NULL, &efds, NULL); 305 | if (ret < 0) { 306 | if (errno != EINTR) { 307 | log_err("Failed to select() on socket: %s\n", strerror(errno)); 308 | goto out; 309 | } 310 | } else if (ret) { 311 | /* handle RTNL messages first so we can respond with 312 | * up-to-date information. 313 | */ 314 | if (FD_ISSET(llmnrd_sock_rtnl, &rfds)) 315 | iface_recv(llmnrd_sock_rtnl); 316 | if (FD_ISSET(llmnrd_sock_ipv4, &rfds)) 317 | llmnr_recv(llmnrd_sock_ipv4); 318 | if (llmnrd_sock_ipv6 >= 0 && FD_ISSET(llmnrd_sock_ipv6, &rfds)) 319 | llmnr_recv(llmnrd_sock_ipv6); 320 | if (llmnrd_fd_hostname >= 0 && FD_ISSET(llmnrd_fd_hostname, &efds)) 321 | hostname_change_handle(hostname, MAXHOSTNAMELEN); 322 | } 323 | } 324 | 325 | log_info("Signal received. Stopping llmnrd.\n"); 326 | 327 | ret = 0; 328 | out: 329 | if (llmnrd_sock_rtnl >= 0) 330 | close(llmnrd_sock_rtnl); 331 | if (llmnrd_sock_ipv6 >= 0) 332 | close(llmnrd_sock_ipv6); 333 | if (llmnrd_sock_ipv4 >= 0) 334 | close(llmnrd_sock_ipv4); 335 | free(hostname); 336 | if (rm_pid_file) 337 | unlink(PIDFILE); 338 | return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 339 | } 340 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.h" 5 | 6 | static bool use_syslog; 7 | 8 | void log_lvl(int lvl, const char *fmt, ...) 9 | { 10 | va_list ap; 11 | 12 | va_start(ap, fmt); 13 | 14 | if (!use_syslog) { 15 | FILE *out = stderr; 16 | switch (lvl) { 17 | case LOG_ERR: 18 | fputs("Error: ", stderr); 19 | break; 20 | case LOG_WARNING: 21 | fputs("Warning: ", stderr); 22 | break; 23 | case LOG_INFO: 24 | case LOG_DEBUG: 25 | out = stdout; 26 | break; 27 | } 28 | vfprintf(out, fmt, ap); 29 | } else { 30 | vsyslog(lvl, fmt, ap); 31 | } 32 | 33 | va_end(ap); 34 | } 35 | 36 | void log_to_syslog(void) 37 | { 38 | use_syslog = true; 39 | } 40 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef LOG_H 20 | #define LOG_H 21 | 22 | #include 23 | #include 24 | 25 | #include "compiler.h" 26 | 27 | void log_lvl(int lvl, const char *fmt, ...) __check_format_printf(2, 3); 28 | 29 | void log_to_syslog(void); 30 | 31 | #define log_err(fmt, args...) log_lvl(LOG_ERR, fmt, ##args) 32 | #define log_warn(fmt, args...) log_lvl(LOG_WARNING, fmt, ##args) 33 | #define log_info(fmt, args...) log_lvl(LOG_INFO, fmt, ##args) 34 | #ifdef DEBUG 35 | # define log_dbg(fmt, args...) log_lvl(LOG_DEBUG, fmt, ##args) 36 | #else 37 | # define log_dbg(fmt, args...) 38 | #endif 39 | 40 | #endif /* LOG_H */ 41 | -------------------------------------------------------------------------------- /pkt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Packet buffer structure and utilities. 3 | * 4 | * Copyright (C) 2015-2017 Tobias Klauser 5 | * 6 | * Based on pkt_buff.h from the netsniff-ng toolkit which is: 7 | * 8 | * Copyright (C) 2012 Christoph Jaeger 9 | * 10 | * This file is part of llmnrd. 11 | * 12 | * llmnrd is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, version 2 of the License. 15 | * 16 | * llmnrd is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with llmnrd. If not, see . 23 | */ 24 | 25 | #ifndef PKT_H 26 | #define PKT_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "log.h" 33 | #include "util.h" 34 | 35 | struct pkt { 36 | uint8_t *data; 37 | uint8_t *tail; 38 | size_t size; 39 | }; 40 | 41 | static inline bool pkt_invariant(struct pkt *p) 42 | { 43 | return p && (p->data <= p->tail); 44 | } 45 | 46 | static inline struct pkt *pkt_alloc(size_t size) 47 | { 48 | struct pkt *p = xmalloc(sizeof(*p) + size); 49 | uint8_t *data = (uint8_t *)p + sizeof(*p); 50 | 51 | p->data = p->tail = data; 52 | p->size = size; 53 | 54 | return p; 55 | } 56 | 57 | static inline void pkt_free(struct pkt *p) 58 | { 59 | free(p); 60 | } 61 | 62 | static inline void pkt_reset(struct pkt *p) 63 | { 64 | assert(pkt_invariant(p)); 65 | 66 | p->tail = p->data; 67 | } 68 | 69 | static inline size_t pkt_len(struct pkt *p) 70 | { 71 | assert(pkt_invariant(p)); 72 | 73 | return p->tail - p->data; 74 | } 75 | 76 | static inline uint8_t *pkt_put(struct pkt *p, size_t len) 77 | { 78 | uint8_t *data; 79 | 80 | assert(pkt_invariant(p)); 81 | 82 | if (pkt_len(p) + len <= p->size) { 83 | data = p->tail; 84 | p->tail += len; 85 | } else { 86 | /* grow packet */ 87 | size_t new_size = p->size + len - pkt_len(p); 88 | struct pkt *np = xrealloc(p, sizeof(*np) + new_size); 89 | 90 | log_dbg("Reallocating packet from %zu to %zu bytes\n", p->size, new_size); 91 | data = (uint8_t *)np + sizeof(*np); 92 | 93 | np->data = data; 94 | np->tail = data + pkt_len(p); 95 | } 96 | 97 | return data; 98 | } 99 | 100 | #define DEFINE_PKT_PUT(__bitwidth) \ 101 | static inline void pkt_put_u##__bitwidth(struct pkt *p, uint##__bitwidth##_t val) \ 102 | { \ 103 | uint8_t *data = pkt_put(p, sizeof(val)); \ 104 | memcpy(data, &val, sizeof(uint##__bitwidth##_t)); \ 105 | } 106 | 107 | DEFINE_PKT_PUT(8) 108 | DEFINE_PKT_PUT(16) 109 | DEFINE_PKT_PUT(32) 110 | 111 | /* extract values from struct pkt in an alignment-safe way */ 112 | #define DEFINE_PKT_PUT_EXTRACT(__bitwidth) \ 113 | static inline uint##__bitwidth##_t pkt_put_extract_u##__bitwidth(struct pkt *p) \ 114 | { \ 115 | uint##__bitwidth##_t val; \ 116 | memcpy(&val, pkt_put(p, sizeof(val)), sizeof(val)); \ 117 | return val; \ 118 | } 119 | 120 | DEFINE_PKT_PUT_EXTRACT(8) 121 | DEFINE_PKT_PUT_EXTRACT(16) 122 | DEFINE_PKT_PUT_EXTRACT(32) 123 | 124 | #endif /* PKT_H */ 125 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "llmnr-packet.h" 33 | #include "log.h" 34 | #include "socket.h" 35 | 36 | static const int YES = 1; 37 | static const int TTL = 255; 38 | 39 | static void socket_bind_to_device(int sock, const char *iface) { 40 | #ifdef SO_BINDTODEVICE 41 | /* bind socket to specific interface */ 42 | if (iface) { 43 | struct ifreq ifr; 44 | 45 | memset(&ifr, 0, sizeof(ifr)); 46 | strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1); 47 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { 48 | log_warn("Failed to bind socket to device %s: %s\n", iface, strerror(errno)); 49 | } 50 | } 51 | #endif 52 | } 53 | 54 | int socket_open_ipv4(uint16_t port, const char *iface) 55 | { 56 | int sock; 57 | struct sockaddr_in sa; 58 | 59 | sock = socket(AF_INET, SOCK_DGRAM, 0); 60 | if (sock < 0) { 61 | log_err("Failed to open UDP socket: %s\n", strerror(errno)); 62 | return -1; 63 | } 64 | 65 | /* pass pktinfo struct on received packets */ 66 | if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &YES, sizeof(YES)) < 0) { 67 | log_err("Failed to set IPv4 packet info socket option: %s\n", strerror(errno)); 68 | goto err; 69 | } 70 | 71 | /* RFC 4795, section 2.5 recommends to set TTL to 255 for UDP */ 72 | if (setsockopt(sock, IPPROTO_IP, IP_TTL, &TTL, sizeof(TTL)) < 0) { 73 | log_err("Failed to set IPv4 unicast TTL socket option: %s\n", strerror(errno)); 74 | goto err; 75 | } 76 | 77 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &TTL, sizeof(TTL)) < 0) { 78 | log_err("Failed to set IPv4 multicast TTL socket option: %s\n", strerror(errno)); 79 | goto err; 80 | } 81 | 82 | socket_bind_to_device(sock, iface); 83 | 84 | /* bind the socket */ 85 | memset(&sa, 0, sizeof(sa)); 86 | sa.sin_family = AF_INET; 87 | sa.sin_addr.s_addr = INADDR_ANY; 88 | sa.sin_port = htons(port); 89 | 90 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 91 | log_err("Failed to bind() socket: %s\n", strerror(errno)); 92 | goto err; 93 | } 94 | 95 | return sock; 96 | err: 97 | close(sock); 98 | return -1; 99 | } 100 | 101 | int socket_open_ipv6(uint16_t port, const char *iface) 102 | { 103 | int sock, opt_pktinfo; 104 | struct sockaddr_in6 sa; 105 | 106 | sock = socket(AF_INET6, SOCK_DGRAM, 0); 107 | if (sock < 0) { 108 | log_err("Failed to open UDP socket: %s\n", strerror(errno)); 109 | return -1; 110 | } 111 | 112 | /* pass pktinfo struct on received packets */ 113 | #if defined(IPV6_RECVPKTINFO) 114 | opt_pktinfo = IPV6_RECVPKTINFO; 115 | #elif defined(IPV6_PKTINFO) 116 | opt_pktinfo = IPV6_PKTINFO; 117 | #endif 118 | if (setsockopt(sock, IPPROTO_IPV6, opt_pktinfo, &YES, sizeof(YES)) < 0) { 119 | log_err("Failed to set IPv6 packet info socket option: %s\n", strerror(errno)); 120 | goto err; 121 | } 122 | 123 | /* RFC 4795, section 2.5 recommends to set TTL to 255 for UDP */ 124 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &TTL, sizeof(TTL)) < 0) { 125 | log_err("Failed to set IPv6 unicast hops socket option: %s\n", strerror(errno)); 126 | goto err; 127 | } 128 | 129 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &TTL, sizeof(TTL)) < 0) { 130 | log_err("Failed to set IPv6 multicast hops socket option: %s\n", strerror(errno)); 131 | goto err; 132 | } 133 | 134 | /* IPv6 only socket */ 135 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &YES, sizeof(YES)) < 0) { 136 | log_err("Failed to set IPv6 only socket option: %s\n", strerror(errno)); 137 | goto err; 138 | } 139 | 140 | socket_bind_to_device(sock, iface); 141 | 142 | /* bind the socket */ 143 | memset(&sa, 0, sizeof(sa)); 144 | sa.sin6_family = AF_INET6; 145 | sa.sin6_port = htons(port); 146 | 147 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 148 | log_err("Failed to bind() socket: %s\n", strerror(errno)); 149 | goto err; 150 | } 151 | 152 | return sock; 153 | err: 154 | close(sock); 155 | return -1; 156 | } 157 | 158 | int socket_open_rtnl(bool ipv6) 159 | { 160 | int sock; 161 | struct sockaddr_nl sa; 162 | 163 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 164 | if (sock < 0) { 165 | log_err("Failed to open netlink route socket: %s\n", strerror(errno)); 166 | return -1; 167 | } 168 | 169 | memset(&sa, 0, sizeof(sa)); 170 | sa.nl_family = AF_NETLINK; 171 | /* 172 | * listen for following events: 173 | * - network interface create/delete/up/down 174 | * - IPv4 address add/delete 175 | * - IPv6 address add/delete (if enabled) 176 | */ 177 | sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; 178 | if (ipv6) 179 | sa.nl_groups |= RTMGRP_IPV6_IFADDR; 180 | 181 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 182 | log_err("Failed to bind() netlink socket: %s\n", strerror(errno)); 183 | goto err; 184 | } 185 | 186 | return sock; 187 | err: 188 | close(sock); 189 | return -1; 190 | } 191 | 192 | int socket_mcast_group_ipv4(int sock, unsigned int ifindex, bool join) 193 | { 194 | struct ip_mreqn mreq; 195 | char ifname[IF_NAMESIZE]; 196 | 197 | /* silently ignore, we might not be listening on an IPv4 socket */ 198 | if (sock < 0) 199 | return -1; 200 | 201 | memset(&mreq, 0, sizeof(mreq)); 202 | mreq.imr_ifindex = ifindex; 203 | mreq.imr_address.s_addr = INADDR_ANY; 204 | inet_pton(AF_INET, LLMNR_IPV4_MCAST_ADDR, &mreq.imr_multiaddr); 205 | 206 | if (setsockopt(sock, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, 207 | &mreq, sizeof(mreq)) < 0) { 208 | /* ignore error if we attempt to join a group the interface is 209 | * already part of */ 210 | if (!join || errno != EADDRINUSE) { 211 | log_err("Failed to %s IPv4 multicast group membership on interface %s: %s\n", 212 | join ? "add" : "drop", if_indextoname(ifindex, ifname), 213 | strerror(errno)); 214 | return -1; 215 | } 216 | } 217 | 218 | return 0; 219 | } 220 | 221 | int socket_mcast_group_ipv6(int sock, unsigned int ifindex, bool join) 222 | { 223 | struct ipv6_mreq mreq6; 224 | char ifname[IF_NAMESIZE]; 225 | 226 | /* silently ignore, we might not be listening on an IPv6 socket */ 227 | if (sock < 0) 228 | return -1; 229 | 230 | memset(&mreq6, 0, sizeof(mreq6)); 231 | mreq6.ipv6mr_interface = ifindex; 232 | inet_pton(AF_INET6, LLMNR_IPV6_MCAST_ADDR, &mreq6.ipv6mr_multiaddr); 233 | 234 | if (setsockopt(sock, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, 235 | &mreq6, sizeof(mreq6)) < 0) { 236 | /* ignore error if we attempt to join a group the interface is 237 | * already part of */ 238 | if (!join || errno != EADDRINUSE) { 239 | log_err("Failed to %s IPv6 multicast group membership on interface %s: %s\n", 240 | join ? "add" : "drop", if_indextoname(ifindex, ifname), 241 | strerror(errno)); 242 | return -1; 243 | } 244 | } 245 | 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Tobias Klauser 3 | * 4 | * This file is part of llmnrd. 5 | * 6 | * llmnrd is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * llmnrd 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 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with llmnrd. If not, see . 17 | */ 18 | 19 | #ifndef SOCKET_H 20 | #define SOCKET_H 21 | 22 | #include 23 | #include 24 | 25 | int socket_open_ipv4(uint16_t port, const char *iface); 26 | int socket_open_ipv6(uint16_t port, const char *iface); 27 | int socket_open_rtnl(bool ipv6); 28 | 29 | int socket_mcast_group_ipv4(int sock, unsigned int ifindex, bool join); 30 | int socket_mcast_group_ipv6(int sock, unsigned int ifindex, bool join); 31 | 32 | #endif /* SOCKET_H */ 33 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2015 Tobias Klauser 3 | * Copyright (C) 2009-2012 Daniel Borkmann 4 | * 5 | * This file is part of llmnrd. 6 | * 7 | * llmnrd is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License. 10 | * 11 | * llmnrd 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 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with llmnrd. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "util.h" 24 | 25 | void *xmalloc(size_t size) 26 | { 27 | void *ptr; 28 | 29 | if (size == 0) 30 | panic("malloc: size 0\n"); 31 | 32 | ptr = malloc(size); 33 | if (!ptr) 34 | panic("malloc: out of memory\n"); 35 | 36 | return ptr; 37 | } 38 | 39 | void *xzalloc(size_t size) 40 | { 41 | void *ptr = xmalloc(size); 42 | memset(ptr, 0, size); 43 | return ptr; 44 | } 45 | 46 | void *xrealloc(void *ptr, size_t size) 47 | { 48 | void *newptr; 49 | 50 | if (size == 0) 51 | panic("realloc: size 0\n"); 52 | 53 | newptr = realloc(ptr, size); 54 | if (!newptr) { 55 | free(ptr); 56 | panic("realloc: out of memory\n"); 57 | } 58 | 59 | return newptr; 60 | } 61 | 62 | char *xstrdup(const char *s) 63 | { 64 | size_t len = strlen(s) + 1; 65 | char *ret = xmalloc(len); 66 | 67 | memcpy(ret, s, len); 68 | 69 | return ret; 70 | } 71 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2017 Tobias Klauser 3 | * Copyright (C) 2009-2012 Daniel Borkmann 4 | * 5 | * This file is part of llmnrd. 6 | * 7 | * llmnrd is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License. 10 | * 11 | * llmnrd 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 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with llmnrd. If not, see . 18 | */ 19 | 20 | #ifndef UTIL_H 21 | #define UTIL_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "compiler.h" 29 | 30 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 31 | 32 | /* 33 | * min()/max() macros with strict type-checking. 34 | * Taken from linux/kernel.h 35 | */ 36 | #undef min 37 | #define min(x, y) ({ \ 38 | typeof(x) _min1 = (x); \ 39 | typeof(y) _min2 = (y); \ 40 | (void) (&_min1 == &_min2); \ 41 | _min1 < _min2 ? _min1 : _min2; }) 42 | 43 | #undef max 44 | #define max(x, y) ({ \ 45 | typeof(x) _max1 = (x); \ 46 | typeof(y) _max2 = (y); \ 47 | (void) (&_max1 == &_max2); \ 48 | _max1 > _max2 ? _max1 : _max2; }) 49 | 50 | static inline void panic(const char *fmt, ...) __check_format_printf(1, 2); 51 | 52 | static inline void __noreturn panic(const char *fmt, ...) 53 | { 54 | va_list vl; 55 | 56 | va_start(vl, fmt); 57 | vfprintf(stderr, fmt, vl); 58 | va_end(vl); 59 | 60 | exit(EXIT_FAILURE); 61 | } 62 | 63 | void *xmalloc(size_t size) __warn_unused_result; 64 | void *xzalloc(size_t size) __warn_unused_result; 65 | void *xrealloc(void *ptr, size_t size) __warn_unused_result; 66 | char *xstrdup(const char *s) __warn_unused_result; 67 | 68 | static inline bool xstreq(const char *str1, const char *str2) 69 | { 70 | size_t n = strlen(str1); 71 | 72 | if (n != strlen(str2)) 73 | return false; 74 | if (strncmp(str1, str2, n) != 0) 75 | return false; 76 | 77 | return true; 78 | } 79 | 80 | #endif /* UTIL_H */ 81 | --------------------------------------------------------------------------------