From 26c433efc2ba632ee0c1840d4f40c50b82dadcee Mon Sep 17 00:00:00 2001 From: David Graeff Date: Fri, 2 Apr 2021 00:28:49 +0200 Subject: [PATCH 1/2] Allow to provide multiple host names RFC 4795 allows an IP to be represented by not just one but multiple names. The "--hostname" / "-H" parameter can be given multiple times now. The use of a dynamic array of host names require a new public API method: `llmnr_release(void)` Keep llmnr_set_hostname function signature. This will always only update the first entry and is mainly used when no --hostname parameter has been provided and llmnrd reacts to system hostname changes. API changes: * Adapt llmnr_init to take an array of host name strings. Signed-off-by: David Graeff --- README.md | 7 ++++++ llmnr.c | 75 +++++++++++++++++++++++++++++++++++++++++-------------- llmnr.h | 3 ++- llmnrd.c | 41 ++++++++++++++++++++++-------- 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 38adefd..af3f45f 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,13 @@ enable LLMNR name resolution over IPv6 use: $ llmnrd -6 ``` +By default, `llmnrd` respond to name requests matching the systems hostname. +Provide one or more additional names via the `-H` argument: + +``` +$ llmnrd -H a_name -H another_name +``` + Use `llmnrd --help` to show additional usage information. Additionally, the `llmnr-query` utility is shipped together with llmnrd and diff --git a/llmnr.c b/llmnr.c index facba70..52cf3a9 100644 --- a/llmnr.c +++ b/llmnr.c @@ -40,38 +40,73 @@ static bool llmnr_ipv6 = false; /* Host name in DNS name format (length octet + name + 0 byte) */ -static char llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 2]; +#define LLMNR_LABEL_LEN (LLMNR_LABEL_MAX_SIZE + 2) +static char** llmnr_hostnames = NULL; +int llmnr_hostname_count = 0; + +static void set_hostname(int entry_index, const char *hostname) +{ + char* entry_llmnr_hostname = NULL; + + llmnr_hostnames[entry_index] = xzalloc(LLMNR_LABEL_LEN); + entry_llmnr_hostname = llmnr_hostnames[entry_index]; + + entry_llmnr_hostname[0] = strlen(hostname); + strncpy(&entry_llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE); + entry_llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0'; +} void llmnr_set_hostname(const char *hostname) { - llmnr_hostname[0] = strlen(hostname); - strncpy(&llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE); - llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0'; + set_hostname(0, hostname); } -void llmnr_init(const char *hostname, bool ipv6) +void llmnr_init(const char *hostnames[], int hostname_count, bool ipv6) { - llmnr_set_hostname(hostname); + int name_i = 0; + llmnr_hostname_count = hostname_count; + llmnr_hostnames = xzalloc(hostname_count); + for (;name_i < hostname_count; ++name_i) { + set_hostname(name_i, hostnames[name_i]); + } llmnr_ipv6 = ipv6; } -static bool llmnr_name_matches(const uint8_t *query) +void llmnr_release() { + int name_i = 0; + for(; name_i < llmnr_hostname_count; ++name_i) { + free(llmnr_hostnames[name_i]); + } + free(llmnr_hostnames); +} + +/* Return the matched name entry (first byte represents the string length) or NULL */ +static char* llmnr_name_matches(const uint8_t *query) { - uint8_t n = llmnr_hostname[0]; + uint8_t n; + int name_i = 0; + + for(; name_i < llmnr_hostname_count; ++name_i) { + n = llmnr_hostnames[name_i][0]; + + /* length */ + if (query[0] != n) + continue; + /* NULL byte */ + if (query[1 + n] != 0) + continue; + + if (strncasecmp((const char *)&query[1], &llmnr_hostnames[name_i][1], n) == 0) + return llmnr_hostnames[name_i]; + } - /* length */ - if (query[0] != n) - return false; - /* NULL byte */ - if (query[1 + n] != 0) - return false; - return strncasecmp((const char *)&query[1], &llmnr_hostname[1], n) == 0; + return NULL; } static void llmnr_respond(unsigned int ifindex, const struct llmnr_hdr *hdr, const uint8_t *query, size_t query_len, int sock, - const struct sockaddr_storage *sst) + const struct sockaddr_storage *sst, char* matched_hostname_entry) { uint16_t qtype, qclass; uint8_t name_len = query[0]; @@ -164,7 +199,7 @@ static void llmnr_respond(unsigned int ifindex, const struct llmnr_hdr *hdr, /* NAME */ if (i == 0) - memcpy(pkt_put(p, llmnr_hostname[0] + 2), llmnr_hostname, llmnr_hostname[0] + 2); + memcpy(pkt_put(p, matched_hostname_entry[0] + 2), matched_hostname_entry, matched_hostname_entry[0] + 2); else { /* message compression (RFC 1035, section 4.1.3) */ uint16_t ptr = 0xC000 | (sizeof(*hdr) + query_len); @@ -196,6 +231,7 @@ static void llmnr_packet_process(int ifindex, const uint8_t *pktbuf, size_t len, const uint8_t *query; size_t query_len; uint8_t name_len; + char* matched_hostname_entry; /* Query too short? */ if (len < sizeof(struct llmnr_hdr)) @@ -218,8 +254,9 @@ static void llmnr_packet_process(int ifindex, const uint8_t *pktbuf, size_t len, return; /* Authoritative? */ - if (llmnr_name_matches(query)) - llmnr_respond(ifindex, hdr, query, query_len, sock, sst); + matched_hostname_entry = llmnr_name_matches(query); + if (matched_hostname_entry) + llmnr_respond(ifindex, hdr, query, query_len, sock, sst, matched_hostname_entry); } void llmnr_recv(int sock) diff --git a/llmnr.h b/llmnr.h index 6380ebf..7853324 100644 --- a/llmnr.h +++ b/llmnr.h @@ -22,7 +22,8 @@ #include void llmnr_set_hostname(const char *hostname); -void llmnr_init(const char *hostname, bool ipv6); +void llmnr_init(const char *hostnames[], int hostname_count, bool ipv6); +void llmnr_release(void); void llmnr_recv(int sock); #endif /* LLMNR_H */ diff --git a/llmnrd.c b/llmnrd.c index 81a5ac1..0bae185 100644 --- a/llmnrd.c +++ b/llmnrd.c @@ -187,7 +187,8 @@ int main(int argc, char **argv) int c, ret = -1; long num_arg; bool daemonize = false, ipv6 = false; - char *hostname = NULL; + char **hostnames = NULL; + int name_count = 0, name_i = 0; char *iface = NULL; uint16_t port = LLMNR_UDP_PORT; int llmnrd_sock_rtnl = -1; @@ -196,6 +197,17 @@ int main(int argc, char **argv) setlinebuf(stdout); + /* First count given (host)names, if any, to allocate memory */ + while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + if (c == 'H') + ++name_count; + } + + if (!name_count) + name_count = 1; + hostnames = xzalloc(name_count); + + optind = 1; while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (c) { case 'd': @@ -206,7 +218,8 @@ int main(int argc, char **argv) log_to_syslog(); break; case 'H': - hostname = xstrdup(optarg); + hostnames[name_i] = xstrdup(optarg); + ++name_i; break; case 'i': iface = xstrdup(optarg); @@ -236,13 +249,13 @@ int main(int argc, char **argv) register_signal(SIGTERM, signal_handler); register_signal(SIGHUP, signal_handler); - if (!hostname) { - hostname = xzalloc(MAXHOSTNAMELEN); - if (gethostname(hostname, MAXHOSTNAMELEN) != 0) { + if (name_i == 0) { + hostnames[0] = xzalloc(MAXHOSTNAMELEN); + if (gethostname(hostnames[0], MAXHOSTNAMELEN) != 0) { log_err("Failed to get hostname"); return EXIT_FAILURE; } - hostname[MAXHOSTNAMELEN - 1] = '\0'; + hostnames[0][MAXHOSTNAMELEN - 1] = '\0'; llmnrd_fd_hostname = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY); } @@ -257,7 +270,11 @@ int main(int argc, char **argv) rm_pid_file = true; } - log_info("Starting llmnrd on port %u, hostname %s\n", port, hostname); + log_info("Starting llmnrd on port %u, hostname(s): ", port); + for(name_i = 0; name_i < name_count; ++name_i) + log_info("%s ", hostnames[name_i]); + log_info("\n"); + if (iface) log_info("Binding to interface %s\n", iface); @@ -275,7 +292,7 @@ int main(int argc, char **argv) if (llmnrd_sock_rtnl < 0) goto out; - llmnr_init(hostname, ipv6); + llmnr_init((const char **) hostnames, name_count, ipv6); ret = iface_init(llmnrd_sock_rtnl, iface, ipv6, &iface_event_handle); if (ret < 0) @@ -318,7 +335,7 @@ int main(int argc, char **argv) if (llmnrd_sock_ipv6 >= 0 && FD_ISSET(llmnrd_sock_ipv6, &rfds)) llmnr_recv(llmnrd_sock_ipv6); if (llmnrd_fd_hostname >= 0 && FD_ISSET(llmnrd_fd_hostname, &efds)) - hostname_change_handle(hostname, MAXHOSTNAMELEN); + hostname_change_handle(hostnames[0], MAXHOSTNAMELEN); } } @@ -332,7 +349,11 @@ int main(int argc, char **argv) close(llmnrd_sock_ipv6); if (llmnrd_sock_ipv4 >= 0) close(llmnrd_sock_ipv4); - free(hostname); + for(name_i = 0; name_i < name_count; ++name_i) { + free(hostnames[name_i]); + } + free(hostnames); + llmnr_release(); if (rm_pid_file) unlink(PIDFILE); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; From c4329356955b08775d01ec63fae5a7a0621f9f65 Mon Sep 17 00:00:00 2001 From: David Graeff Date: Tue, 6 Apr 2021 20:34:10 +0200 Subject: [PATCH 2/2] Address review comments --- README.md | 4 ++-- llmnr.c | 51 ++++++++++++++++++++++++++------------------------- llmnr.h | 2 +- llmnrd.c | 10 +++++----- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index af3f45f..af59fe1 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ enable LLMNR name resolution over IPv6 use: $ llmnrd -6 ``` -By default, `llmnrd` respond to name requests matching the systems hostname. -Provide one or more additional names via the `-H` argument: +By default, `llmnrd` responds to name requests matching the systems hostname. +Instead you can provide one or more names via the `-H` argument: ``` $ llmnrd -H a_name -H another_name diff --git a/llmnr.c b/llmnr.c index 52cf3a9..d7975f4 100644 --- a/llmnr.c +++ b/llmnr.c @@ -42,7 +42,7 @@ static bool llmnr_ipv6 = false; /* Host name in DNS name format (length octet + name + 0 byte) */ #define LLMNR_LABEL_LEN (LLMNR_LABEL_MAX_SIZE + 2) static char** llmnr_hostnames = NULL; -int llmnr_hostname_count = 0; +size_t llmnr_hostname_count = 0; static void set_hostname(int entry_index, const char *hostname) { @@ -51,7 +51,7 @@ static void set_hostname(int entry_index, const char *hostname) llmnr_hostnames[entry_index] = xzalloc(LLMNR_LABEL_LEN); entry_llmnr_hostname = llmnr_hostnames[entry_index]; - entry_llmnr_hostname[0] = strlen(hostname); + entry_llmnr_hostname[0] = (uint8_t)strlen(hostname); strncpy(&entry_llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE); entry_llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0'; } @@ -61,44 +61,45 @@ void llmnr_set_hostname(const char *hostname) set_hostname(0, hostname); } -void llmnr_init(const char *hostnames[], int hostname_count, bool ipv6) +void llmnr_init(const char *hostnames[], size_t hostname_count, bool ipv6) { - int name_i = 0; + size_t i; llmnr_hostname_count = hostname_count; llmnr_hostnames = xzalloc(hostname_count); - for (;name_i < hostname_count; ++name_i) { - set_hostname(name_i, hostnames[name_i]); + for (i = 0; i < hostname_count; ++i) { + set_hostname(i, hostnames[i]); } llmnr_ipv6 = ipv6; } -void llmnr_release() { - int name_i = 0; - for(; name_i < llmnr_hostname_count; ++name_i) { - free(llmnr_hostnames[name_i]); +void llmnr_release(void) +{ + size_t i; + for(i = 0; i < llmnr_hostname_count; ++i) { + free(llmnr_hostnames[i]); } free(llmnr_hostnames); } /* Return the matched name entry (first byte represents the string length) or NULL */ -static char* llmnr_name_matches(const uint8_t *query) +static char *llmnr_name_matches(const uint8_t *query) { - uint8_t n; - int name_i = 0; + size_t i; - for(; name_i < llmnr_hostname_count; ++name_i) { - n = llmnr_hostnames[name_i][0]; + for (i = 0; i < llmnr_hostname_count; ++i) { + uint8_t n; + n = llmnr_hostnames[i][0]; - /* length */ - if (query[0] != n) - continue; - /* NULL byte */ - if (query[1 + n] != 0) - continue; + /* length */ + if (query[0] != n) + continue; + /* NULL byte */ + if (query[1 + n] != 0) + continue; - if (strncasecmp((const char *)&query[1], &llmnr_hostnames[name_i][1], n) == 0) - return llmnr_hostnames[name_i]; - } + if (strncasecmp((const char *) &query[1], &llmnr_hostnames[i][1], n) == 0) + return llmnr_hostnames[i]; + } return NULL; @@ -231,7 +232,7 @@ static void llmnr_packet_process(int ifindex, const uint8_t *pktbuf, size_t len, const uint8_t *query; size_t query_len; uint8_t name_len; - char* matched_hostname_entry; + char* matched_hostname_entry; /* Query too short? */ if (len < sizeof(struct llmnr_hdr)) diff --git a/llmnr.h b/llmnr.h index 7853324..bf243e9 100644 --- a/llmnr.h +++ b/llmnr.h @@ -22,7 +22,7 @@ #include void llmnr_set_hostname(const char *hostname); -void llmnr_init(const char *hostnames[], int hostname_count, bool ipv6); +void llmnr_init(const char *hostnames[], size_t hostname_count, bool ipv6); void llmnr_release(void); void llmnr_recv(int sock); diff --git a/llmnrd.c b/llmnrd.c index 0bae185..c5edac9 100644 --- a/llmnrd.c +++ b/llmnrd.c @@ -188,7 +188,7 @@ int main(int argc, char **argv) long num_arg; bool daemonize = false, ipv6 = false; char **hostnames = NULL; - int name_count = 0, name_i = 0; + size_t name_count = 0, name_i = 0; char *iface = NULL; uint16_t port = LLMNR_UDP_PORT; int llmnrd_sock_rtnl = -1; @@ -270,10 +270,10 @@ int main(int argc, char **argv) rm_pid_file = true; } - log_info("Starting llmnrd on port %u, hostname(s): ", port); - for(name_i = 0; name_i < name_count; ++name_i) - log_info("%s ", hostnames[name_i]); - log_info("\n"); + log_info("Starting llmnrd on port %u. Assigned hostname(s):\n", port); + + for(name_i = 0; name_i < name_count; ++name_i) + log_info("%s\n", hostnames[name_i]); if (iface) log_info("Binding to interface %s\n", iface);