Add hooks to compile contrib/lease-tools/dhcp_release.c. This utility is used to
send a DHCPRELEASE message to tell the local DHCP server to delete a
particular lease.

Solaris doesn't support AF_NETLINK, so use getifaddrs() to determine the
IP address of an interface on which Dnsmasq process is listening.

This patch was developed in-house. Since it is Solaris-specific it is not
suitable for upstream.

--- a/Makefile	2023-02-02 21:24:24.000000000 +0100
+++ b/Makefile	2023-05-12 10:41:49.982977448 +0200
@@ -19,6 +19,7 @@
 # Variables you may well want to override.
 
 PREFIX        = /usr/local
+LIBDIR        = $(PREFIX)/lib/inet
 BINDIR        = $(PREFIX)/sbin
 MANDIR        = $(PREFIX)/share/man
 LOCALEDIR     = $(PREFIX)/share/locale
@@ -43,6 +44,7 @@
 SRC = src
 PO  = po
 MAN = man
+LEASE_TOOLS = contrib/lease-tools
 
 #################################################################
 
@@ -86,6 +88,8 @@
        poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
        metrics.o hash-questions.o domain-match.o nftset.o
 
+dhcp_release_objs = dhcp_release.o
+
 hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
        dns-protocol.h radv-protocol.h ip6addr.h metrics.h
 
@@ -94,11 +98,12 @@
  top="$(top)" \
  build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
  build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
- -f $(top)/Makefile dnsmasq 
+ -f $(top)/Makefile dnsmasq dhcp_release
 
 mostly_clean :
 	rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot 
-	rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
+	rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq \
+		$(BUILDDIR)/dhcp_release
 
 clean : mostly_clean
 	rm -f $(BUILDDIR)/dnsmasq_baseline
@@ -110,8 +115,10 @@
 install-common :
 	$(INSTALL) -d $(DESTDIR)$(BINDIR)
 	$(INSTALL) -d $(DESTDIR)$(MANDIR)/man8
+	$(INSTALL) -d $(DESTDIR)/$(LIBDIR)
 	$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 
 	$(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)
+	$(INSTALL) -m 755 $(BUILDDIR)/dhcp_release $(DESTDIR)$(LIBDIR)
 
 all-i18n : $(BUILDDIR)
 	@cd $(BUILDDIR) && $(MAKE) \
@@ -119,7 +126,7 @@
  i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
  build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
  build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)"  \
- -f $(top)/Makefile dnsmasq
+ -f $(top)/Makefile dnsmasq dhcp_release
 	for f in `cd $(PO); echo *.po`; do \
 		cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/bakefile $${f%.po}.mo; \
 	done
@@ -162,6 +169,9 @@
 $(objs:.o=.c) $(hdrs):
 	ln -s $(top)/$(SRC)/$@ .
 
+$(dhcp_release_objs:.o=.c):
+	ln -s $(top)/$(LEASE_TOOLS)/$@ .
+
 $(objs): $(copts_conf) $(hdrs)
 
 .c.o:
@@ -170,6 +180,9 @@
 dnsmasq : $(objs)
 	$(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) 
 
+dhcp_release : $(objs) $(dhcp_release_objs)
+	$(CC) $(LDFLAGS) -o $@ $(dhcp_release_objs) $(build_libs) $(LIBS) 
+
 dnsmasq.pot : $(objs:.o=.c) $(hdrs)
 	$(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(objs:.o=.c)
 
--- a/contrib/lease-tools/dhcp_release.c	2021-09-08 22:21:22.000000000 +0000
+++ b/contrib/lease-tools/dhcp_release.c	2021-11-30 10:04:51.507053902 +0000
@@ -33,6 +33,10 @@
    The client-id is optional. If it is "*" then it treated as being missing.
 */
 
+#if defined(__sun) || defined(__sun__)
+#define HAVE_SOLARIS_NETWORK
+#endif
+
 #include <sys/types.h> 
 #include <netinet/in.h>
 #include <net/if.h>
@@ -44,9 +48,13 @@
 #include <stdlib.h>
 #include <net/if_arp.h>
 #include <sys/ioctl.h>
+#ifdef HAVE_SOLARIS_NETWORK
+#include <ifaddrs.h>
+#else
 #include <linux/types.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#endif
 #include <errno.h>
 
 #define DHCP_CHADDR_MAX          16
@@ -73,6 +81,7 @@
   unsigned char options[308];
 };
 
+#if !defined(HAVE_SOLARIS_NETWORK)
 static struct iovec iov;
 
 static int expand_buf(struct iovec *iov, size_t size)
@@ -139,6 +148,8 @@
   return rc;
 }
 
+#endif
+
 static int parse_hex(char *in, unsigned char *out, int maxlen, int *mac_type)
 {
   int i = 0;
@@ -178,6 +189,7 @@
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
 }
 
+#if !defined(HAVE_SOLARIS_NETWORK)
 static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index, int ifrfd, struct ifreq *ifr)
 {
   struct sockaddr_nl addr;
@@ -254,6 +266,7 @@
  
   exit(0);
 }
+#endif
 
 int main(int argc, char **argv)
 { 
@@ -264,7 +277,12 @@
   struct sockaddr_in dest;
   struct ifreq ifr;
   int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#ifdef HAVE_SOLARIS_NETWORK
+  int nl = 0;
+  struct ifaddrs *ifp_head, *ifp;
+#else
   int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+#endif
 
   if (argc < 4 || argc > 5)
     { 
@@ -277,7 +295,8 @@
       perror("cannot create socket");
       exit(1);
     }
-  
+
+#if !defined(HAVE_SOLARIS_NETWORK)
   /* This voodoo fakes up a packet coming from the correct interface, which really matters for 
      a DHCP server */
   strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
@@ -287,15 +306,46 @@
       perror("cannot setup interface");
       exit(1);
     }
+#endif
   
   if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
     {
       perror("invalid ip address");
       exit(1);
     }
-  
+ 
+#ifdef HAVE_SOLARIS_NETWORK
+  if (getifaddrs(&ifp_head) < 0)
+    {
+      perror("could not retrieve IP addresses");
+      exit(1);
+    }
+  for (ifp = ifp_head; ifp != NULL; ifp = ifp->ifa_next) {
+    if (ifp->ifa_addr->sa_family != AF_INET)
+      continue;
+    if (strcmp(ifp->ifa_name, argv[1]) == 0 &&
+        is_same_net(lease, ((struct sockaddr_in *)ifp->ifa_addr)->sin_addr,
+       ((struct sockaddr_in *)ifp->ifa_netmask)->sin_addr))
+      break;
+    }
+  if (ifp == NULL) {
+    freeifaddrs(ifp_head);
+    exit(0);
+  }
+  memcpy(&server, &((struct sockaddr_in *)ifp->ifa_addr)->sin_addr,
+       sizeof(server));
+  /* bind to the socket */
+  if (bind(fd, ifp->ifa_addr, sizeof(struct sockaddr_in)) == -1)
+    {
+      freeifaddrs(ifp_head);
+      perror("cannot bind to socket");
+      exit(1);
+    }
+  freeifaddrs(ifp_head);
+#else /* HAVE_SOLARIS_NETWORK */
   server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
-  
+#endif /* HAVE_SOLARIS_NETWORK */
+
   memset(&packet, 0, sizeof(packet));
  
   packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
