Signed-off-by: Gavin Lambert <gavinl@compacsort.com>

diff -ur a/slpd/slpd_main.c b/slpd/slpd_main.c
--- a/slpd/slpd_main.c
+++ b/slpd/slpd_main.c
@@ -199,10 +199,8 @@
 #endif
 
 /** Handles a SIG_HUP signal from the system.
- *
- * @internal
  */
-static void HandleSigHup(void)
+void HandleSigHup(void)
 {
    /* Reinitialize */
    SLPDLog("****************************************\n");
@@ -224,6 +222,9 @@
    /* Re-read the static registration file (slp.reg)*/
    SLPDDatabaseReInit(G_SlpdCommandLine.regfile);
 
+   /* Reopen listening sockets */
+   SLPDIncomingReinit();
+
    /* Rebuild Known DA database */
    SLPDKnownDAInit();
 
@@ -231,6 +232,7 @@
    SLPDLogTime();
    SLPDLog("SLPD daemon reset finished\n");
    SLPDLog("****************************************\n\n");
+   SLPDLog("Agent Interfaces = %s\n", G_SlpdProperty.interfaces);
 }
 
 /** Handles a SIG_ALRM signal from the system.
diff -ur a/slpd/slpd_win32.c b/slpd/slpd_win32.c
--- a/slpd/slpd_win32.c
+++ b/slpd/slpd_win32.c
@@ -73,7 +73,161 @@
       fd_set * readfds, fd_set * writefds);
 void HandleSigTerm(void);
 void HandleSigAlrm(void);
+void HandleSigHup(void);
 
+/* Callback parameter type redefined here as some versions of the Windows SDK
+   do not specify a calling convention for the callback type; the compiler 
+   will typically default to assuming CDECL, but the function must really be 
+   STDCALL.  That way lies crashes. */
+typedef VOID (WINAPI *PSLP_IPINTERFACE_CHANGE_CALLBACK)(IN PVOID CallerContext,
+                                                        IN PMIB_IPINTERFACE_ROW Row OPTIONAL,
+                                                        IN MIB_NOTIFICATION_TYPE NotificationType);
+
+typedef NETIO_STATUS (WINAPI *PNotifyIpInterfaceChange)(IN ADDRESS_FAMILY Family,
+                                                        IN PSLP_IPINTERFACE_CHANGE_CALLBACK Callback,
+                                                        IN PVOID CallerContext,
+                                                        IN BOOLEAN InitialNotification,
+                                                        IN OUT HANDLE *NotificationHandle);
+typedef NETIO_STATUS (WINAPI *PCancelMibChangeNotify2)(IN HANDLE NotificationHandle);
+
+/* interface address monitoring data (encapsulated to provide a bit of abstraction) */
+struct interface_monitor
+{
+   HANDLE hAddrChange;
+   /* WinXP data (IPv4 only) */
+   OVERLAPPED addrChange;
+   /* WinVista data (IPv4+IPv6) */
+   HMODULE hNetIoLib;
+   PNotifyIpInterfaceChange pNotifyIpInterfaceChange;
+   PCancelMibChangeNotify2 pCancelMibChangeNotify2;
+   BOOL addrChanged;
+};
+
+/** Callback for Vista+ IPv4/IPv6 interface change monitoring.
+ *
+ * @internal
+ */
+static VOID CALLBACK InterfaceMonitorCallback(IN PVOID CallerContext, IN PMIB_IPINTERFACE_ROW Row OPTIONAL, IN MIB_NOTIFICATION_TYPE NotificationType)
+{
+   struct interface_monitor *self = (struct interface_monitor *) CallerContext;
+   Row;  /* unused */
+   NotificationType;  /* unused */
+
+   self->addrChanged = TRUE;
+}
+
+/** Initializes the interface monitoring.
+ *
+ * @param[in] self - An uninitialized struct interface_monitor.
+ *
+ * @internal
+ */
+static void InterfaceMonitorInit(struct interface_monitor *self)
+{
+   self->hAddrChange = NULL;
+   memset(&self->addrChange, 0, sizeof(self->addrChange));
+
+   /* try to load the Vista+ IPv6 functions */
+   self->hNetIoLib = LoadLibraryA("iphlpapi");
+   if (self->hNetIoLib)
+   {
+      self->pNotifyIpInterfaceChange = (PNotifyIpInterfaceChange) GetProcAddress(self->hNetIoLib, "NotifyIpInterfaceChange");
+      self->pCancelMibChangeNotify2 = (PCancelMibChangeNotify2) GetProcAddress(self->hNetIoLib, "CancelMibChangeNotify2");
+      if (!(self->pNotifyIpInterfaceChange && self->pNotifyIpInterfaceChange))
+      {
+         FreeLibrary(self->hNetIoLib);
+         self->hNetIoLib = NULL;
+      }
+      self->addrChanged = FALSE;
+   }
+
+   /* register for IP change notifications */
+   if (self->hNetIoLib)
+   {
+      /* Note that the cast is required here as some versions of the Windows SDK do not specify a calling
+         convention for the callback type; the compiler will typically default to assuming CDECL, but the
+         function must really be STDCALL.  Without the cast we get a compiler error when that happens. */
+      if (self->pNotifyIpInterfaceChange(AF_UNSPEC, &InterfaceMonitorCallback, self, FALSE, &self->hAddrChange))
+      {
+         SLPDLog("Error watching for IPv4/IPv6 interface changes; continuing anyway...\n");
+      }
+   }
+   else
+   {
+      if (NotifyAddrChange(&self->hAddrChange, &self->addrChange) != ERROR_IO_PENDING)
+      {
+         SLPDLog("Error watching for IPv4 interface changes, continuing anyway...\n");
+      }
+   }
+}
+
+/** Deinitializes the interface monitoring.
+ *
+ * @param[in] self - A struct interface_monitor.
+ *
+ * @internal
+ */
+static void InterfaceMonitorDeinit(struct interface_monitor *self)
+{
+   if (self->hNetIoLib)
+   {
+      /* Vista+ */
+      if (self->hAddrChange)
+      {
+         self->pCancelMibChangeNotify2(self->hAddrChange);
+         self->hAddrChange = NULL;
+         FreeLibrary(self->hNetIoLib);
+         self->hNetIoLib = NULL;
+      }
+   }
+   else
+   {
+      /* XP */
+      if (self->hAddrChange)
+      {
+         CancelIPChangeNotify(&self->addrChange);
+         self->hAddrChange = NULL;
+      }
+   }
+}
+
+/** Reports whether interfaces have been changed.
+ *
+ * @param[in] self - An initialized struct interface_monitor.
+ *
+ * @return A boolean value; TRUE if interfaces have changed, FALSE otherwise.
+ *
+ * @internal
+ */
+static BOOL InterfacesChanged(struct interface_monitor *self)
+{
+   if (self->hNetIoLib)
+   {
+      /* Vista+ */
+      if (self->addrChanged)
+      {
+         self->addrChanged = FALSE;
+         return TRUE;
+      }
+      return FALSE;
+   }
+   else
+   {
+      /* XP */
+      if (self->hAddrChange)
+      {
+         DWORD dwResult;
+         if (GetOverlappedResult(self->hAddrChange, &self->addrChange, &dwResult, FALSE))
+         {
+            /* start watching again for further interface changes */
+            NotifyAddrChange(&self->hAddrChange, &self->addrChange);
+            return TRUE;
+         }
+      }
+   }
+   return FALSE;
+}
+
 /** Reports the current status of the service to the SCM.
  *
  * @param[in] dwCurrentState - The state of the service.
@@ -96,7 +250,8 @@
       if (dwCurrentState == SERVICE_START_PENDING)
          ssStatus.dwControlsAccepted = 0;
       else
-         ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 
+         ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
+                                     | SERVICE_ACCEPT_PARAMCHANGE;
 
       ssStatus.dwCurrentState = dwCurrentState; 
       ssStatus.dwWin32ExitCode = dwWin32ExitCode; 
@@ -153,7 +308,7 @@
 static void ServiceStop(void) 
 {
    G_SIGTERM = 1;
-   ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 3000);
+   ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 3000);
 } 
 
 /** Start the service and report it.
@@ -173,7 +328,8 @@
    time_t alarmtime;
    struct timeval timeout;
    WSADATA wsaData; 
-   WORD wVersionRequested = MAKEWORD(1, 1); 
+   WORD wVersionRequested = MAKEWORD(1, 1);
+   struct interface_monitor monitor;
 
    /* service initialization */
    if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
