diff --git a/README.md b/README.md index 38adefd..af59fe1 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,13 @@ enable LLMNR name resolution over IPv6 use: $ llmnrd -6 ``` +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 +``` + 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..d7975f4 100644 --- a/llmnr.c +++ b/llmnr.c @@ -40,38 +40,74 @@ 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; +size_t 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] = (uint8_t)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[], size_t hostname_count, bool ipv6) { - llmnr_set_hostname(hostname); + size_t i; + llmnr_hostname_count = hostname_count; + llmnr_hostnames = xzalloc(hostname_count); + for (i = 0; i < hostname_count; ++i) { + set_hostname(i, hostnames[i]); + } llmnr_ipv6 = ipv6; } -static bool llmnr_name_matches(const uint8_t *query) +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) { - uint8_t n = llmnr_hostname[0]; + size_t i; + + 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; + + if (strncasecmp((const char *) &query[1], &llmnr_hostnames[i][1], n) == 0) + return llmnr_hostnames[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 +200,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 +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; /* Query too short? */ if (len < sizeof(struct llmnr_hdr)) @@ -218,8 +255,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..bf243e9 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[], size_t 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..c5edac9 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; + size_t 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. 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); @@ -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;