]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Sec 3384] NTP-01-009 NTP: Privileged execution of User Library code (Pentest report...
authorJuergen Perlinger <perlinger@ntp.org>
Sun, 5 Mar 2017 13:56:43 +0000 (14:56 +0100)
committerJuergen Perlinger <perlinger@ntp.org>
Sun, 5 Mar 2017 13:56:43 +0000 (14:56 +0100)
 - load PPS providers from registry
 - disable PPSAPI_HACK envvar
 - major cleanup of 'instsrv'

bk: 58bc191bDZd_XnGYlOxrP6OMXLbkOw

ChangeLog
include/ntp_stdlib.h
include/ntpd.h
ntpd/ntp_config.c
ntpd/ntp_proto.c
ports/winnt/include/ntp_iocpltypes.h
ports/winnt/instsrv/instsrv.c
ports/winnt/ntpd/nt_ppsimpl.c
ports/winnt/ntpd/ntp_iocompletionport.c

index aef57d7f71aac4965e2a4ee313e5cb8f66bb0062..b4827c2c4dd17f5d37b408f9504a5bc60fa2a622 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+---
+* [Sec 3384] NTP-01-009: Privileged execution of User Library code
+  (Pentest report 01.2017) <perlinger@ntp.org>
+
 ---
 (4.2.8p10)
 
index 3213900ea73713f40881956d38811f546c6d2ac3..a4e857425ff1dd9297a1c00f113a60c9318f0042 100644 (file)
@@ -131,9 +131,13 @@ extern     void *  oreallocarrayxz (void *optr, size_t nmemb, size_t size,
                                          __FILE__, __LINE__)
 #define        erealloc_zero(p, n, o)  ereallocz((p), (n), (o), TRUE, \
                                          __FILE__, __LINE__)
-#define ereallocarray(p, n, s) oreallocarray((p), (n), (s), \
+#define ereallocarray(p, n, s) oreallocarrayxz((p), (n), (s), 0, \
                                          __FILE__, __LINE__)
-#define eallocarray(n, s)      oreallocarray(NULL, (n), (s), \
+#define eallocarray(n, s)      oreallocarrayxz(NULL, (n), (s), 0, \
+                                         __FILE__, __LINE__)
+#define ereallocarrayxz(p, n, s, x)    oreallocarrayxz((p), (n), (s), (x), \
+                                         __FILE__, __LINE__)
+#define eallocarrayxz(n, s, x) oreallocarrayxz(NULL, (n), (s), (x), \
                                          __FILE__, __LINE__)
 extern char *  estrdup_impl(const char *, const char *, int);
 #define        estrdup(s) estrdup_impl((s), __FILE__, __LINE__)
index 0026a0fffab706d9fc7da792d8acc31a5f2ed24c..f944235cd6d20f8a7e68eed7ea2224d0c0d84995 100644 (file)
@@ -495,7 +495,7 @@ extern int  sys_minsane;            /* minimum candidates */
 extern int     sys_floor;              /* cluster stratum floor */
 extern int     sys_ceiling;            /* cluster stratum ceiling */
 extern u_char  sys_ttl[MAX_TTL];       /* ttl mapping vector */
-extern int     sys_ttlmax;             /* max ttl mapping vector index */
+extern u_int   sys_ttlmax;             /* max ttl mapping vector index */
 
 /*
  * Statistics counters
index 1355e7343baef288abc5c8e024aa376733d75677..428ab9f467d6791d1b1e4ade64016887da49e1ca 100644 (file)
@@ -3262,7 +3262,7 @@ config_ttl(
                                "ttl: Number of TTL entries exceeds %zu. Ignoring TTL %d...",
                                COUNTOF(sys_ttl), curr_ttl->i);
        }
-       sys_ttlmax = i - 1;
+       sys_ttlmax = (i) ? (i - 1) : 0;
 }
 #endif /* !SIM */
 
index 94411cf8f3b4f4502672eeb78ca8848e1b1a83d4..c5d7cc6db4a865764aab004c43126c373a97d071 100644 (file)
@@ -147,7 +147,7 @@ int sys_cohort = 0;         /* cohort switch */
 int    sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */
 int    sys_orphwait = NTP_ORPHWAIT; /* orphan wait */
 int    sys_beacon = BEACON;    /* manycast beacon interval */
-int    sys_ttlmax;             /* max ttl mapping vector index */
+u_int  sys_ttlmax;             /* max ttl mapping vector index */
 u_char sys_ttl[MAX_TTL];       /* ttl mapping vector */
 
 /*
@@ -401,7 +401,7 @@ transmit(
                        peer_xmit(peer);
                } else if (   sys_survivors < sys_minclock
                           || peer_associations < sys_maxclock) {
-                       if (peer->ttl < (u_int32)sys_ttlmax)
+                       if (peer->ttl < sys_ttlmax)
                                peer->ttl++;
                        peer_xmit(peer);
                }
index b3e320b4d125dbc3e177ad94f5f5482f256eb63b..5022378929ca09b0a3be3b486f83035601f471d5 100644 (file)
@@ -87,6 +87,7 @@ typedef struct DeviceContext DevCtx_t;
 struct DeviceContext {
        volatile u_long ref_count;
        volatile u_long cov_count;
+       int             pps_active;
        PPSData_t       pps_data;
        PPSDataEx_t     pps_buff[PPS_QUEUE_LEN];
 };
index 2c93e257e779cce48a814b90319d912883715baf..3487c218ed60ded55a69b19388f324df9ce044be 100644 (file)
  *
  */
 #ifndef __RPCASYNC_H__
-#define __RPCASYNC_H__ /* Skip asynch rpc inclusion */
+#define __RPCASYNC_H__  /* Skip asynch rpc inclusion */
 #endif
 
 #include <windows.h>
 #include <stdio.h>
 
 #define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
-    __FILE__, GetLastError(), api, __LINE__);
+                         __FILE__, GetLastError(), api, __LINE__);
 
 #define MSG_FOR_ACCESS_DENIED "You aren't authorized to do this - please see your system Administrator"
 #define MSG_1_FOR_BAD_PATH "The fully qualified path name to the .exe must be given, and"
 #define MSG_2_FOR_BAD_PATH "  the drive letter must be for a fixed disk (e.g., not a net drive)"
 
+/* Building an initialised REG_MULTISZ needs a bit of care: The array must be big enough
+** to hold the closing double-NUL and should have *exactly* the required size. Since the
+** dependencies are fixed here, we take exactly the required 11 bytes:
+*/
+static const char s_acSvcDeps[11] = "TcpIp\0Afd\0\0";
+/* likewise we do with the PPAS API list... */
+static const char s_acPpsDlls[30] = "loopback-ppsapi-provider.dll\0\0";
+
 SC_HANDLE schService;
 SC_HANDLE schSCManager;
 int ok2;
 