@@ -224,6 +380,9 @@
    if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
       goto cleanup_winsock;
 
+   /* start watching for address changes *before* we initialise, to minimise races */
+   InterfaceMonitorInit(&monitor);
+
    /* initialize for the first time */
    SLPDPropertyReinit();  /*So we get any property-related log messages*/
    if (SLPDDatabaseInit(G_SlpdCommandLine.regfile) 
@@ -243,6 +402,7 @@
    /* main loop */
    SLPDLog("Startup complete entering main run loop ...\n\n");
    G_SIGTERM   = 0;
+   G_SIGHUP = 0;
    time(&curtime);
    alarmtime = curtime + 2;  /*Start with a shorter time so SAs register with us quickly on startup*/
    while (G_SIGTERM == 0)
@@ -273,13 +433,20 @@
          SLPDOutgoingRetry(time(0) - curtime);
 
       /* handle signals */
-      HANDLE_SIGNAL:
+HANDLE_SIGNAL:
       time(&curtime);
       if (curtime >= alarmtime)
       {
          HandleSigAlrm();
          alarmtime = curtime + SLPD_AGE_INTERVAL;
       }
+
+      /* check for interface changes */
+      if (InterfacesChanged(&monitor) || G_SIGHUP)
+      {
+         G_SIGHUP = 0;
+         HandleSigHup();
+      }
    }
 
    /* Got SIGTERM */
