Commits

Kaz Nishimura committed 979a1b2 Merge

Merge with default.

  • Participants
  • Parent commits c53cb7e, ba74c51
  • Branches feature/port-osx

Comments (0)

Files changed (11)

-[**Note**: this file _should not_ be distributed with the xllmnrd package.]
+**Note**: this file _should not_ be distributed with the xllmnrd package.
 
 This repository contains the source code for xllmnrd.
 
 systems).
 It allows Microsoft Windows clients to get the IPv6 address of a server
 on the same local network _without any DNS configuration_ and
-effectively complements IPv4-only NetBIOS name resolution
+effectively complements IPv4-only NetBIOS name resolution traditionally
 provided by [Samba][].
 
 xllmnrd is [free software][]: you can redistribute it and/or modify it
 under the terms of the [GNU General Public License][].
 
 For more information about xllmnrd, visit the xllmnrd project
-at <http://www.vx68k.org/xllmnrd>.
+at <http://xllmnrd.vx68k.org/>.
 
 [Samba]: <http://www.samba.org/>
 [Free software]: <http://www.gnu.org/philosophy/free-sw.html>
-    "What is free software?"
+                 "What is free software?"
 [GNU General Public License]: <http://www.gnu.org/licenses/gpl.html>
 
 # Installation
 depending on the compiler version) before you run 'configure'.
 
 If you find any other problem while installation, please report it to our
-issue tracker at <https://bitbucket.org/kazssym/xllmnrd/issues>.
+issue tracker at <https://bitbucket.org/kazssym/xllmnrd/issues>.

File acinclude.m4

+# _XX_PROG_CXX_STD11
+AC_DEFUN([_XX_PROG_CXX_STD11],
+[
+  AC_LANG_PUSH([C++])dnl
+  AC_COMPILE_IFELSE([AC_LANG_SOURCE(
+[#if __cplusplus < 201103L
+#error Not C++11
+#endif
+])], [$1], [$2])
+  AC_LANG_POP([C++])dnl
+])
+
+# XX_PROG_CXX_STD11
+# -----------------
+# Check whether the C++ compiler supports ISO/IEC 14882:2011 and if not,
+# try to add options to enable it.
+AC_DEFUN([XX_PROG_CXX_STD11],
+[
+  AC_MSG_CHECKING([whether $CXX supports C++11])
+  _XX_PROG_CXX_STD11([xx_cv_cxx_std11=yes], [xx_cv_cxx_std11=no])
+  AC_MSG_RESULT([$xx_cv_cxx_std11])
+  if test "$xx_cv_cxx_std11" = no; then
+    xx_save_CXX=$CXX
+    CXX="$CXX -std=gnu++11"
+    AC_MSG_CHECKING([whether $CXX supports C++11])
+    _XX_PROG_CXX_STD11([xx_cv_cxx_std11=yes], [CXX=$xx_save_CXX])
+    AC_MSG_RESULT([$xx_cv_cxx_std11])
+  fi
+])

File build.makefile

 prefix = /tmp/xllmnrd
 
 AUTORECONF = autoreconf
-CC = gcc -std=gnu99
-CXX = g++ -std=gnu++11
 TAR = tar
 
 CFLAGS = -g -O2 -Wall -Wextra
 
-export CC CXX
-
-build: clean check image dist
+build: clean check dist
 	hg status || true
 
 all check clean dist distcheck: $(builddir)/Makefile

File configure.ac

 # Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.69])