-VOID DisplayHelp(VOID);
+void DisplayHelp(void);
 
 /* --------------------------------------------------------------------------------------- */
-
-int InstallService(LPCTSTR serviceName, LPCTSTR displayName, LPCTSTR serviceExe)
+static const char *
+getServicePath(
+    const char * const * const argv,
+    const int                  argc) /* MUST be >= 1 !! */
 {
-  LPCTSTR lpszBinaryPathName = serviceExe;
-  TCHAR lpszRootPathName[] ="?:\\";
-
-  if ( (':' != *(lpszBinaryPathName+1)) || ('\\' != *(lpszBinaryPathName+2)) )
-  { printf("\n%s",MSG_1_FOR_BAD_PATH);
-    printf("\n%s\n",MSG_2_FOR_BAD_PATH);
-    return 1;
-  }
-
-  #define DRIVE_TYPE_INDETERMINATE 0
-  #define ROOT_DIR_DOESNT_EXIST    1
-
-  *lpszRootPathName = *(lpszBinaryPathName+0) ;
-
-  switch (  GetDriveType(lpszRootPathName)  )
-  {
-    case DRIVE_FIXED :
-    { // OK
-      break;
+    static const char * const s_chars_to_quote = " []()";
+    size_t      minsize = argc * 3; /* may need separator/NUL + quotes */
+    int         i;
+    const char *execPath = argv[0];
+    char       *cbuf, *cpos;
+    
+    /* if just the executable and no dangerous chars, return the exe path. */
+    if (argc == 1 && !strpbrk(execPath, s_chars_to_quote))
+        return execPath;
+    
+    /* calculate buffer size and get the buffer */
+    for (i = 0; i < argc; ++i)
+        minsize += strlen(argv[i]);
+
+    cpos = cbuf = malloc(minsize);
+    if (NULL == cbuf) {
+        printf("malloc() failed\n");
+        exit(2);
     }
-    case  ROOT_DIR_DOESNT_EXIST :
-    { printf("\n%s",MSG_1_FOR_BAD_PATH);
-      printf("\n  the root directory where the .exe is specified to be must exist, and");
-      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
-      return 1;
+    
+    /* program name must be quoted in all cases */
+    *cpos++ = '"';
+    strcpy(cpos, execPath);
+    cpos += strlen(cpos);
+    *cpos++ = '"';
+    
+    /* append additional args */
+    for (i = 1; i < argc; ++i) {
+        *cpos++ = ' ';
+        if (strpbrk(argv[i], s_chars_to_quote)) { /* needs quotes? */
+            *cpos++ = '"';
+            strcpy(cpos, argv[i]);
+            cpos += strlen(cpos);
+            *cpos++ = '"';
+        } else {
+            strcpy(cpos, argv[i]);
+            cpos += strlen(cpos);
+        }
     }
-    case  DRIVE_TYPE_INDETERMINATE :
-    case  DRIVE_REMOVABLE          :
-    case  DRIVE_REMOTE             :
-    case  DRIVE_CDROM              :
-    case  DRIVE_RAMDISK            :
-    { printf("\n%s",MSG_1_FOR_BAD_PATH);
-      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
-      return 1;
+    *cpos = '\0';
+    return cbuf;
+}
+
+/* --------------------------------------------------------------------------------------- */
+BOOL
+validateExeName(
+        const char *exePath)
+{
+    char rootPath[] = "?:\\";
+    
+    /* check for absolute path */
+    if ((':' != exePath[1]) || ('\\' != exePath[2])) {
+        printf("\n%s", MSG_1_FOR_BAD_PATH);
+        printf("\n%s\n", MSG_2_FOR_BAD_PATH);
+        return 1;
     }
-    default :
-    { printf("\n%s",MSG_1_FOR_BAD_PATH);
-      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
-      return 1;
+
+#define DRIVE_TYPE_INDETERMINATE 0
+#define ROOT_DIR_DOESNT_EXIST    1
+    
+    /* check drive type -- must be local HDD! */
+    rootPath[0] = exePath[0];
+    switch (GetDriveTypeA(rootPath))
+    {
+    case DRIVE_FIXED:
+        // OK
+        break;
+        
+    case  ROOT_DIR_DOESNT_EXIST:
+        printf("\n%s", MSG_1_FOR_BAD_PATH);
+        printf("\n  the root directory where the .exe is specified to be must exist, and");
+        printf("\n%s\n", MSG_2_FOR_BAD_PATH);
+        return 1;
+        
+    case  DRIVE_TYPE_INDETERMINATE:
+    case  DRIVE_REMOVABLE:
+    case  DRIVE_REMOTE:
+    case  DRIVE_CDROM:
+    case  DRIVE_RAMDISK:
+        printf("\n%s", MSG_1_FOR_BAD_PATH);
+        printf("\n%s\n", MSG_2_FOR_BAD_PATH);
+        return 1;
+        
+    default:
+        printf("\n%s", MSG_1_FOR_BAD_PATH);
+        printf("\n%s\n", MSG_2_FOR_BAD_PATH);
+        return 1;
     }
-  }
-
-  if (INVALID_HANDLE_VALUE == CreateFile(lpszBinaryPathName,
-                                         GENERIC_READ,
-                                         FILE_SHARE_READ,
-                                         NULL,
-                                         OPEN_EXISTING,
-                                         FILE_ATTRIBUTE_NORMAL,
-                                         NULL))
-  { 
-    printf("\n%s",MSG_1_FOR_BAD_PATH);
-    printf("\n  the file must exist, and");
-    printf("\n%s\n",MSG_2_FOR_BAD_PATH);
-    return 1;
-  }
+    
+    /* check if file exists. We just drop the handle. This is a one-shot program! */
+    if (INVALID_HANDLE_VALUE == CreateFileA(
+            exePath, GENERIC_READ, FILE_SHARE_READ,
+            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
+    {
+        printf("\n%s", MSG_1_FOR_BAD_PATH);
+        printf("\n  the file must exist, and");
+        printf("\n%s\n", MSG_2_FOR_BAD_PATH);
+        return 1;
+    }
+    
+    return 0;
+}
+/* --------------------------------------------------------------------------------------- */
 
-  schService = CreateService(
+int
+InstallService(
+    LPCSTR serviceName,
+    LPCSTR displayName,
+    LPCSTR serviceExe ,
+    LPCSTR servDepends)
+{
+
+    /* create the service now. */
+    schService = CreateServiceA(
         schSCManager,               // SCManager database
         serviceName,                // name of service
         displayName,                // name to display
@@ -91,368 +158,332 @@ int InstallService(LPCTSTR serviceName, LPCTSTR displayName, LPCTSTR serviceExe)
         SERVICE_WIN32_OWN_PROCESS,  // service type
         SERVICE_AUTO_START,         // start type
         SERVICE_ERROR_NORMAL,       // error control type
-        lpszBinaryPathName,         // service's binary
+        serviceExe,                 // service's binary
         NULL,                       // no load ordering group
         NULL,                       // no tag identifier
-        NULL,                       // no dependencies
+        servDepends,                // possible dependencies
         NULL,                       // Local System account
         NULL);                      // null password
 
-  if (NULL == schService)
-  { switch (GetLastError())
-    {
-      case ERROR_ACCESS_DENIED :
-      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
-        break;
-      }
-      case ERROR_SERVICE_EXISTS :
-      { printf("\nThe %s service is already installed",serviceName);
-        printf("\nRemove it first if you need to re-install a new version\n");
-        break;
-      }
-      default :
-      { PERR("CreateService");
-      }
+    if (NULL == schService) {
+        switch (GetLastError())
+        {
+        case ERROR_ACCESS_DENIED :
+            printf("\n%s",MSG_FOR_ACCESS_DENIED);
+            break;
+        
+        case ERROR_SERVICE_EXISTS :
+            printf("\nThe %s service is already installed",serviceName);
+            printf("\nRemove it first if you need to re-install a new version\n");
+            break;
+
+        default :
+            PERR("CreateService");
+            break;
+        }
+        return 1;
     }
-    return 1;
-  }
-  else
 
-  CloseServiceHandle(schService);
-  return 0;
+    CloseServiceHandle(schService);
+    return 0;
 }
 
 /* --------------------------------------------------------------------------------------- */
 
-int RemoveService(LPCTSTR serviceName)
+int
+RemoveService(
+    LPCSTR serviceName)
 {
-  {
-    #define                                     SZ_ENUM_BUF 4096
-    ENUM_SERVICE_STATUS        essServiceStatus[SZ_ENUM_BUF];
-    DWORD   dwBufSize = sizeof(essServiceStatus);
-    DWORD   dwBytesNeeded      = 0;
-    DWORD   dwServicesReturned = 0;
-    DWORD   dwResumeHandle     = 0;
-    DWORD   dwI                = 0;
-    BOOLEAN bFound = FALSE;
-
-    if (!EnumServicesStatus(schSCManager,
-                            SERVICE_WIN32,
-                            SERVICE_ACTIVE,
-                            (LPENUM_SERVICE_STATUS)&essServiceStatus,
-                            dwBufSize,
-                            &dwBytesNeeded,
-                            &dwServicesReturned,
-                            &dwResumeHandle))
-    { switch (GetLastError())
-      {
-        case ERROR_ACCESS_DENIED :
-        { printf("\n%s",MSG_FOR_ACCESS_DENIED);
-          break;
+    {
+#       define SZ_ENUM_BUF 4096
+        ENUM_SERVICE_STATUS essServiceStatus[SZ_ENUM_BUF];
+        DWORD   dwBufSize = sizeof(essServiceStatus);
+        DWORD   dwBytesNeeded      = 0;
+        DWORD   dwServicesReturned = 0;
+        DWORD   dwResumeHandle     = 0;
+        DWORD   dwI                = 0;
+        BOOLEAN bFound = FALSE;
+
+        if (!EnumServicesStatusA(schSCManager, SERVICE_WIN32, SERVICE_ACTIVE,
+                                (LPENUM_SERVICE_STATUS)&essServiceStatus,
+                                dwBufSize, &dwBytesNeeded, &dwServicesReturned,
+                                &dwResumeHandle))
+        {
+            switch (GetLastError())
+            {
+            case ERROR_ACCESS_DENIED :
+                printf("\n%s", MSG_FOR_ACCESS_DENIED);
+                break;
+
+            default :
+                PERR("EnumServicesStatus");
+                break;
+            }
+            return 1;
         }
-        default :
-        { PERR("EnumServicesStatus");
+
+        for (dwI = 0; dwI < dwServicesReturned; ++dwI) {
+            if(0 == _stricmp(essServiceStatus[dwI].lpServiceName, serviceName)) {
+                bFound = TRUE;
+                break;
+            }
+        }
+        
+        if (bFound) {
+            printf("\nThe %s service cannot be removed until it has been stopped.", serviceName);
+            printf("\nTo stop the %s service, use the Stop button in the Control" , serviceName);
+            printf("\n  Panel Services applet\n");
+            return 1;
         }
-      }
-      return 1;
     }
+    
+    schService = OpenServiceA(schSCManager, serviceName, SERVICE_ALL_ACCESS);
+    if (NULL == schService) {
+        switch (GetLastError())
+        {
+        case ERROR_ACCESS_DENIED :
+            printf("\n%s", MSG_FOR_ACCESS_DENIED);
+            break;
 
-    for (dwI=0; dwI<dwServicesReturned; dwI++)
-    { if(0 == _stricmp(essServiceStatus[dwI].lpServiceName,serviceName))
-      { bFound = TRUE;
-        break;
-      }
-    }
+        case ERROR_SERVICE_DOES_NOT_EXIST :
+            printf("\nThe %s service is not installed, so cannot be removed\n", serviceName);
+            break;
 
-    if (bFound)
-    { printf("\nThe %s service cannot be removed until it has been stopped.",serviceName);
-      printf("\nTo stop the %s service, use the Stop button in the Control",serviceName);
-      printf("\n  Panel Services applet\n");
-      return 1;
+        default :
+            PERR("OpenService");
+            break;
+        }
+        return 1;
+    }
+    
+    if (DeleteService(schService)) {
+        printf("\nDelete of Service \"Network Time Protocol\" was SUCCESSFUL\n");
+        return 0;
     }
-  }
 
-  schService = OpenService(schSCManager,
-                           serviceName,
-                           SERVICE_ALL_ACCESS);
-  if (NULL == schService)
-  { switch (GetLastError())
+    switch (GetLastError())
     {
-      case ERROR_ACCESS_DENIED :
-      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
+    case ERROR_ACCESS_DENIED :
+        printf("\n%s", MSG_FOR_ACCESS_DENIED);
         break;
-      }
-      case ERROR_SERVICE_DOES_NOT_EXIST :
-      { printf("\nThe %s service is not installed, so cannot be removed\n",serviceName);
+
+    default :
+        PERR("DeleteService");
         break;
-      }
-      default :
-      { PERR("OpenService");
-      }
     }
+    
     return 1;
-  }
-
-  if (DeleteService(schService))
-  { printf("\nDelete of Service \"Network Time Protocol\" was SUCCESSFUL\n");
-   return 0;
-  }
-  else
-  { switch (GetLastError())
-    {
-      case ERROR_ACCESS_DENIED :
-      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
-        break;
-      }
-      default :
-      { PERR("DeleteService");
-      }
-    }
-   return 1;
-  }
 }
 
 /* --------------------------------------------------------------------------------------- */
 
-int addSourceToRegistry(const char * pszAppname, const char * pszMsgDLL)
+int
+addSourceToRegistry(
+    const char * pszAppname,
+    const char * pszMsgDLL )
 {
-  HKEY  hk;                      /* registry key handle */
-  DWORD dwData;
-  BOOL  bSuccess;
-  char  regarray[200];
-  int   rc;
-
-  /* When an application uses the RegisterEventSource or OpenEventLog
-     function to get a handle of an event log, the event loggging service
-     searches for the specified source name in the registry. You can add a
-     new source name to the registry by opening a new registry subkey
-     under the Application key and adding registry values to the new
-     subkey. */
-
-  rc = snprintf(regarray, sizeof(regarray), "%s%s",
-                "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\",
-                pszAppname);
-  if (rc < 0 || rc >= sizeof(regarray))
-  {
-    fputs("addSourceToRegistry: buffer overrun(app name)", stderr);
-    return 1;
-  }
-  /* Create a new key for our application */
-  bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, regarray, &hk);
-  if(bSuccess != ERROR_SUCCESS)
-  {
-    PERR("RegCreateKey");
-    return 1;
-  }
+    HKEY  hk;                      /* registry key handle */
+    DWORD dwData;
+    BOOL  bSuccess;
+    char  regarray[200];
+    int   rc;
+
+    /* When an application uses the RegisterEventSource or OpenEventLog function to get a
+       handle of an event log, the event loggging service searches for the specified
+       source name in the registry. You can add a new source name to the registry by
+       opening a new registry subkey under the Application key and adding registry values
+       to the new subkey.
+    */
+
+    rc = snprintf(regarray, sizeof(regarray), "%s%s",
+                  "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\",
+                  pszAppname);
+    if (rc < 0 || rc >= sizeof(regarray)) {
+        fputs("addSourceToRegistry: buffer overrun(app name)", stderr);
+        return 1;
+    }
+    /* Create a new key for our application */
+    bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, regarray, &hk);
+    if(bSuccess != ERROR_SUCCESS) {
+        PERR("RegCreateKey");
+        return 1;
+    }
     
-  /* Add the Event-ID message-file name to the subkey. */
-  bSuccess = RegSetValueExA(hk,         /* subkey handle         */
-      "EventMessageFile",               /* value name            */
-      0,                                /* must be zero          */
-      REG_EXPAND_SZ,                    /* value type            */
-      (LPBYTE)pszMsgDLL,                /* address of value data */
-      (DWORD)(strlen(pszMsgDLL) + 1));  /* length of value data  */
-  if(bSuccess != ERROR_SUCCESS)
-  {
-    PERR("RegSetValueEx");
-    return 1;
-  }
+    /* Add the Event-ID message-file name to the subkey. */
+    bSuccess = RegSetValueExA(hk,                               /* subkey handle         */
+                              "EventMessageFile",               /* value name            */
+                              0,                                /* must be zero          */
+                              REG_EXPAND_SZ,                    /* value type            */
+                              (LPBYTE)pszMsgDLL,                /* address of value data */
+                              (DWORD)(strlen(pszMsgDLL) + 1));  /* length of value data  */
+    if(bSuccess != ERROR_SUCCESS) {
+        PERR("RegSetValueEx");
+        return 1;
+    }
   
-  /* Set the supported types flags and addit to the subkey. */
-  dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
-      EVENTLOG_INFORMATION_TYPE;
-  bSuccess = RegSetValueExA(hk,  /* subkey handle                */
-      "TypesSupported",         /* value name                   */
-      0,                        /* must be zero                 */
-      REG_DWORD,                /* value type                   */
-      (LPBYTE) &dwData,         /* address of value data        */
-      sizeof(DWORD));           /* length of value data         */
-  if(bSuccess != ERROR_SUCCESS)
-  {
-    PERR("RegSetValueEx");
-    return 1;
-  }
-  RegCloseKey(hk);
-  return 0;
+    /* Set the supported types flags and addit to the subkey. */
+    dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
+    bSuccess = RegSetValueExA(hk,                       /* subkey handle                */
+                              "TypesSupported",         /* value name                   */
+                              0,                        /* must be zero                 */
+                              REG_DWORD,                /* value type                   */
+                              (LPBYTE) &dwData,         /* address of value data        */
+                              sizeof(DWORD));           /* length of value data         */
+    if(bSuccess != ERROR_SUCCESS) {
+        PERR("RegSetValueEx");
+        return 1;
+    }
+    
+    RegCloseKey(hk);
+    return 0;
 }
 
 /* --------------------------------------------------------------------------------------- */
 
-int addKeysToRegistry()
+int
+addKeysToRegistry(void)
 {
-  HKEY hk;                      /* registry key handle */
-  BOOL bSuccess;
-
-  /* Building an initialised REG_MULTISZ needs a bit of care: The array must be big enough
-  ** to hold the closing double-NUL and should have *exactly* the required size. Since the
-  ** dependencies are fixed here, we take exactly the required 11 bytes:
-  */
-  static const char s_acSvcDeps[11] = "TcpIp\0Afd\0\0";
-  /* now add the depends on service key */
+    HKEY hk;                      /* registry key handle */
+    BOOL bSuccess;
+
+    /* now add the depends on service key */
  
-  /* Create a new key for our application */
-  bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE,
-      "SYSTEM\\CurrentControlSet\\Services\\NTP", &hk);
-  if(bSuccess != ERROR_SUCCESS) {
-    PERR("RegCreateKey");
-    return 1;
-  }
-
-  bSuccess = RegSetValueExA(hk,     /* subkey handle         */
-      "DependOnService",            /* value name            */
-      0,                            /* must be zero          */
-      REG_MULTI_SZ,                 /* value type            */
-      (LPBYTE)s_acSvcDeps,          /* address of value data */
-      (DWORD)sizeof(s_acSvcDeps));  /* length of value data  */
-  if(bSuccess != ERROR_SUCCESS)
-  {
-    PERR("RegSetValueEx");
-    return 1;
-  }
+    /* Create a new key for our application */
+    bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE,
+                             "SYSTEM\\CurrentControlSet\\Services\\NTP", &hk);
+    if(bSuccess != ERROR_SUCCESS) {
+        PERR("RegCreateKey");
+        return 1;
+    }
+
+    bSuccess = RegSetValueExA(hk,                           /* subkey handle         */
+                              "PPSProviders",               /* value name            */
+                              0,                            /* must be zero          */
+                              REG_MULTI_SZ,                 /* value type            */
+                              (LPBYTE)s_acPpsDlls,          /* address of value data */
+                              (DWORD)sizeof(s_acPpsDlls));  /* length of value data  */
+    if (bSuccess != ERROR_SUCCESS) {
+        PERR("RegSetValueEx");
+        return 1;
+    }
 
-  RegCloseKey(hk);
-  return 0;
+    RegCloseKey(hk);
+    return 0;
 }
 
 /* --------------------------------------------------------------------------------------- */
 
 int main(int argc, char *argv[])
 {
-  #define           SZ_NAME_BUF  270  // 256 is max, add a little
-  UCHAR   ucNameBuf[SZ_NAME_BUF] = "NTP";
-  LPTSTR  lpszServName = (LPTSTR)&ucNameBuf;
-
-  UCHAR   ucDNameBuf[SZ_NAME_BUF] = "Network Time Protocol";
-  LPTSTR  lpszDispName = (LPTSTR)&ucDNameBuf;
+    static const char * const szServiceName = "NTP";
+    static const char * const szDisplayName = "Network Time Protocol";
 
+    BOOL  bRemovingService = FALSE;
+    char *p;
+    int   ok = 0;  
+  
+    // check if Win32s, if so, display notice and terminate
+    if (GetVersion() & 0x80000000) {
+        MessageBoxA(NULL,
+                    "This application cannot run on Windows 3.1.\n"
+                    "This application will now terminate.",
+                    "NAMED",
+                    MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
+        return 1;
+    }
 
-  UCHAR   ucExeNBuf[SZ_NAME_BUF] = "";
-  LPTSTR  lpszExeName  = (LPTSTR)&ucExeNBuf;
+    if (argc >= 2)
+            bRemovingService = (!stricmp(argv[1], "remove"));
 
-  BOOL    bRemovingService = FALSE;
-  char *p;
+    if ((bRemovingService && argc != 2) || (!bRemovingService && argc < 2)) {
+        DisplayHelp();
+        return 1;
+    }
 
-  int ok = 0;  
-  
-  // check if Win32s, if so, display notice and terminate
-      if( GetVersion() & 0x80000000 )
-      {
-        MessageBox( NULL,
-           "This application cannot run on Windows 3.1.\n"
-           "This application will now terminate.",
-           "NAMED",
-           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
-        return( 1 );
-      }
-  if (argc == 2)
-     bRemovingService = (!stricmp(argv[1], "remove"));
-
-  if(!bRemovingService)
-   {
+    if(!bRemovingService) {
+        p = argv[1];
+        if (('/' == *p) || ('-' == *p) || validateExeName(p)) {
+            DisplayHelp();
+            return 1;
+        }
+    }
 
-  
-  if (argc != 2)
-  {
-    DisplayHelp();
-    return(1);
-  }
-
-  p=argv[1];
-  if (    ('/' == *p)
-       || ('-' == *p) )
-  {
-    DisplayHelp();
-    return(1);
-  }
-        
-  
-   }
 
-  if (strlen(argv[1]) > 256)
-    {
-      printf("\nThe service name cannot be longer than 256 characters\n");
-      return(1);
+    if (strlen(argv[1]) > 256) {
+        printf("\nThe service name cannot be longer than 256 characters\n");
+        return 1;
     }
 
+    schSCManager = OpenSCManagerA(
+        NULL,                   // machine (NULL == local)
+        NULL,                   // database (NULL == default)
+        SC_MANAGER_ALL_ACCESS); // access required
 
+    if (NULL == schSCManager) {
+        switch (GetLastError())
+        {
+        case ERROR_ACCESS_DENIED :
+            printf("\n%s", MSG_FOR_ACCESS_DENIED);
+            break;
 
-  bRemovingService = (!stricmp(argv[1], "remove"));
-  schSCManager = OpenSCManager(
-                      NULL,                   // machine (NULL == local)
-                      NULL,                   // database (NULL == default)
-                      SC_MANAGER_ALL_ACCESS); // access required
-
-  if (NULL == schSCManager)
-  { switch (GetLastError())
-    {
-      case ERROR_ACCESS_DENIED :
-      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
-        break;
-      }
-      default :
-      { PERR("OpenSCManager");
-      }
+        default :
+            PERR("OpenSCManager");
+            break;
+        }
+        return 0;
     }
-    return (0);
-  }
    
-  if (bRemovingService)
-  {
-   ok = RemoveService(lpszServName);
-  }
-  else
-  {
-   /* get the exe name */
-   strcpy(lpszExeName,argv[1]);
-   ok = InstallService(lpszServName, lpszDispName, lpszExeName);
-  }
-
-  CloseServiceHandle(schSCManager);
-
-  if (!bRemovingService)
-    {
-  if (ok == 0)
-   { /* Set the Event-ID message-file name. */
-    ok = addSourceToRegistry("NTP", lpszExeName);
-    if (ok == 0)
-      ok = addKeysToRegistry();
-    else return ok;
-
-    if (ok == 0)
-    {
-      printf("\nThe \"Network Time Protocol\" service was successfully created.\n");
-      printf("\nDon't forget!!! You must now go to the Control Panel and");
-      printf("\n  use the Services applet to change the account name and");
-      printf("\n  password that the NTP Service will use when");
-      printf("\n  it starts.");
-      printf("\nTo do this: use the Startup button in the Services applet,");
-      printf("\n  and (for example) specify the desired account and");
-      printf("\n  correct password.");
-      printf("\nAlso, use the Services applet to ensure this newly installed");
-      printf("\n  service starts automatically on bootup.\n");
-     return 0;
+    if (bRemovingService)
+        ok = RemoveService(szServiceName);
+    else
+        ok = InstallService(szServiceName, szDisplayName,
+                            getServicePath(argv+1, argc-1), s_acSvcDeps);
+
+    CloseServiceHandle(schSCManager);
+
+    if (!bRemovingService) {
+        if (ok == 0)
+            ok = addSourceToRegistry("NTP", argv[1]);/* Set the Event-ID message-file name. */
+        if (ok == 0)
+            ok = addKeysToRegistry(); /* add other stuff */
+
+        if (ok == 0)
+        {
+            static const char s_msg[] =
+                "\nThe \"Network Time Protocol\" service was successfully created.\n"
+                "\nDon't forget!!! You must now go to the Control Panel and"
+                "\n  use the Services applet to change the account name and"
+                "\n  password that the NTP Service will use when"
+                "\n  it starts."
+                "\nTo do this: use the Startup button in the Services applet,"
+                "\n  and (for example) specify the desired account and"
+                "\n  correct password."
+                "\nAlso, use the Services applet to ensure this newly installed"
+                "\n  service starts automatically on bootup.\n";
+            fputs(s_msg, stdout);
+        }
     }
-   }
-  else return ok;
-  }
- return 0;
+    return ok;
 }
 
 /* --------------------------------------------------------------------------------------- */
 
-VOID DisplayHelp(VOID)
+void
+DisplayHelp(void)
 {
-    printf("Installs or removes the NTP service.\n");
-    printf("To install the NTP service,\n");
-    printf("type INSTSRV <path> \n");
-    printf("Where:\n");
-    printf("    path    Absolute path to the NTP service, name.exe.  You must\n");
-    printf("            use a fully qualified path and the drive letter must be for a\n");
-    printf("            fixed, local drive.\n\n");
-    printf("For example, INSTSRV i:\\winnt\\system32\\ntpd.exe\n");
-    printf("To remove the NTP service,\n");
-    printf("type INSTSRV remove \n");
-
+    static const char s_hlpmsg[] =
+        "Installs or removes the NTP service.\n"
+        "To install the NTP service,\n"
+        "type INSTSRV <path> [args]\n"
+        "Where:\n"
+        "    path    Absolute path to the NTP service. (ntpd.exe)  You must\n"
+        "            use a fully qualified path and the drive letter must be for a\n"
+        "            fixed, local drive.\n\n"
+        "    args    Additional command line arguments for the service\n"
+        "For example, INSTSRV i:\\winnt\\system32\\ntpd.exe\n"
+        "To remove the NTP service,\n"
+        "type INSTSRV remove \n";
+    fputs(s_hlpmsg, stdout);
 }
 
 /* EOF */
index d3b3c5daa8e63228c0b4f198bc6fac86a82ddc2f..2a7c0d88f1d94bc641411c2e50c494dd73f2a326 100644 (file)
 #include "timepps.h"
 #include "ntp_stdlib.h"
 #include "lib_strbuf.h"
+#include "ntp_iocpltypes.h"
+#include "ntp_iocplmem.h"
 
+struct InstListNode {
+       struct InstListNode *   next;
+       pps_handle_t            ppsu;
+       DevCtx_t *              devu;
+};
+typedef struct ProvListNode ProvListNode_t;
+
+static struct InstListNode *   g_active_units;
 static ppsapi_provider *       g_provider_list;
 static ppsapi_provider *       g_curr_provider;
 
+static void
+ppsu_register(
+       pps_handle_t ppsu,
+       DevCtx_t *   devu)
+{
+       struct InstListNode *   node;
+
+       if (devu && (node = IOCPLPoolAlloc(sizeof(*node), "PPS registration"))) {
+               node->next = g_active_units;
+               node->ppsu = ppsu;
+               node->devu = DevCtxAttach(devu);
+               devu->pps_active = TRUE;
+               g_active_units = node;
+       }
+}
+
+static void
+ppsu_remove(
+       pps_handle_t ppsu)
+{
+       struct InstListNode **  link;
+       struct InstListNode *   node;
+
+       link = &g_active_units;
+       while (NULL != (node = *link)) {
+               if (node->ppsu == ppsu) {
+                       node->devu->pps_active = FALSE;
+                       DevCtxDetach(node->devu);
+                       *link = node->next;
+                       IOCPLPoolFree(node, "PPS registration");
+               } else {
+                       link = &node->next;
+               }
+       }
+}
+
+static HKEY
+myRegOpenKey(
+       const char * szSubKey)
+{
+       static const char * const s_RegKey =
+               "SYSTEM\\CurrentControlSet\\services\\NTP";
+
+       HKEY    hkey1 = NULL;
+       HKEY    hkey2 = NULL;
+       DWORD   rc;
+
+       rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, s_RegKey, 0, KEY_READ, &hkey1);
+       if (ERROR_SUCCESS  != rc)
+               return NULL;
+       if (!(szSubKey && *szSubKey))
+               return hkey1;
+
+       rc = RegOpenKeyExA(hkey1, szSubKey, 0, KEY_READ, &hkey2);
+       RegCloseKey(hkey1);
+       if (ERROR_SUCCESS != rc)
+               return NULL;
+       return hkey2;
+}
+
+static char*
+myRegReadMultiString(
+       HKEY        hKey   ,
+       const char *szValue,
+       DWORD      *pSize  )
+{
+       char *  endp;
+       char *  retv  = NULL;
+       DWORD   rSize = 0, rType = REG_NONE, rc;
+
+       /* take two turns: one to get the size, another one to malloc & read */
+       do {
+               if (rType != REG_NONE) {
+                       retv = malloc(rSize += 2);
+                       if (NULL == retv)
+                               goto fail;
+               }
+               rc = RegQueryValueExA(hKey, szValue, NULL, &rType, retv, &rSize);
+               if (ERROR_SUCCESS != rc || (REG_SZ != rType && REG_MULTI_SZ != rType))
+                       goto fail;      
+       } while (NULL == retv);
+
+       /* trim trailing NULs and ensure two of them */
+       endp = retv + rSize;
+       while (endp != retv && endp[-1])
+               --endp;
+       if (endp != retv) {
+               endp[0] = endp[1] = '\0';
+               if (NULL != pSize)
+                       *pSize = (DWORD)(endp - retv);
+               return retv;
+       }
+fail:
+       free(retv);
+       if (NULL != pSize)
+               *pSize = 0;
+       return NULL;
+}
+
+static DWORD
+myRegReadDWord(
+       HKEY        hKey   ,
+       const char *szValue,
+       DWORD       Default)
+{
+       DWORD   rc, rSize, rType, rValue;
+       
+       rSize = sizeof(rValue);
+       rc = RegQueryValueExA(hKey, szValue, NULL, &rType, (PBYTE)&rValue, &rSize);
+       if (rc != ERROR_SUCCESS || rSize != sizeof(rValue) || rType != REG_DWORD)
+               rValue = Default;
+       return rValue;
+}
+
+
 static pps_handle_t
 internal_create_pps_handle(
        void *  prov_context
@@ -169,6 +294,82 @@ fail:
        return NULL;
 }
 
+static char *
+get_provider_list(void)
+{
+       static char * s_Value = NULL;
+
+       HKEY hKey;
+       DWORD rSize;
+       char *cp, *op;
+
+       if (s_Value != NULL)
+               return (*s_Value) ? s_Value : NULL;
+
+       /*
+       ** try registry first
+       */
+       hKey = myRegOpenKey(NULL);
+       if (NULL == hKey)
+               goto regfail;
+
+       s_Value = myRegReadMultiString(hKey, "PPSProviders", &rSize);
+       if (NULL == s_Value)
+               goto regfail;
+
+       /* make sure we have backslashes in the path */
+       for (cp = s_Value; rSize; --rSize, ++cp)
+               if (*cp == '/')
+                       *cp = '\\';
+regfail:
+       if (NULL != hKey)
+               RegCloseKey(hKey);
+
+       if (s_Value && *s_Value)
+               return s_Value;
+
+       /*
+       ** try environment next.
+       */
+       free(s_Value);
+       s_Value = NULL;
+
+       /* try to get env var */
+       cp = getenv("PPSAPI_DLLS");
+       if (!(cp && *cp))
+               goto envfail;
+
+       /* get size & allocate buffer */
+       rSize = strlen(cp);
+       s_Value = malloc(rSize + 2);
+       if (s_Value == NULL)
+               goto envfail;
+
+       /* copy string value and convert to MULTI_SZ.
+        * Converts sequences of ';' to a single NUL byte, and rplaces
+        * slashes by backslashes on the fly.
+        */
+       for (op = s_Value; *cp; ++cp) {
+               if (*cp == '/') {
+                       *op++ = '\\';
+               } else if (*cp == ';') {
+                       if (op != s_Value && op[-1])
+                               *op++ = '\0';
+               } else {
+                       *op++ = *cp;
+               }
+       }
+       cp[0] = '\0';
+       cp[1] = '\0';
+       return s_Value;
+
+envfail:
+       free(s_Value);
+       s_Value = calloc(2, 1);
+       return s_Value;
+}
+
+
 /* Iteration helper for the provider list. Naked names (without *any*
  * path) will be prepended with path to the executable running this
  * code. While this was not necessary until Win7, newer versions of
@@ -177,38 +378,48 @@ fail:
  */
 static char*
 provlist_next_item(
-       const char ** const ppath
+       const char ** iter
        )
 {
        static char *   s_modpath    /* = NULL */;
        static char     s_nullstr[1] /* = { '\0' } */;
 
-       const char *    phead;
-       const char *    ptail;
-       char *          retv;
-       char *          endp;
+       const char     *phead, *phold;
+       char           *retv, *endp;
        int/*BOOL*/     nodir;
-       int             ch;
-       DWORD           slen;
+       DWORD           slen, mlen;
 
-       /* check if we're already done */
-       if (NULL == (phead = *ppath))
+       /* get next item -- might be start of a new round or the end */
+again:
+       if (*iter == NULL)
+               *iter = phead = get_provider_list();
+       else
+               *iter = phead = *iter + strlen(*iter) + 1;
+       if (!(phead && *phead)) {
+               *iter = NULL;
                return NULL;
-       /* Skip any leading ';' -- should not happen, but this is
-        * user-provided input after all...
+       }
+
+       /* Inspect the next section of input string. It must be
+        * either an absolute path or just a name.
         */
-       while (*phead == ';')
-               ++phead;
-       /* check if we're already done */
-       if (!*phead) {
-               *ppath = NULL;
-               return NULL;
+       if (isalpha((u_char)phead[0]) && phead[1] == ':' && phead[2] == '\\') {
+               nodir = FALSE;
+       } else {
+               nodir = TRUE;
+               phold = phead;
+               while (NULL != (endp = strpbrk(phold, "\\:")))
+                       phold = endp + 1;
+               if (phead != phold) {
+                       msyslog(LOG_WARNING,
+                               "pps api: path component(s) of '%s' ignored, use '%s'",
+                               phead, phold);
+                       phead = phold;
+               }
        }
-       /* Inspect the next section of input string. */
-       nodir = TRUE;
-       for (ptail = phead; (ch = *ptail) && ch != ';'; ++ptail)
-               if (ch == '\\' || ch == '/')
-                       nodir = FALSE;
+       if (!*phead || strchr("\\.:", (u_char)phead[strlen(phead) - 1]))
+               goto again;     /* empty or looks like a directory! */
+
        /* Make sure we have a proper module path when we need one. */
        if (nodir && NULL == s_modpath) {
                s_modpath = get_module_path();
@@ -217,27 +428,23 @@ provlist_next_item(
        }
 
        /* Prepare buffer for copy of file name. */
-       slen = (DWORD)(ptail - phead); /* 4GB string should be enough... */
+       slen = (DWORD)strlen(phead); /* 4GB string should be enough... */
        if (nodir && NULL != s_modpath) {
                /* Prepend full path to executable to the name. */
-               endp = retv = malloc(strlen(s_modpath) + slen + 1);
+               mlen = (DWORD)strlen(s_modpath);
+               endp = retv = malloc(mlen + slen + 1);
                if (NULL != endp) {
-                       strcpy(endp, s_modpath);
-                       endp += strlen(endp);
+                       memcpy(endp, s_modpath, mlen);
+                       endp += mlen;
                }
        } else {
                endp = retv = malloc(slen + 1u);
        }
        /* Copy with conversion from '/' to '\\' */
        if (NULL != endp) {
-               while (phead != ptail) {
-                       ch = (u_char)*phead++;
-                       *endp++ = (ch != '/') ? ch : '\\';
-               }
-               *endp = '\0';
+               memcpy(endp, phead, slen);
+               endp[slen] = '\0';
        }
-       /*Update scan pointer & return result. */
-       *ppath = (*ptail) ? (ptail + 1) : NULL;
        return retv;
 }
 
@@ -327,6 +534,49 @@ load_pps_provider(
 }
 
 
+static ppsapi_provider*
+get_first_provider(void)
+{
+       const char *     itpos;
+       char *           dll;
+       ppsapi_provider *prov, *hold;
+       int              err;
+
+       /* check if we have done our work so far... */
+       if (g_provider_list == INVALID_HANDLE_VALUE)
+               return NULL;
+       if (g_provider_list != NULL)
+               return g_provider_list;
+
+       itpos = NULL;
+       while (NULL != (dll = provlist_next_item(&itpos))) {
+               err = load_pps_provider(dll);
+               if (err)
+                       msyslog(LOG_ERR, "time_pps_create: load failed (%s) --> %d / %s",
+                               dll, err, strerror(err));
+               else
+                       msyslog(LOG_INFO, "time_pps_create: loaded '%s'",
+                               dll);
+               free(dll);
+       }
+
+       /* reverse the list, possibly mark as EMPTY */
+       prov = g_provider_list;
+       if (NULL != prov) {
+               g_provider_list = NULL;
+               do {
+                       hold = prov;
+                       prov = hold->next;
+                       hold->next = g_provider_list;
+                       g_provider_list = hold;
+               } while (prov);
+               prov = g_provider_list;
+       } else {
+               g_provider_list = INVALID_HANDLE_VALUE;
+       }
+       return prov;
+}
+
 int
 time_pps_create(
        int             filedes,/* device file descriptor */
@@ -334,8 +584,6 @@ time_pps_create(
        )
 {
        HANDLE                  winhandle;
-       const char *            dlls;
-       char *                  dll;
        ppsapi_provider *       prov;
        pps_handle_t            ppshandle;
        int                     err;
@@ -347,37 +595,6 @@ time_pps_create(
        if (INVALID_HANDLE_VALUE == winhandle)
                return set_pps_errno(EBADF);
 
-       /* For initial testing the list of PPSAPI backend providers is
-        * provided by the environment variable PPSAPI_DLLS, separated by
-        * semicolons such as
-        * PPSAPI_DLLS=c:\ntp\serial_ppsapi.dll;..\parport_ppsapi.dll
-        * There are a million better ways, such as a well-known registry
-        * key under which a value is created for each provider DLL
-        * installed, or even a platform-specific ntp.conf directive or
-        * command-line switch.
-        *
-        * [Bug 3139] Since nothing is more durable than a provisional
-        * implementation, we're still stuck with that...
-        */
-       dlls = getenv("PPSAPI_DLLS");
-       if (NULL != dlls && NULL == g_provider_list) {
-               msyslog(LOG_INFO, "time_pps_create: load list '%s'",
-                       dlls);
-       } else
-               dlls = NULL;
-
-       while (NULL != (dll = provlist_next_item(&dlls))) {
-               err = load_pps_provider(dll);
-               if (err) {
-                       msyslog(LOG_ERR, "time_pps_create: load failed (%s) --> %d / %s",
-                               dll, err, strerror(err));
-               } else {
-                       msyslog(LOG_INFO, "time_pps_create: loaded '%s'",
-                               dll);
-               }
-               free(dll);
-       }
-
        /* Hand off to each provider in turn until one returns a PPS
         * handle or they've all declined.
         *
@@ -388,23 +605,25 @@ time_pps_create(
         * this provides slightly more information.
         */
        err = ENOEXEC;
-       if (NULL == g_provider_list) {
+       prov = get_first_provider();
+       if (NULL == prov) {
                msyslog(LOG_ERR, "time_pps_create: %s",
                        "no providers available");
                return set_pps_errno(err);
-       }
-       for (prov = g_provider_list; NULL != prov; prov = prov->next) {
+       } else do {
                ppshandle = 0;
                g_curr_provider = prov;
                err = (*prov->ptime_pps_create)(winhandle, &ppshandle);
                g_curr_provider = NULL;
                if (!err && ppshandle) {
                        *phandle = ppshandle;
+                       ppsu_register(ppshandle, serial_devctx(winhandle));
                        return 0;
                }
                msyslog(LOG_INFO, "time_pps_create: provider '%s' failed: %d / %s",
                        prov->short_name, err, strerror(err));
-       }
+       } while (NULL != (prov = prov->next));
+
        msyslog(LOG_ERR, "time_pps_create: %s",
                "all providers failed");
        return set_pps_errno(err);
@@ -423,6 +642,7 @@ time_pps_destroy(
        if (NULL == punit)
                return set_pps_errno(EBADF);
        /* Call provider. Note the handle is gone anyway... */
+       ppsu_remove(handle);
        err = (*punit->provider->ptime_pps_destroy)(punit, punit->context);
        free(punit);
        if (err)
index a52e7f86005dc962fe7915083063e750c5b91e04..333bd2fb46c41afb0286973142c9ca3c3e4d0563 100644 (file)
@@ -119,7 +119,6 @@ static void free_io_completion_port_mem(void);
 static HANDLE  hndIOCPLPort;
 static HANDLE  hMainThread;
 static HANDLE  hMainRpcDone;
-static BOOL    DoPPShack;
 
 DWORD  ActiveWaitHandles;
 HANDLE WaitHandles[4];
@@ -227,26 +226,11 @@ void
 init_io_completion_port(void)
 {
        OSVERSIONINFO vi;
-       const char *    envp;
 
 #   ifdef DEBUG
        atexit(&free_io_completion_port_mem);
 #   endif
 
-       /* TODO: this should not be done via environment;
-        * It would be much better to have this as proper config option.
-        * (The same is true for the PPS API DLL list...)
-        */
-       if (NULL != (envp = getenv("PPSAPI_HACK")))
-               /* check for [Tt]{rue}, [Yy]{es}, or '1' as first char*/
-               DoPPShack = !!strchr("yYtT1", (u_char)*envp);
-       else if (NULL != (envp = getenv("PPSAPI_DLLS")))
-               /* any non-empty list disables PPS hack */
-               DoPPShack = !*envp;
-       else
-               /* otherwise use the PPS hack */
-               DoPPShack = TRUE;
-
        memset(&vi, 0, sizeof(vi));
        vi.dwOSVersionInfoSize = sizeof(vi);
 
@@ -819,11 +803,15 @@ OnSerialWaitComplete(
                 * more like the behaviour under a UN*Xish OS. On the other hand, it
                 * will give a nasty surprise for people which have until now happily
                 * taken the pps hack for granted, and after the first complaint, I have
-                * decided to keep the old implementation unconditionally. So here it is:
+                * decided to keep the old implementation.
+                *
+                * perlinger@ntp.org, 2017-03-04
+                * If the loopback PPS API provider is active on this channel, the
+                * PPS hack will be *disabled*.
                 *
                 * backward compat: 'usermode-pps-hack'
                 */
-               if ((MS_RLSD_ON & modem_status) && DoPPShack) {
+               if ((MS_RLSD_ON & modem_status) && !(dev && dev->pps_active)) {
                        lpo->aux.DCDSTime = lpo->aux.RecvTime;
                        lpo->aux.flTsDCDS = 1;
                        DPRINTF(2, ("upps-hack: fd %d DCD PPS Rise at %s\n",
@@ -1301,7 +1289,7 @@ ntp_pps_attach_device(
        DevCtx_t *      dev = NULL;
 
        dev = DevCtxAttach(serial_devctx(hndIo));
-       if ( NULL == dev)
+       if (NULL == dev)
                SetLastError(ERROR_INVALID_HANDLE);
        return dev;
 }