@@ -287,7 +454,8 @@
 
 cleanup_winsock:
 
-   WSACleanup();     
+   InterfaceMonitorDeinit(&monitor);
+   WSACleanup();
 } 
 
 /** Handles console control events.
@@ -322,13 +490,16 @@
    switch(dwCtrlCode)
    {
       case SERVICE_CONTROL_STOP: 
-         ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0); 
          ServiceStop(); 
          return; 
 
       case SERVICE_CONTROL_INTERROGATE: 
-         break; 
+         break;
 
+      case SERVICE_CONTROL_PARAMCHANGE:
+         G_SIGHUP = 1;
+         break;
+
       default: 
          break; 
    } 
@@ -360,7 +531,7 @@
 
    /* try to report the stopped status to the service control manager. */
    if (sshStatusHandle)
-      ReportStatusToSCMgr(SERVICE_STOPPED, 0, 0);
+      ReportStatusToSCMgr(SERVICE_STOPPED, G_SIGTERM ? NO_ERROR : 1, 0);
 } 
 
 /** Install the service.
diff -ur a/slpd/slpd_incoming.c b/slpd/slpd_incoming.c
--- a/slpd/slpd_incoming.c
+++ b/slpd/slpd_incoming.c
@@ -618,12 +618,58 @@
    return 0;
 }
 
+/** Find an existing listening socket
+ *
+ * @param[in] sockaddr - The address of the interface to listen to.
+ *
+ * @return The existing listening socket on success, or NULL on failure.
+ *
+ * @internal
+ */
+static SLPDSocket * FindListeningSocket(struct sockaddr_storage * sockaddr)
+{
+   SLPDSocket * sock = (SLPDSocket *) G_IncomingSocketList.head;
+
+   while (sock)
+   {
+      if (sock->state == SOCKET_LISTEN
+          && SLPNetCompareAddrs(&(sock->localaddr), sockaddr) == 0)
+         break;
+      sock = (SLPDSocket *) sock->listitem.next;
+   }
+
+   return sock;
+}
+
 /** Initialize incoming socket list for all network interfaces.
  *
  * @return Zero on success, or a non-zero value on failure.
  */
 int SLPDIncomingInit(void)
 {
+#if 1
+   /* 
+    * do not remove ipv6 multicast service sockets previously
+    * created from database init (slp.reg)
+    */
+#else
+   /*------------------------------------------------------------*/
+   /* First, remove all of the sockets that might be in the list */
+   /*------------------------------------------------------------*/
+   while (G_IncomingSocketList.count)
+      SLPDSocketFree((SLPDSocket *)
+            SLPListUnlink(&G_IncomingSocketList, G_IncomingSocketList.head));
+#endif
+
+   return SLPDIncomingReinit();
+}
+
+/** Reinitialize incoming socket list for all network interfaces.
+ *
+ * @return Zero on success, or a non-zero value on failure.
+ */
+int SLPDIncomingReinit(void)
+{
    struct sockaddr_storage myaddr;
    struct sockaddr_storage mcast4addr;
 #if defined(ENABLE_SLPv1)
@@ -642,21 +688,6 @@
    SLPIfaceInfo ifaces;
    int i;
 
-#if 1
-   /* 
-    * do not remove ipv6 multicast service sockets previously
-    * created from database init (slp.reg)
-    */
-#else
-   /*------------------------------------------------------------*/
-   /* First, remove all of the sockets that might be in the list */
-   /*------------------------------------------------------------*/
-   while (G_IncomingSocketList.count)
-      SLPDSocketFree((SLPDSocket *)
-            SLPListUnlink(&G_IncomingSocketList, G_IncomingSocketList.head));
-#endif
-
-
    /*---------------------------------------------------------*/
    /* set up addresses to use for ipv4 loopback and multicast */
    /*---------------------------------------------------------*/
@@ -670,7 +701,6 @@
       tmpaddr = SLPv1_DA_MCAST_ADDRESS;
       SLPNetSetAddr(&v1mcast4addr, AF_INET, G_SlpdProperty.port, &tmpaddr);
 #endif
-
    }
 
    /*-------------------------------------------------------*/