-AC_INIT([xllmnrd], [1.3x1], [https://bitbucket.org/kazssym/xllmnrd/issues],,
+AC_INIT([xllmnrd], [2.0], [https://bitbucket.org/kazssym/xllmnrd/issues/new],,
 [http://www.vx68k.org/xllmnrd])
 AC_CONFIG_SRCDIR([src/main.c])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_PROG_CC_STDC
 AC_PROG_CXX
 gl_EARLY
+XX_PROG_CXX_STD11
 
 # Checks for libraries.
 AC_SEARCH_LIBS([pthread_create], [pthread])
 msgstr ""
 "Project-Id-Version: xllmnrd 1.3x1\n"
 "Report-Msgid-Bugs-To: <https://bitbucket.org/kazssym/xllmnrd/issues>\n"
-"POT-Creation-Date: 2014-02-18 14:15+0900\n"
+"POT-Creation-Date: 2014-03-02 09:34+0900\n"
 "PO-Revision-Date: 2014-01-09 08:48+0000\n"
 "Last-Translator: Kaz Nishimura\n"
 "Language-Team: Japanese (http://www.transifex.com/projects/p/xllmnrd/"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-#: src/main.c:322
+#: src/main.c:324
 #, c-format
 msgid "Try '%s --help' for more information.\n"
 msgstr "詳しくは ‘%s --help’ をお試しください。\n"
 
-#: src/main.c:329
+#: src/main.c:331
 #, c-format
 msgid "Usage: %s [OPTION]...\n"
 msgstr "使用法: %s [OPTION]...\n"
 
-#: src/main.c:330
+#: src/main.c:332
 #, c-format
 msgid "Respond to IPv6 LLMNR queries.\n"
 msgstr "IPv6 LLMNR クエリに応答します。\n"
 
-#: src/main.c:332
+#: src/main.c:334
 #, c-format
 msgid "  -f, --foreground      run in foreground\n"
 msgstr "  -f, --foreground      フォアグラウンドで実行\n"
 
-#: src/main.c:334
+#: src/main.c:336
 #, c-format
 msgid "  -p, --pid-file=FILE   record the process ID in FILE\n"
 msgstr "  -p, --pid-file=FILE   プロセス ID を FILE に記録\n"
 
-#: src/main.c:336
+#: src/main.c:338
 #, c-format
 msgid "  -n, --name=NAME       set the host name of the responder to NAME\n"
 msgstr "  -n, --name=NAME       レスポンダーのホスト名を NAME に設定\n"
 
-#: src/main.c:338
+#: src/main.c:340
 #, c-format
 msgid "      --help            display this help and exit\n"
 msgstr "      --help            このヘルプを表示して終了\n"
 
-#: src/main.c:340
+#: src/main.c:342
 #, c-format
 msgid "      --version         output version information and exit\n"
 msgstr "      --version         バージョン情報を出力して終了\n"
 
-#: src/main.c:343
+#: src/main.c:345
 #, c-format
 msgid "Report bugs to <%s>.\n"
 msgstr "バグ レポートは <%s> まで。\n"
 
-#: src/main.c:347
+#: src/main.c:349
 #, c-format
 msgid "%s %s\n"
 msgstr "%s %s\n"
 
-#: src/main.c:349
+#: src/main.c:351
 #, c-format
 msgid "Packaged from revision %s\n"
 msgstr "リビジョン %s からのパッケージ化\n"
 
-#: src/main.c:351
+#: src/main.c:353
 msgid "(C)"
 msgstr "©"
 
-#: src/main.c:352
+#: src/main.c:354
 #, c-format
 msgid ""
 "This is free software: you are free to change and redistribute it.\n"

File po/xllmnrd.pot

 msgstr ""
 "Project-Id-Version: xllmnrd 1.3x1\n"
 "Report-Msgid-Bugs-To: <https://bitbucket.org/kazssym/xllmnrd/issues>\n"
-"POT-Creation-Date: 2014-02-18 14:15+0900\n"
+"POT-Creation-Date: 2014-03-02 09:34+0900\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: src/main.c:322
+#: src/main.c:324
 #, c-format
 msgid "Try '%s --help' for more information.\n"
 msgstr ""
 
-#: src/main.c:329
+#: src/main.c:331
 #, c-format
 msgid "Usage: %s [OPTION]...\n"
 msgstr ""
 
-#: src/main.c:330
+#: src/main.c:332
 #, c-format
 msgid "Respond to IPv6 LLMNR queries.\n"
 msgstr ""
 
-#: src/main.c:332
+#: src/main.c:334
 #, c-format
 msgid "  -f, --foreground      run in foreground\n"
 msgstr ""
 
-#: src/main.c:334
+#: src/main.c:336
 #, c-format
 msgid "  -p, --pid-file=FILE   record the process ID in FILE\n"
 msgstr ""
 
-#: src/main.c:336
+#: src/main.c:338
 #, c-format
 msgid "  -n, --name=NAME       set the host name of the responder to NAME\n"
 msgstr ""
 
-#: src/main.c:338
+#: src/main.c:340
 #, c-format
 msgid "      --help            display this help and exit\n"
 msgstr ""
 
-#: src/main.c:340
+#: src/main.c:342
 #, c-format
 msgid "      --version         output version information and exit\n"
 msgstr ""
 
-#: src/main.c:343
+#: src/main.c:345
 #, c-format
 msgid "Report bugs to <%s>.\n"
 msgstr ""
 
-#: src/main.c:347
+#: src/main.c:349
 #, c-format
 msgid "%s %s\n"
 msgstr ""
 
-#: src/main.c:349
+#: src/main.c:351
 #, c-format
 msgid "Packaged from revision %s\n"
 msgstr ""
 
-#: src/main.c:351
+#: src/main.c:353
 msgid "(C)"
 msgstr ""
 
-#: src/main.c:352
+#: src/main.c:354
 #, c-format
 msgid ""
 "This is free software: you are free to change and redistribute it.\n"

File src/ifaddr.c

 /*
- * Interface address lookups (implementation)
- * Copyright (C) 2013 Kaz Nishimura
+ * ifaddr - interface addresses (implementation)
+ * Copyright (C) 2013-2014 Kaz Nishimura
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
 #include <config.h>
 #endif
 #undef _GNU_SOURCE
-// Workaround for undefined s6_addr32 in IN6_IS_ADDR_UNSPECIFIED.
+// Workaround for undefined s6_addr32 in IN6_ARE_ADDR_EQUAL.
 // TODO: Remove this workaround when we no longer need it.
 #if __GNUC__
 #define _GNU_SOURCE 1
 #include <linux/rtnetlink.h>
 #endif
 #include <net/if.h> /* if_indextoname */
+#include <arpa/inet.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <pthread.h>
 /**
  * Interface record.
  */
-struct ifaddr_if {
-    unsigned int ifindex;
+struct ifaddr_interface {
+    unsigned int index;
     size_t addr_v4_size;
     struct in_addr *addr_v4;
-    struct in6_addr addr;
+    size_t addr_v6_size;
+    struct in6_addr *addr_v6;
 };
 
 /**
  */
 static ifaddr_change_handler if_change_handler;
 
-static const size_t if_table_capacity = 32;
-static size_t if_table_size;
-static struct ifaddr_if if_table[32]; // TODO: Allocate dynamically.
+/**
+ * Table of interfaces.
+ */
+static const size_t interfaces_capacity = 32;
+static size_t interfaces_size;
+static struct ifaddr_interface interfaces[32]; // TODO: Allocate dynamically.
 
 /**
  * Mutex for refresh_not_in_progress.
 static void ifaddr_remove_addr_v4(unsigned int __index,
         const struct in_addr *__addr);
 
+/**
+ * Adds an IPv6 address to an interface.
+ * @param __index interface index.
+ * @param __addr [in] IPv6 address to be added.
+ */
+static void ifaddr_add_addr_v6(unsigned int __index,
+        const struct in6_addr *__addr);
+
+/**
+ * Removes an IPv6 address from an interface.
+ * @param __index interface index.
+ * @param __addr [in] IPv6 address to be removed.
+ */
+static void ifaddr_remove_addr_v6(unsigned int __index,
+        const struct in6_addr *__addr);
+
 /*
  * Declarations for static functions.
  */
 }
 
 /**
+ * Finds an interface.
+ * @param index interface index.
+ * @return pointer to the matching interface record, or the end of the
+ * interfaces if nothing found.
+ */
+static inline struct ifaddr_interface *ifaddr_find_interface(
+        unsigned int index) {
+    struct ifaddr_interface *i = interfaces;
+    while (i != interfaces + interfaces_size && i->index != index) {
+        ++i;
+    }
+    return i;
+}
+
+/**
  * Tests if an interface has no address.
  * This function MUST be called while 'if_mutex' is locked.
  * @param i [in] interface record to be tested.
  * @return true if the interface has no address.
  */
-static inline int ifaddr_interface_is_free(
-        const struct ifaddr_if * restrict i) {
-    return i->addr_v4_size == 0 && IN6_IS_ADDR_UNSPECIFIED(&i->addr);
+static inline int ifaddr_interface_is_free(const struct ifaddr_interface *i) {
+    return i->addr_v4_size == 0 && i->addr_v6_size == 0;
 }
 
 /**
  * This function MUST be called while 'if_mutex' is locked.
  * @param i [in] interface record to be erased.
  */
-static inline void ifaddr_erase_interface(struct ifaddr_if *restrict i) {
+static inline void ifaddr_erase_interface(struct ifaddr_interface *i) {
+    free(i->addr_v6);
     free(i->addr_v4);
 
-    struct ifaddr_if *j = i++;
-    while (i != if_table + if_table_size) {
+    struct ifaddr_interface *j = i++;
+    while (i != interfaces + interfaces_size) {
         *j++ = *i++;
     }
-    --if_table_size;
+    --interfaces_size;
 
-    // Clears the pointer that is no longer used though it is unnecessary.
-    if_table[if_table_size].addr_v4 = NULL;
-}
-
-/**
- * Adds an IPv6 address to an interface.
- * @param index interface index.
- * @param addr IPv6 address.
- */
-static inline void ifaddr_add_addr_v6(unsigned int index,
-        const struct in6_addr *restrict addr) {
-    lock_mutex(&if_mutex);
-
-    unsigned int i = 0;
-    while (i != if_table_size && if_table[i].ifindex != index) {
-        ++i;
-    }
-    if (i == if_table_size) {
-        if (if_table_size == if_table_capacity) {
-            abort(); // TODO: Think later.
-        }
-        ++if_table_size;
-
-        if_table[i] = (struct ifaddr_if){
-            .ifindex = index,
-            .addr = *addr,
-        };
-        if (if_change_handler) {
-            struct ifaddr_change change = {
-                .type = IFADDR_ADDED,
-                .ifindex = index,
-            };
-            (*if_change_handler)(&change);
-        }
-    } else if (!IN6_ARE_ADDR_EQUAL(&if_table[i].addr, addr)) {
-        // Handles an address-only change.
-        if_table[i].addr = *addr;
-        if (if_change_handler) {
-            struct ifaddr_change change = {
-                .type = IFADDR_ADDED,
-                .ifindex = index,
-            };
-            (*if_change_handler)(&change);
-        }
-    }
-
-    unlock_mutex(&if_mutex);
-}
-
-/**
- * Removes an IPv6 address from an interface.
- * @param index interface index.
- * @param addr IPv6 address.
- */
-static inline void ifaddr_remove_addr_v6(unsigned int index,
-        const struct in6_addr *restrict addr) {
-    lock_mutex(&if_mutex);
-
-    unsigned int i = 0;
-    while (i != if_table_size && if_table[i].ifindex != index) {
-        ++i;
-    }
-    if (i != if_table_size && IN6_ARE_ADDR_EQUAL(&if_table[i].addr, addr)) {
-        if_table[i].addr = in6addr_any;
-        if (ifaddr_interface_is_free(&if_table[i])) {
-            ifaddr_erase_interface(&if_table[i]);
-        }
-
-        if (if_change_handler) {
-            struct ifaddr_change change = {
-                .type = IFADDR_REMOVED,
-                .ifindex = index,
-            };
-            (*if_change_handler)(&change);
-        }
-    }
-
-    unlock_mutex(&if_mutex);
+#if EXTRA_DEBUG
+    // Clears the pointer that is no longer used.
+    interfaces[interfaces_size].addr_v6 = NULL;
+    interfaces[interfaces_size].addr_v4 = NULL;
+#endif
 }
 
 /**
     }
     interrupt_signo = sig;
     if_change_handler = NULL;
-    if_table_size = 0;
+    interfaces_size = 0;
     started = false;
     refresh_not_in_progress = true;
 
             terminated = true;
 
             if (interrupt_signo != 0) {
-                pthread_kill(worker_thread, interrupt_signo); // TODO: Check for an error.
+                pthread_kill(worker_thread, interrupt_signo);
+                // TODO: Check for an error.
             }
             pthread_join(worker_thread, NULL); // TODO: Check for an error.
         }
         const struct in_addr *restrict addr) {
     lock_mutex(&if_mutex);
 
-    struct ifaddr_if *i = if_table;
-    while (i != if_table + if_table_size && i->ifindex != index) {
-        ++i;
-    }
-    if (i == if_table + if_table_size) {
-        if (if_table_size == if_table_capacity) {
+    struct ifaddr_interface *i = ifaddr_find_interface(index);
+    if (i == interfaces + interfaces_size) {
+        if (interfaces_size == interfaces_capacity) {
             abort(); // TODO: Think later.
         }
-        *i = (struct ifaddr_if){
-            .ifindex = index,
-            .addr_v4_size = 0,
+        *i = (struct ifaddr_interface) {
+            .index = index,
         };
-        ++if_table_size;
+        ++interfaces_size;
     }
 
     struct in_addr *j = i->addr_v4;
     while (j != i->addr_v4 + i->addr_v4_size && j->s_addr != addr->s_addr) {
         ++j;
     }
-    // Appends an address if no matching one is found
+    // Appends the address if not found
     if (j == i->addr_v4 + i->addr_v4_size) {
         struct in_addr *addr_v4 = NULL;
         // Avoids potential overflow in size calculation.
         const struct in_addr *restrict addr) {
     lock_mutex(&if_mutex);
 
-    struct ifaddr_if *i = if_table;
-    while (i != if_table + if_table_size && i->ifindex != index) {
-        ++i;
-    }
-    if (i != if_table + if_table_size) {
+    struct ifaddr_interface *i = ifaddr_find_interface(index);
+    if (i != interfaces + interfaces_size) {
         struct in_addr *j = i->addr_v4;
         while (j != i->addr_v4 + i->addr_v4_size &&
                 j->s_addr != addr->s_addr) {
             ++j;
         }
-        // Erases a matching address if one is found.
+        // Erases the address if found.
         if (j != i->addr_v4 + i->addr_v4_size) {
             struct in_addr *k = j++;
             while (j != i->addr_v4 + i->addr_v4_size) {
                 *k++ = *j++;
             }
-            --(i->addr_v4_size);
+            i->addr_v4_size -= 1;
 
             char ifname[IF_NAMESIZE];
             if_indextoname(index, ifname);
     unlock_mutex(&if_mutex);
 }
 
+void ifaddr_add_addr_v6(unsigned int index,
+        const struct in6_addr *restrict addr) {
+    lock_mutex(&if_mutex);
+
+    struct ifaddr_interface *i = ifaddr_find_interface(index);
+    if (i == interfaces + interfaces_size) {
+        if (interfaces_size == interfaces_capacity) {
+            abort(); // TODO: Think later.
+        }
+        *i = (struct ifaddr_interface) {
+            .index = index,
+        };
+        ++interfaces_size;
+    }
+
+    struct in6_addr *j = i->addr_v6;
+    while (j != i->addr_v6 + i->addr_v6_size &&
+            !IN6_ARE_ADDR_EQUAL(j, addr)) {
+        ++j;
+    }
+    // Appends the address if not found
+    if (j == i->addr_v6 + i->addr_v6_size) {
+        struct in6_addr *addr_v6 = NULL;
+        // Avoids potential overflow in size calculation.
+        if (i->addr_v6_size + 1 <= SIZE_MAX / sizeof (struct in6_addr)) {
+            addr_v6 = (struct in6_addr *) realloc(i->addr_v6,
+                    (i->addr_v6_size + 1) * sizeof (struct in6_addr));
+        }
+        if (addr_v6) {
+            if (i->addr_v6_size == 0 && if_change_handler) {
+                struct ifaddr_change change = {
+                    .type = IFADDR_ADDED,
+                    .ifindex = index,
+                };
+                (*if_change_handler)(&change);
+            }
+
+            addr_v6[i->addr_v6_size] = *addr;
+            i->addr_v6 = addr_v6;
+            i->addr_v6_size += 1;
+
+            char addrstr[INET6_ADDRSTRLEN];
+            char name[IF_NAMESIZE];
+            inet_ntop(AF_INET6, addr, addrstr, INET6_ADDRSTRLEN);
+            if_indextoname(index, name);
+            syslog(LOG_DEBUG, "ifaddr: Added IPv6 address %s on %s [%zu]",
+                    addrstr, name, i->addr_v6_size);
+        } else {
+            syslog(LOG_ERR, "ifaddr: Failed to reallocate an array");
+        }
+    }
+
+    unlock_mutex(&if_mutex);
+}
+
+void ifaddr_remove_addr_v6(unsigned int index,
+        const struct in6_addr *restrict addr) {
+    lock_mutex(&if_mutex);
+
+    struct ifaddr_interface *i = ifaddr_find_interface(index);
+    if (i != interfaces + interfaces_size) {
+        struct in6_addr *j = i->addr_v6;
+        while (j != i->addr_v6 + i->addr_v6_size &&
+                !IN6_ARE_ADDR_EQUAL(j, addr)) {
+            ++j;
+        }
+        // Erases the address if found.
+        if (j != i->addr_v6 + i->addr_v6_size) {
+            struct in6_addr *k = j++;
+            while (j != i->addr_v6 + i->addr_v6_size) {
+                *k++ = *j++;
+            }
+            i->addr_v6_size -= 1;
+
+            if (i->addr_v6_size == 0 && if_change_handler) {
+                struct ifaddr_change change = {
+                    .type = IFADDR_REMOVED,
+                    .ifindex = index,
+                };
+                (*if_change_handler)(&change);
+            }
+
+            char addrstr[INET6_ADDRSTRLEN];
+            char name[IF_NAMESIZE];
+            inet_ntop(AF_INET6, addr, addrstr, INET6_ADDRSTRLEN);
+            if_indextoname(index, name);
+            syslog(LOG_DEBUG, "ifaddr: Removed IPv6 address %s on %s [%zu]",
+                    addrstr, name, i->addr_v4_size);
+
+            if (ifaddr_interface_is_free(i)) {
+                ifaddr_erase_interface(i);
+            }
+        }
+    }
+
+    unlock_mutex(&if_mutex);
+}
+
 int ifaddr_start(void) {
     if (!ifaddr_initialized()) {
         return ENXIO;
     if (nlmsg->nlmsg_len >= rta_offset) {
         const struct ifaddrmsg *ifa = (const struct ifaddrmsg *)
                 NLMSG_DATA(nlmsg);
-        // Handles link-local or wider interfaces only.
-        if (ifa->ifa_scope <= RT_SCOPE_LINK) {
+        // Only handles addresses both non-temporary and at least link-local.
+        if ((ifa->ifa_flags & (IFA_F_TEMPORARY | IFA_F_TENTATIVE)) == 0 &&
+                ifa->ifa_scope <= RT_SCOPE_LINK) {
             const struct rtattr *rta = (const struct rtattr *)
                     ((const char *) nlmsg + rta_offset);
             size_t rta_len = nlmsg->nlmsg_len - rta_offset;
                 rta->rta_len >= RTA_LENGTH(sizeof (struct in6_addr))) {
             const struct in6_addr *addr =
                     (const struct in6_addr *) RTA_DATA(rta);
-            // TODO: Add support for global addresses.
-            if (IN6_IS_ADDR_LINKLOCAL(addr)) {
-                switch (nlmsg_type) {
-                case RTM_NEWADDR:
-                    ifaddr_add_addr_v6(index, addr);
-                    break;
+            switch (nlmsg_type) {
+            case RTM_NEWADDR:
+                ifaddr_add_addr_v6(index, addr);
+                break;
 
-                case RTM_DELADDR:
-                    ifaddr_remove_addr_v6(index, addr);
-                    break;
-                }
+            case RTM_DELADDR:
+                ifaddr_remove_addr_v6(index, addr);
+                break;
             }
         }
 
     if (refresh_not_in_progress) {
         lock_mutex(&if_mutex);
         // TODO: Call the change handler for each interface.
-        if_table_size = 0;
+        interfaces_size = 0;
         unlock_mutex(&if_mutex);
 
         unsigned char buf[NLMSG_LENGTH(sizeof (struct ifaddrmsg))];
     return err;
 }
 
-int ifaddr_lookup(unsigned int ifindex, struct in6_addr *restrict addr_out) {
+int ifaddr_lookup_v6(unsigned int index, size_t addr_size,
+        // Using 'struct in6_addr addr[restrict]' caused an error on CLang.
+        struct in6_addr *restrict addr, size_t *number_of_addresses) {
     if (!ifaddr_initialized() || !ifaddr_started()) {
         return ENXIO;
     }
 
     lock_mutex(&if_mutex);
 
-    unsigned int i = 0;
-    while (i != if_table_size && if_table[i].ifindex != ifindex) {
-        ++i;
-    }
+    int err = 0;
+    const struct ifaddr_interface *i = ifaddr_find_interface(index);
+    if (i != interfaces + interfaces_size) {
+        const struct in6_addr *restrict j = i->addr_v6;
+        if (addr_size > i->addr_v6_size) {
+            addr_size = i->addr_v6_size;
+        }
+        while (addr_size--) {
+            *addr++ = *j++;
+        }
 
-    int err = 0;
-    if (i != if_table_size) {
-        if (addr_out) {
-            *addr_out = if_table[i].addr;
-        }
+        *number_of_addresses = i->addr_v6_size;
     } else {
         err = ENODEV;
     }

File src/ifaddr.h

 /*
- * Interface address lookups (interface)
- * Copyright (C) 2013  Kaz Nishimura
+ * ifaddr - interface addresses (interface)
+ * Copyright (C) 2013-2014 Kaz Nishimura
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
 extern int ifaddr_refresh(void);
 
 /**
- * Looks up the address of an interface.
+ * Looks up the IPv6 addresses of an interface.
  * This module MUST be initialized and started.
- * @param __ifindex interface index.
- * @param __addr [out] interface address; if its value is null, no output will
- * be retrieved.
- * @return 0 if any address is found, 'ENODEV' if no address is found, 'ENXIO'
- * if this module is not started, or any non-zero error number.
+ * @param __index interface index.
+ * @param __addr_size maximum size of the output array.
+ * @param __addr array pointer to the interface addresses.  If the actual
+ * number of interface addresses is greater than the array size, ones that do
+ * not fit this array will be discarded.
+ * @param __number_of_addresses [out] number of the interface addresses.
+ * @return 0 if the interface index is valid, 'ENODEV' if not, 'ENXIO' if this
+ * module is not started, or any non-zero error number.
  */
-extern int ifaddr_lookup(unsigned int __ifindex, struct in6_addr *__addr);
+extern int ifaddr_lookup_v6(unsigned int __index, size_t __addr_size,
+        struct in6_addr __addr[], size_t *__number_of_addresses);
 
 #endif

File src/responder.c

 /*
- * LLMNR responder (implementation)
- * Copyright (C) 2013 Kaz Nishimura
+ * responder - LLMNR responder (implementation)
+ * Copyright (C) 2013-2014 Kaz Nishimura
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
 #include <config.h>
 #endif
 #ifndef _GNU_SOURCE
+// This definition might be required to enable RFC 3542 API.
 #define _GNU_SOURCE 1
 #endif
 #ifndef _DARWIN_C_SOURCE
 #include <stdbool.h>
 #include <assert.h>
 
+#ifndef IPV6_DONTFRAG
+// Workaround for undefined 'IPV6_DONTFRAG' on Linux-based systems.
+#if __linux__
+#define IPV6_DONTFRAG 62
+#endif
+#endif /* !defined IPV6_DONTFRAG */
+
 /**
  * Sets socket options for an IPv6 UDP responder socket.
  * @param fd file descriptor of a socket.
                 unicast_hops, strerror(errno));
     }
 
+#ifdef IPV6_DONTFRAG
+    int dontfrag = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &dontfrag, sizeof (int))
+            != 0) {
+        syslog(LOG_WARNING, "Could not set IPV6_DONTFRAG to %d: %s",
+                dontfrag, strerror(errno));
+    }
+#else
+    syslog(LOG_WARNING,
+            "No socket option to disable IPv6 packet fragmentation");
+#endif
+
     return 0;
 }
 
 int responder_respond_for_name(unsigned int index,
         const struct llmnr_header *query, const uint8_t *query_qname_end,
         const struct sockaddr_in6 *restrict sender) {
-    size_t packet_size = query_qname_end + 4 - (const uint8_t *) query;
+    size_t query_size = query_qname_end + 4 - (const uint8_t *) query;
+    size_t packet_size = query_size;
+    size_t number_of_addr_v6 = 0;
 
-    uint8_t packet[packet_size + 512]; // TODO: Check the array size.
-    memcpy(packet, query, packet_size);
+    uint_fast16_t qtype = llmnr_get_uint16(query_qname_end);
+    uint_fast16_t qclass = llmnr_get_uint16(query_qname_end + 2);
+    if (qclass == LLMNR_QCLASS_IN) {
+        switch (qtype) {
+            int err;
+
+        case LLMNR_QTYPE_AAAA:
+        case LLMNR_QTYPE_ANY:
+            err = ifaddr_lookup_v6(index, 0, NULL, &number_of_addr_v6);
+            if (err == 0 && number_of_addr_v6 != 0) {
+                packet_size += 1 + host_name[0];
+                packet_size -= 2;
+                packet_size += number_of_addr_v6 * (2 + 10
+                        + sizeof (struct in6_addr));
+            } else {
+                char ifname[IF_NAMESIZE];
+                if_indextoname(index, ifname);
+                syslog(LOG_NOTICE, "No interface addresses found for %s",
+                        ifname);
+            }
+            break;
+        }
+    }
+
+    uint8_t packet[packet_size];
+    memcpy(packet, query, query_size);
 
     struct llmnr_header *response = (struct llmnr_header *) packet;
     response->flags = htons(LLMNR_HEADER_QR);
     response->nscount = htons(0);
     response->arcount = htons(0);
 
-    uint_fast16_t qtype = llmnr_get_uint16(query_qname_end);
-    uint_fast16_t qclass = llmnr_get_uint16(query_qname_end + 2);
-    if (qclass == LLMNR_QCLASS_IN) {
-        switch (qtype) {
-            struct in6_addr addr_v6;
-            int err;
+    uint8_t *packet_end = packet + query_size;
+    if (number_of_addr_v6 != 0) {
+        struct in6_addr addr_v6[number_of_addr_v6];
+        size_t n = 0;
+        int e = ifaddr_lookup_v6(index, number_of_addr_v6, addr_v6, &n);
+        if (e == 0 && n < number_of_addr_v6) {
+            // The number of interface addresses changed.
+            // TODO: We should log it.
+            number_of_addr_v6 = n;
+        }
 
-        case LLMNR_QTYPE_AAAA:
-        case LLMNR_QTYPE_ANY:
-            err = ifaddr_lookup(index, &addr_v6);
-            if (err == 0) {
-                char addrstr[INET6_ADDRSTRLEN];
-                inet_ntop(AF_INET6, &addr_v6, addrstr, INET6_ADDRSTRLEN);
-                syslog(LOG_DEBUG, "Found interface address %s", addrstr);
+        for (size_t i = 0; i != number_of_addr_v6; ++i) {
+            // TODO: Clean up the following code.
+            if (packet_end == packet + query_size) {
+                // The first must be a name.
+                memcpy(packet_end, host_name, 1 + host_name[0]);
+                packet_end += 1 + host_name[0];
+                *packet_end++ = '\0';
+            } else {
+                llmnr_put_uint16(0xc000 + query_size, packet_end);
+                packet_end += 2;
+            }
+            // TYPE
+            llmnr_put_uint16(LLMNR_TYPE_AAAA, packet_end);
+            packet_end += 2;
+            // CLASS
+            llmnr_put_uint16(LLMNR_CLASS_IN, packet_end);
+            packet_end += 2;
+            // TTL
+            // TODO: We should make the TTL value configurable?
+            llmnr_put_uint32(30, packet_end);
+            packet_end += 4;
+            // RDLENGTH
+            llmnr_put_uint16(sizeof (struct in6_addr), packet_end);
+            packet_end += 2;
+            // RDATA
+            memcpy(packet_end, &addr_v6[i], sizeof (struct in6_addr));
+            packet_end += sizeof (struct in6_addr);
+        }
 
-                // TODO: Clean up the following code.
-                memcpy(packet + packet_size, host_name, 1 + host_name[0]);
-                packet_size += 1 + host_name[0];
-                packet[packet_size++] = '\0';
-                // TYPE
-                llmnr_put_uint16(LLMNR_TYPE_AAAA, packet + packet_size);
-                packet_size += 2;
-                // CLASS
-                llmnr_put_uint16(LLMNR_CLASS_IN, packet + packet_size);
-                packet_size += 2;
-                // TTL
-                llmnr_put_uint32(30, packet + packet_size);
-                packet_size += 4;
-                // RDLENGTH
-                llmnr_put_uint16(sizeof addr_v6, packet + packet_size);
-                packet_size += 2;
-                // RDATA
-                memcpy(packet + packet_size, &addr_v6, sizeof addr_v6);
-                packet_size += sizeof (struct in6_addr);
-
-                response->ancount = htons(ntohs(response->ancount) + 1);
-            } else {
-                char ifname[IF_NAMESIZE];
-                if_indextoname(index, ifname);
-                syslog(LOG_NOTICE, "Interface address not found for %s",
-                        ifname);
-            }
-            break;
-
-        default:
-            // Leaves the answer section empty.
-            break;
-        }
+        response->ancount = htons(ntohs(response->ancount)
+                + number_of_addr_v6);
     }
 
     // Sends the response.
-    if (sendto(udp_fd, packet, packet_size, 0, sender,
+    if (sendto(udp_fd, packet, packet_end - packet, 0, sender,
             sizeof (struct sockaddr_in6)) >= 0) {
         return 0;
     }
 
+    // TODO
     if (packet_size > 512 && errno == EMSGSIZE) {
         // Resends with truncation.
         response->flags |= htons(LLMNR_HEADER_TC);
-        if (sendto(udp_fd, packet, 512, 0, sender,
+        if (sendto(udp_fd, response, 512, 0, sender,
                 sizeof (struct sockaddr_in6)) >= 0) {
             return 0;
         }

File src/responder.h

 /*
- * LLMNR responder (interface)
- * Copyright (C) 2013 Kaz Nishimura
+ * responder - LLMNR responder (interface)
+ * Copyright (C) 2013-2014 Kaz Nishimura
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free

File test/testifaddr.cpp

 /*
- * Test fixture for the ifaddr module
- * Copyright (C) 2013  Kaz Nishimura
+ * testifaddr - test fixtures for ifaddr
+ * Copyright (C) 2013-2014 Kaz Nishimura
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
 #include <cppunit/extensions/HelperMacros.h>
 #include <cppunit/TestFixture.h>
 #include <netinet/in.h>
+#include <net/if.h>
+#include <vector>
 #include <csignal>
 #include <cerrno>
 
 using namespace std;
 using CppUnit::TestFixture;
 
-class IfaddrTest : public TestFixture {
-    CPPUNIT_TEST_SUITE(IfaddrTest);
+/*
+ * Uninitialized tests for ifaddr.
+ */
+class IfaddrPreTests : public TestFixture {
+    CPPUNIT_TEST_SUITE(IfaddrPreTests);
     CPPUNIT_TEST(testInitialize);
-    CPPUNIT_TEST(testSetHandler);
-    CPPUNIT_TEST(testStart);
-    CPPUNIT_TEST(testRefresh);
-    CPPUNIT_TEST(testLookup);
+    CPPUNIT_TEST(testFailures);
     CPPUNIT_TEST_SUITE_END();
 
 public:
         ifaddr_finalize();
         // This MUST succeed again.
         CPPUNIT_ASSERT_EQUAL(0, ifaddr_initialize(SIGUSR2));
-        ifaddr_finalize();
     }
 
-    void testSetHandler() {
+    void testFailures() {
         ifaddr_change_handler handler = &handle_change;
-        // Before initialization, an error MUST be detected.
+        // Without initialization, an error MUST be detected.
         CPPUNIT_ASSERT_EQUAL(ENXIO, ifaddr_set_change_handler(&handle_change,
                 &handler));
-        ifaddr_initialize(SIGUSR2);
-        // After initialization, the handler function MUST be null.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(&handle_change,
-                &handler));
-        CPPUNIT_ASSERT(handler == NULL);
-        // The function MUST be retrieved.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(NULL, &handler));
-        CPPUNIT_ASSERT(handler == &handle_change);
-        // And null MUST be retrieved again.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(&handle_change,
-                &handler));
-        CPPUNIT_ASSERT(handler == NULL);
-        // The handler function remains set.
-        ifaddr_finalize();
-        // After finalization, an error MUST be detected.
-        CPPUNIT_ASSERT_EQUAL(ENXIO, ifaddr_set_change_handler(&handle_change,
-                &handler));
-        ifaddr_initialize(SIGUSR2);
-        // After initialization, the handler function MUST be null again.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(NULL, &handler));
-        CPPUNIT_ASSERT(handler == NULL);
-        // Setting the function without retrieving the old one.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(&handle_change,
-                NULL));
-        // And the function MUST be retrieved.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(NULL, &handler));
-        CPPUNIT_ASSERT(handler == &handle_change);
 
-        // TODO: The handler function MUST be called on each interface change.
-    }
-
-    void testStart() {
         CPPUNIT_ASSERT_ASSERTION_FAIL(
                 // This MUST fail.
                 CPPUNIT_ASSERT_EQUAL(0, ifaddr_start()));
-        ifaddr_initialize(SIGUSR2);
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_start());
-        // This MUST succeed.
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_start());
-        ifaddr_finalize();
-        CPPUNIT_ASSERT_ASSERTION_FAIL(
-                // This MUST fail again.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_start()));
-    }
-
-    void testRefresh() {
         CPPUNIT_ASSERT_ASSERTION_FAIL(
                 // This MUST fail.
                 CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh()));
-        ifaddr_initialize(SIGUSR2);
-        CPPUNIT_ASSERT_ASSERTION_FAIL(
-                // This still MUST fail.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh()));
-        ifaddr_start();
-        CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh());
-        ifaddr_finalize();
-        CPPUNIT_ASSERT_ASSERTION_FAIL(
-                // This MUST fail again.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh()));
-    }
-
-    void testLookup() {
-        struct in6_addr addr;
+ 
+        size_t size;
         CPPUNIT_ASSERT_ASSERTION_FAIL(
                 // This MUST fail.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_lookup(0, &addr)));
-        ifaddr_initialize(SIGUSR2);
-        CPPUNIT_ASSERT_ASSERTION_FAIL(
-                // This still MUST fail.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_lookup(0, &addr)));
-        ifaddr_start();
-        CPPUNIT_ASSERT_EQUAL(ENODEV, ifaddr_lookup(0, &addr));
-        ifaddr_finalize();
-        CPPUNIT_ASSERT_ASSERTION_FAIL(
-                // This MUST fail again.
-                CPPUNIT_ASSERT_EQUAL(0, ifaddr_lookup(0, &addr)));
+                CPPUNIT_ASSERT_EQUAL(0, ifaddr_lookup_v6(0, 0, NULL, &size)));
     }
 
 protected:
     static void handle_change(const struct ifaddr_change *change) {
     }
 };
-CPPUNIT_TEST_SUITE_REGISTRATION(IfaddrTest);
+CPPUNIT_TEST_SUITE_REGISTRATION(IfaddrPreTests);
+
+/*
+ * Initialized tests for ifaddr.
+ */
+class IfaddrTests : public TestFixture {
+    CPPUNIT_TEST_SUITE(IfaddrTests);
+    CPPUNIT_TEST(testSetHandler);
+    CPPUNIT_TEST(testStart);
+    CPPUNIT_TEST(testRefresh);
+    CPPUNIT_TEST(testLookup);
+    CPPUNIT_TEST_SUITE_END();
+
+public:
+    virtual void setUp() override {
+        struct sigaction sa = {};
+        sa.sa_handler = &handle_signal;
+        sigaction(SIGUSR2, &sa, NULL);
+
+        ifaddr_initialize(SIGUSR2);
+    }
+
+    virtual void tearDown() override {
+        ifaddr_finalize();
+
+        struct sigaction sa = {};
+        sa.sa_handler = SIG_DFL;
+        sigaction(SIGUSR2, &sa, NULL);
+    }
+
+    void testSetHandler() {
+        ifaddr_change_handler handler = &handle_change;
+        // The initial handler function MUST be null.
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(&handle_change,
+                &handler));
+        CPPUNIT_ASSERT(handler == NULL);
+        // The set function MUST be retrieved.
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(NULL, &handler));
+        CPPUNIT_ASSERT(handler == &handle_change);
+        // And null MUST be retrieved again.
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_set_change_handler(&handle_change,
+                &handler));
+        CPPUNIT_ASSERT(handler == NULL);
+
+        // TODO: The handler function MUST be called on each interface change.
+    }
+
+    void testStart() {
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_start());
+        // This MUST also succeed.
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_start());
+    }
+
+    void testRefresh() {
+        CPPUNIT_ASSERT_ASSERTION_FAIL(
+                // This still MUST fail.
+                CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh()));
+
+        ifaddr_start();
+
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_refresh());
+    }
+
+    void testLookup() {
+        // TODO: These values may be missing.
+        auto lo = if_nametoindex("lo");
+        auto eth0 = if_nametoindex("eth0");
+        if (eth0 == 0) {
+            CPPUNIT_FAIL("eth0 not found");
+        }
+
+        size_t size;
+        CPPUNIT_ASSERT_ASSERTION_FAIL(
+                // This still MUST fail.
+                CPPUNIT_ASSERT_EQUAL(0,
+                ifaddr_lookup_v6(eth0, 0, NULL, &size)));
+
+        ifaddr_start();
+
+        // The loopback interface SHALL be ignored.
+        CPPUNIT_ASSERT_EQUAL(ENODEV, ifaddr_lookup_v6(lo, 0, NULL, &size));
+
+        CPPUNIT_ASSERT_EQUAL(0, ifaddr_lookup_v6(eth0, 0, NULL, &size));
+        CPPUNIT_ASSERT(size >= 0);
+
+        std::vector<struct in6_addr> addr(size);
+        CPPUNIT_ASSERT_EQUAL(0,
+                ifaddr_lookup_v6(eth0, size, &addr[0], &size));
+    }
+
+protected:
+    static void handle_signal(int sig) {
+    }
+
+    static void handle_change(const struct ifaddr_change *change) {
+    }
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(IfaddrTests);