@@ -694,53 +724,69 @@
    /*--------------------------------------------------------------------*/
    if (SLPNetIsIPV4())
    {
-      sock = SLPDSocketCreateListen(&lo4addr);
+      sock = FindListeningSocket(&lo4addr);
       if (sock)
       {
-         SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
-         SLPDLog("Listening on loopback TCP...\n");
+         SLPDLog("Already listening on IPv4 loopback.\n");
       }
       else
       {
-         SLPDLog("NETWORK_ERROR - Could not listen for TCP on IPv4 loopback\n");
-         SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with TCP\n");
+         sock = SLPDSocketCreateListen(&lo4addr);
+         if (sock)
+         {
+            SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
+            SLPDLog("Listening on loopback TCP...\n");
+         }
+         else
+         {
+            SLPDLog("NETWORK_ERROR - Could not listen for TCP on IPv4 loopback\n");
+            SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with TCP\n");
+         }
+         sock = SLPDSocketCreateBoundDatagram(&lo4addr, &lo4addr, DATAGRAM_UNICAST);
+         if (sock)
+         {
+            SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
+            SLPDLog("Listening on loopback UDP...\n");
+         }
+         else
+         {
+            SLPDLog("NETWORK_ERROR - Could not listen for UDP on IPv4 loopback\n");
+            SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with UDP\n");
+         }
       }
-      sock = SLPDSocketCreateBoundDatagram(&lo4addr, &lo4addr, DATAGRAM_UNICAST);
-      if (sock)
-      {
-         SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
-         SLPDLog("Listening on loopback UDP...\n");
-      }
-      else
-      {
-         SLPDLog("NETWORK_ERROR - Could not listen for UDP on IPv4 loopback\n");
-         SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with UDP\n");
-      }
    }
    if (SLPNetIsIPV6())
    {
-      sock = SLPDSocketCreateListen(&lo6addr);
+      sock = FindListeningSocket(&lo6addr);
       if (sock)
       {
-         SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
-         SLPDLog("Listening on IPv6 loopback TCP...\n");
+         SLPDLog("Already listening on IPv6 loopback.\n");
       }
       else
       {
-         SLPDLog("NETWORK_ERROR - Could not listen on TCP IPv6 loopback\n");
-         SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with TCP\n");
+         sock = SLPDSocketCreateListen(&lo6addr);
+         if (sock)
+         {
+            SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
+            SLPDLog("Listening on IPv6 loopback TCP...\n");
+         }
+         else
+         {
+            SLPDLog("NETWORK_ERROR - Could not listen on TCP IPv6 loopback\n");
+            SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with TCP\n");
+         }
+         sock = SLPDSocketCreateBoundDatagram(&lo6addr, &lo6addr, DATAGRAM_UNICAST);
+         if (sock)
+         {
+            SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
+            SLPDLog("Listening on IPv6 loopback UDP...\n");
+         }
+         else
+         {
+            SLPDLog("NETWORK_ERROR - Could not listen on UDP IPv6 loopback\n");
+            SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with UDP\n");
+         }
       }
-      sock = SLPDSocketCreateBoundDatagram(&lo6addr, &lo6addr, DATAGRAM_UNICAST);
-      if (sock)
-      {
-         SLPListLinkTail(&G_IncomingSocketList, (SLPListItem *) sock);
-         SLPDLog("Listening on IPv6 loopback UDP...\n");
-      }
-      else
-      {
-         SLPDLog("NETWORK_ERROR - Could not listen on UDP IPv6 loopback\n");
-         SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available with UDP\n");
-      }
    }
 
    /*---------------------------------------------------------------------*/
@@ -762,6 +808,17 @@
       memcpy(&myaddr, &ifaces.iface_addr[i], sizeof(struct sockaddr_storage));
 
       /*------------------------------------------------*/
+      /* Check for existing listening socket            */
+      /*------------------------------------------------*/
+      if (FindListeningSocket(&myaddr))
+      {
+         SLPDLog("Already listening on %s.\n",
+               SLPNetSockAddrStorageToString(&myaddr, addr_str,
+                     sizeof(addr_str)));
+         continue;
+      }
+
+      /*------------------------------------------------*/
       /* Create TCP_LISTEN that will handle unicast TCP */
       /*------------------------------------------------*/
       sock = SLPDSocketCreateListen(&myaddr);
diff -ur a/slpd/slpd_incoming.h b/slpd/slpd_incoming.h
--- a/slpd_incoming.h
+++ b/slpd/slpd_incoming.h
@@ -61,6 +61,7 @@
 void SLPDIncomingHandler(int * fdcount, fd_set * readfds, fd_set * writefds);
 int SLPDIncomingInit(void);
 int SLPDIncomingDeinit(void);
+int SLPDIncomingReinit(void);
 
 #ifdef DEBUG
 void SLPDIncomingSocketDump(void);
